roojs-ui-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              Roo.log("load records failed");
19557             Roo.log(o);
19558             Roo.log(this);
19559            Roo.log(' handle loadexception? ' + (this.hasListener('loadexception') ? 'YES' : 'NO' ));
19560             // show a message if no listener is registered.
19561             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19562                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19563             }
19564             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19565             return;
19566         }
19567         var r = o.records, t = o.totalRecords || r.length;
19568         if(!options || options.add !== true){
19569             if(this.pruneModifiedRecords){
19570                 this.modified = [];
19571             }
19572             for(var i = 0, len = r.length; i < len; i++){
19573                 r[i].join(this);
19574             }
19575             if(this.snapshot){
19576                 this.data = this.snapshot;
19577                 delete this.snapshot;
19578             }
19579             this.data.clear();
19580             this.data.addAll(r);
19581             this.totalLength = t;
19582             this.applySort();
19583             this.fireEvent("datachanged", this);
19584         }else{
19585             this.totalLength = Math.max(t, this.data.length+r.length);
19586             this.add(r);
19587         }
19588         this.fireEvent("load", this, r, options);
19589         if(options.callback){
19590             options.callback.call(options.scope || this, r, options, true);
19591         }
19592     },
19593
19594
19595     /**
19596      * Loads data from a passed data block. A Reader which understands the format of the data
19597      * must have been configured in the constructor.
19598      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19599      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19600      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19601      */
19602     loadData : function(o, append){
19603         var r = this.reader.readRecords(o);
19604         this.loadRecords(r, {add: append}, true);
19605     },
19606
19607     /**
19608      * Gets the number of cached records.
19609      * <p>
19610      * <em>If using paging, this may not be the total size of the dataset. If the data object
19611      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19612      * the data set size</em>
19613      */
19614     getCount : function(){
19615         return this.data.length || 0;
19616     },
19617
19618     /**
19619      * Gets the total number of records in the dataset as returned by the server.
19620      * <p>
19621      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19622      * the dataset size</em>
19623      */
19624     getTotalCount : function(){
19625         return this.totalLength || 0;
19626     },
19627
19628     /**
19629      * Returns the sort state of the Store as an object with two properties:
19630      * <pre><code>
19631  field {String} The name of the field by which the Records are sorted
19632  direction {String} The sort order, "ASC" or "DESC"
19633      * </code></pre>
19634      */
19635     getSortState : function(){
19636         return this.sortInfo;
19637     },
19638
19639     // private
19640     applySort : function(){
19641         if(this.sortInfo && !this.remoteSort){
19642             var s = this.sortInfo, f = s.field;
19643             var st = this.fields.get(f).sortType;
19644             var fn = function(r1, r2){
19645                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19646                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19647             };
19648             this.data.sort(s.direction, fn);
19649             if(this.snapshot && this.snapshot != this.data){
19650                 this.snapshot.sort(s.direction, fn);
19651             }
19652         }
19653     },
19654
19655     /**
19656      * Sets the default sort column and order to be used by the next load operation.
19657      * @param {String} fieldName The name of the field to sort by.
19658      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19659      */
19660     setDefaultSort : function(field, dir){
19661         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19662     },
19663
19664     /**
19665      * Sort the Records.
19666      * If remote sorting is used, the sort is performed on the server, and the cache is
19667      * reloaded. If local sorting is used, the cache is sorted internally.
19668      * @param {String} fieldName The name of the field to sort by.
19669      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19670      */
19671     sort : function(fieldName, dir){
19672         var f = this.fields.get(fieldName);
19673         if(!dir){
19674             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19675             
19676             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19677                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19678             }else{
19679                 dir = f.sortDir;
19680             }
19681         }
19682         this.sortToggle[f.name] = dir;
19683         this.sortInfo = {field: f.name, direction: dir};
19684         if(!this.remoteSort){
19685             this.applySort();
19686             this.fireEvent("datachanged", this);
19687         }else{
19688             this.load(this.lastOptions);
19689         }
19690     },
19691
19692     /**
19693      * Calls the specified function for each of the Records in the cache.
19694      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19695      * Returning <em>false</em> aborts and exits the iteration.
19696      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19697      */
19698     each : function(fn, scope){
19699         this.data.each(fn, scope);
19700     },
19701
19702     /**
19703      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19704      * (e.g., during paging).
19705      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19706      */
19707     getModifiedRecords : function(){
19708         return this.modified;
19709     },
19710
19711     // private
19712     createFilterFn : function(property, value, anyMatch){
19713         if(!value.exec){ // not a regex
19714             value = String(value);
19715             if(value.length == 0){
19716                 return false;
19717             }
19718             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19719         }
19720         return function(r){
19721             return value.test(r.data[property]);
19722         };
19723     },
19724
19725     /**
19726      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19727      * @param {String} property A field on your records
19728      * @param {Number} start The record index to start at (defaults to 0)
19729      * @param {Number} end The last record index to include (defaults to length - 1)
19730      * @return {Number} The sum
19731      */
19732     sum : function(property, start, end){
19733         var rs = this.data.items, v = 0;
19734         start = start || 0;
19735         end = (end || end === 0) ? end : rs.length-1;
19736
19737         for(var i = start; i <= end; i++){
19738             v += (rs[i].data[property] || 0);
19739         }
19740         return v;
19741     },
19742
19743     /**
19744      * Filter the records by a specified property.
19745      * @param {String} field A field on your records
19746      * @param {String/RegExp} value Either a string that the field
19747      * should start with or a RegExp to test against the field
19748      * @param {Boolean} anyMatch True to match any part not just the beginning
19749      */
19750     filter : function(property, value, anyMatch){
19751         var fn = this.createFilterFn(property, value, anyMatch);
19752         return fn ? this.filterBy(fn) : this.clearFilter();
19753     },
19754
19755     /**
19756      * Filter by a function. The specified function will be called with each
19757      * record in this data source. If the function returns true the record is included,
19758      * otherwise it is filtered.
19759      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19760      * @param {Object} scope (optional) The scope of the function (defaults to this)
19761      */
19762     filterBy : function(fn, scope){
19763         this.snapshot = this.snapshot || this.data;
19764         this.data = this.queryBy(fn, scope||this);
19765         this.fireEvent("datachanged", this);
19766     },
19767
19768     /**
19769      * Query the records by a specified property.
19770      * @param {String} field A field on your records
19771      * @param {String/RegExp} value Either a string that the field
19772      * should start with or a RegExp to test against the field
19773      * @param {Boolean} anyMatch True to match any part not just the beginning
19774      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19775      */
19776     query : function(property, value, anyMatch){
19777         var fn = this.createFilterFn(property, value, anyMatch);
19778         return fn ? this.queryBy(fn) : this.data.clone();
19779     },
19780
19781     /**
19782      * Query by a function. The specified function will be called with each
19783      * record in this data source. If the function returns true the record is included
19784      * in the results.
19785      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19786      * @param {Object} scope (optional) The scope of the function (defaults to this)
19787       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19788      **/
19789     queryBy : function(fn, scope){
19790         var data = this.snapshot || this.data;
19791         return data.filterBy(fn, scope||this);
19792     },
19793
19794     /**
19795      * Collects unique values for a particular dataIndex from this store.
19796      * @param {String} dataIndex The property to collect
19797      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19798      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19799      * @return {Array} An array of the unique values
19800      **/
19801     collect : function(dataIndex, allowNull, bypassFilter){
19802         var d = (bypassFilter === true && this.snapshot) ?
19803                 this.snapshot.items : this.data.items;
19804         var v, sv, r = [], l = {};
19805         for(var i = 0, len = d.length; i < len; i++){
19806             v = d[i].data[dataIndex];
19807             sv = String(v);
19808             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19809                 l[sv] = true;
19810                 r[r.length] = v;
19811             }
19812         }
19813         return r;
19814     },
19815
19816     /**
19817      * Revert to a view of the Record cache with no filtering applied.
19818      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19819      */
19820     clearFilter : function(suppressEvent){
19821         if(this.snapshot && this.snapshot != this.data){
19822             this.data = this.snapshot;
19823             delete this.snapshot;
19824             if(suppressEvent !== true){
19825                 this.fireEvent("datachanged", this);
19826             }
19827         }
19828     },
19829
19830     // private
19831     afterEdit : function(record){
19832         if(this.modified.indexOf(record) == -1){
19833             this.modified.push(record);
19834         }
19835         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19836     },
19837     
19838     // private
19839     afterReject : function(record){
19840         this.modified.remove(record);
19841         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19842     },
19843
19844     // private
19845     afterCommit : function(record){
19846         this.modified.remove(record);
19847         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19848     },
19849
19850     /**
19851      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19852      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19853      */
19854     commitChanges : function(){
19855         var m = this.modified.slice(0);
19856         this.modified = [];
19857         for(var i = 0, len = m.length; i < len; i++){
19858             m[i].commit();
19859         }
19860     },
19861
19862     /**
19863      * Cancel outstanding changes on all changed records.
19864      */
19865     rejectChanges : function(){
19866         var m = this.modified.slice(0);
19867         this.modified = [];
19868         for(var i = 0, len = m.length; i < len; i++){
19869             m[i].reject();
19870         }
19871     },
19872
19873     onMetaChange : function(meta, rtype, o){
19874         this.recordType = rtype;
19875         this.fields = rtype.prototype.fields;
19876         delete this.snapshot;
19877         this.sortInfo = meta.sortInfo || this.sortInfo;
19878         this.modified = [];
19879         this.fireEvent('metachange', this, this.reader.meta);
19880     }
19881 });/*
19882  * Based on:
19883  * Ext JS Library 1.1.1
19884  * Copyright(c) 2006-2007, Ext JS, LLC.
19885  *
19886  * Originally Released Under LGPL - original licence link has changed is not relivant.
19887  *
19888  * Fork - LGPL
19889  * <script type="text/javascript">
19890  */
19891
19892 /**
19893  * @class Roo.data.SimpleStore
19894  * @extends Roo.data.Store
19895  * Small helper class to make creating Stores from Array data easier.
19896  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19897  * @cfg {Array} fields An array of field definition objects, or field name strings.
19898  * @cfg {Array} data The multi-dimensional array of data
19899  * @constructor
19900  * @param {Object} config
19901  */
19902 Roo.data.SimpleStore = function(config){
19903     Roo.data.SimpleStore.superclass.constructor.call(this, {
19904         isLocal : true,
19905         reader: new Roo.data.ArrayReader({
19906                 id: config.id
19907             },
19908             Roo.data.Record.create(config.fields)
19909         ),
19910         proxy : new Roo.data.MemoryProxy(config.data)
19911     });
19912     this.load();
19913 };
19914 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19915  * Based on:
19916  * Ext JS Library 1.1.1
19917  * Copyright(c) 2006-2007, Ext JS, LLC.
19918  *
19919  * Originally Released Under LGPL - original licence link has changed is not relivant.
19920  *
19921  * Fork - LGPL
19922  * <script type="text/javascript">
19923  */
19924
19925 /**
19926 /**
19927  * @extends Roo.data.Store
19928  * @class Roo.data.JsonStore
19929  * Small helper class to make creating Stores for JSON data easier. <br/>
19930 <pre><code>
19931 var store = new Roo.data.JsonStore({
19932     url: 'get-images.php',
19933     root: 'images',
19934     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19935 });
19936 </code></pre>
19937  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19938  * JsonReader and HttpProxy (unless inline data is provided).</b>
19939  * @cfg {Array} fields An array of field definition objects, or field name strings.
19940  * @constructor
19941  * @param {Object} config
19942  */
19943 Roo.data.JsonStore = function(c){
19944     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19945         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19946         reader: new Roo.data.JsonReader(c, c.fields)
19947     }));
19948 };
19949 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19950  * Based on:
19951  * Ext JS Library 1.1.1
19952  * Copyright(c) 2006-2007, Ext JS, LLC.
19953  *
19954  * Originally Released Under LGPL - original licence link has changed is not relivant.
19955  *
19956  * Fork - LGPL
19957  * <script type="text/javascript">
19958  */
19959
19960  
19961 Roo.data.Field = function(config){
19962     if(typeof config == "string"){
19963         config = {name: config};
19964     }
19965     Roo.apply(this, config);
19966     
19967     if(!this.type){
19968         this.type = "auto";
19969     }
19970     
19971     var st = Roo.data.SortTypes;
19972     // named sortTypes are supported, here we look them up
19973     if(typeof this.sortType == "string"){
19974         this.sortType = st[this.sortType];
19975     }
19976     
19977     // set default sortType for strings and dates
19978     if(!this.sortType){
19979         switch(this.type){
19980             case "string":
19981                 this.sortType = st.asUCString;
19982                 break;
19983             case "date":
19984                 this.sortType = st.asDate;
19985                 break;
19986             default:
19987                 this.sortType = st.none;
19988         }
19989     }
19990
19991     // define once
19992     var stripRe = /[\$,%]/g;
19993
19994     // prebuilt conversion function for this field, instead of
19995     // switching every time we're reading a value
19996     if(!this.convert){
19997         var cv, dateFormat = this.dateFormat;
19998         switch(this.type){
19999             case "":
20000             case "auto":
20001             case undefined:
20002                 cv = function(v){ return v; };
20003                 break;
20004             case "string":
20005                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20006                 break;
20007             case "int":
20008                 cv = function(v){
20009                     return v !== undefined && v !== null && v !== '' ?
20010                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20011                     };
20012                 break;
20013             case "float":
20014                 cv = function(v){
20015                     return v !== undefined && v !== null && v !== '' ?
20016                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20017                     };
20018                 break;
20019             case "bool":
20020             case "boolean":
20021                 cv = function(v){ return v === true || v === "true" || v == 1; };
20022                 break;
20023             case "date":
20024                 cv = function(v){
20025                     if(!v){
20026                         return '';
20027                     }
20028                     if(v instanceof Date){
20029                         return v;
20030                     }
20031                     if(dateFormat){
20032                         if(dateFormat == "timestamp"){
20033                             return new Date(v*1000);
20034                         }
20035                         return Date.parseDate(v, dateFormat);
20036                     }
20037                     var parsed = Date.parse(v);
20038                     return parsed ? new Date(parsed) : null;
20039                 };
20040              break;
20041             
20042         }
20043         this.convert = cv;
20044     }
20045 };
20046
20047 Roo.data.Field.prototype = {
20048     dateFormat: null,
20049     defaultValue: "",
20050     mapping: null,
20051     sortType : null,
20052     sortDir : "ASC"
20053 };/*
20054  * Based on:
20055  * Ext JS Library 1.1.1
20056  * Copyright(c) 2006-2007, Ext JS, LLC.
20057  *
20058  * Originally Released Under LGPL - original licence link has changed is not relivant.
20059  *
20060  * Fork - LGPL
20061  * <script type="text/javascript">
20062  */
20063  
20064 // Base class for reading structured data from a data source.  This class is intended to be
20065 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20066
20067 /**
20068  * @class Roo.data.DataReader
20069  * Base class for reading structured data from a data source.  This class is intended to be
20070  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20071  */
20072
20073 Roo.data.DataReader = function(meta, recordType){
20074     
20075     this.meta = meta;
20076     
20077     this.recordType = recordType instanceof Array ? 
20078         Roo.data.Record.create(recordType) : recordType;
20079 };
20080
20081 Roo.data.DataReader.prototype = {
20082      /**
20083      * Create an empty record
20084      * @param {Object} data (optional) - overlay some values
20085      * @return {Roo.data.Record} record created.
20086      */
20087     newRow :  function(d) {
20088         var da =  {};
20089         this.recordType.prototype.fields.each(function(c) {
20090             switch( c.type) {
20091                 case 'int' : da[c.name] = 0; break;
20092                 case 'date' : da[c.name] = new Date(); break;
20093                 case 'float' : da[c.name] = 0.0; break;
20094                 case 'boolean' : da[c.name] = false; break;
20095                 default : da[c.name] = ""; break;
20096             }
20097             
20098         });
20099         return new this.recordType(Roo.apply(da, d));
20100     }
20101     
20102 };/*
20103  * Based on:
20104  * Ext JS Library 1.1.1
20105  * Copyright(c) 2006-2007, Ext JS, LLC.
20106  *
20107  * Originally Released Under LGPL - original licence link has changed is not relivant.
20108  *
20109  * Fork - LGPL
20110  * <script type="text/javascript">
20111  */
20112
20113 /**
20114  * @class Roo.data.DataProxy
20115  * @extends Roo.data.Observable
20116  * This class is an abstract base class for implementations which provide retrieval of
20117  * unformatted data objects.<br>
20118  * <p>
20119  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20120  * (of the appropriate type which knows how to parse the data object) to provide a block of
20121  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20122  * <p>
20123  * Custom implementations must implement the load method as described in
20124  * {@link Roo.data.HttpProxy#load}.
20125  */
20126 Roo.data.DataProxy = function(){
20127     this.addEvents({
20128         /**
20129          * @event beforeload
20130          * Fires before a network request is made to retrieve a data object.
20131          * @param {Object} This DataProxy object.
20132          * @param {Object} params The params parameter to the load function.
20133          */
20134         beforeload : true,
20135         /**
20136          * @event load
20137          * Fires before the load method's callback is called.
20138          * @param {Object} This DataProxy object.
20139          * @param {Object} o The data object.
20140          * @param {Object} arg The callback argument object passed to the load function.
20141          */
20142         load : true,
20143         /**
20144          * @event loadexception
20145          * Fires if an Exception occurs during data retrieval.
20146          * @param {Object} This DataProxy object.
20147          * @param {Object} o The data object.
20148          * @param {Object} arg The callback argument object passed to the load function.
20149          * @param {Object} e The Exception.
20150          */
20151         loadexception : true
20152     });
20153     Roo.data.DataProxy.superclass.constructor.call(this);
20154 };
20155
20156 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20157
20158     /**
20159      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20160      */
20161 /*
20162  * Based on:
20163  * Ext JS Library 1.1.1
20164  * Copyright(c) 2006-2007, Ext JS, LLC.
20165  *
20166  * Originally Released Under LGPL - original licence link has changed is not relivant.
20167  *
20168  * Fork - LGPL
20169  * <script type="text/javascript">
20170  */
20171 /**
20172  * @class Roo.data.MemoryProxy
20173  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20174  * to the Reader when its load method is called.
20175  * @constructor
20176  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20177  */
20178 Roo.data.MemoryProxy = function(data){
20179     if (data.data) {
20180         data = data.data;
20181     }
20182     Roo.data.MemoryProxy.superclass.constructor.call(this);
20183     this.data = data;
20184 };
20185
20186 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20187     /**
20188      * Load data from the requested source (in this case an in-memory
20189      * data object passed to the constructor), read the data object into
20190      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20191      * process that block using the passed callback.
20192      * @param {Object} params This parameter is not used by the MemoryProxy class.
20193      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20194      * object into a block of Roo.data.Records.
20195      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20196      * The function must be passed <ul>
20197      * <li>The Record block object</li>
20198      * <li>The "arg" argument from the load function</li>
20199      * <li>A boolean success indicator</li>
20200      * </ul>
20201      * @param {Object} scope The scope in which to call the callback
20202      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20203      */
20204     load : function(params, reader, callback, scope, arg){
20205         params = params || {};
20206         var result;
20207         try {
20208             result = reader.readRecords(this.data);
20209         }catch(e){
20210             this.fireEvent("loadexception", this, arg, null, e);
20211             callback.call(scope, null, arg, false);
20212             return;
20213         }
20214         callback.call(scope, result, arg, true);
20215     },
20216     
20217     // private
20218     update : function(params, records){
20219         
20220     }
20221 });/*
20222  * Based on:
20223  * Ext JS Library 1.1.1
20224  * Copyright(c) 2006-2007, Ext JS, LLC.
20225  *
20226  * Originally Released Under LGPL - original licence link has changed is not relivant.
20227  *
20228  * Fork - LGPL
20229  * <script type="text/javascript">
20230  */
20231 /**
20232  * @class Roo.data.HttpProxy
20233  * @extends Roo.data.DataProxy
20234  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20235  * configured to reference a certain URL.<br><br>
20236  * <p>
20237  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20238  * from which the running page was served.<br><br>
20239  * <p>
20240  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20241  * <p>
20242  * Be aware that to enable the browser to parse an XML document, the server must set
20243  * the Content-Type header in the HTTP response to "text/xml".
20244  * @constructor
20245  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20246  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20247  * will be used to make the request.
20248  */
20249 Roo.data.HttpProxy = function(conn){
20250     Roo.data.HttpProxy.superclass.constructor.call(this);
20251     // is conn a conn config or a real conn?
20252     this.conn = conn;
20253     this.useAjax = !conn || !conn.events;
20254   
20255 };
20256
20257 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20258     // thse are take from connection...
20259     
20260     /**
20261      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20262      */
20263     /**
20264      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20265      * extra parameters to each request made by this object. (defaults to undefined)
20266      */
20267     /**
20268      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20269      *  to each request made by this object. (defaults to undefined)
20270      */
20271     /**
20272      * @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)
20273      */
20274     /**
20275      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20276      */
20277      /**
20278      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20279      * @type Boolean
20280      */
20281   
20282
20283     /**
20284      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20285      * @type Boolean
20286      */
20287     /**
20288      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20289      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20290      * a finer-grained basis than the DataProxy events.
20291      */
20292     getConnection : function(){
20293         return this.useAjax ? Roo.Ajax : this.conn;
20294     },
20295
20296     /**
20297      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20298      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20299      * process that block using the passed callback.
20300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20301      * for the request to the remote server.
20302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20303      * object into a block of Roo.data.Records.
20304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20305      * The function must be passed <ul>
20306      * <li>The Record block object</li>
20307      * <li>The "arg" argument from the load function</li>
20308      * <li>A boolean success indicator</li>
20309      * </ul>
20310      * @param {Object} scope The scope in which to call the callback
20311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20312      */
20313     load : function(params, reader, callback, scope, arg){
20314         if(this.fireEvent("beforeload", this, params) !== false){
20315             var  o = {
20316                 params : params || {},
20317                 request: {
20318                     callback : callback,
20319                     scope : scope,
20320                     arg : arg
20321                 },
20322                 reader: reader,
20323                 callback : this.loadResponse,
20324                 scope: this
20325             };
20326             if(this.useAjax){
20327                 Roo.applyIf(o, this.conn);
20328                 if(this.activeRequest){
20329                     Roo.Ajax.abort(this.activeRequest);
20330                 }
20331                 this.activeRequest = Roo.Ajax.request(o);
20332             }else{
20333                 this.conn.request(o);
20334             }
20335         }else{
20336             callback.call(scope||this, null, arg, false);
20337         }
20338     },
20339
20340     // private
20341     loadResponse : function(o, success, response){
20342         delete this.activeRequest;
20343         if(!success){
20344             this.fireEvent("loadexception", this, o, response);
20345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20346             return;
20347         }
20348         var result;
20349         try {
20350             result = o.reader.read(response);
20351         }catch(e){
20352             this.fireEvent("loadexception", this, o, response, e);
20353             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20354             return;
20355         }
20356         
20357         this.fireEvent("load", this, o, o.request.arg);
20358         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20359     },
20360
20361     // private
20362     update : function(dataSet){
20363
20364     },
20365
20366     // private
20367     updateResponse : function(dataSet){
20368
20369     }
20370 });/*
20371  * Based on:
20372  * Ext JS Library 1.1.1
20373  * Copyright(c) 2006-2007, Ext JS, LLC.
20374  *
20375  * Originally Released Under LGPL - original licence link has changed is not relivant.
20376  *
20377  * Fork - LGPL
20378  * <script type="text/javascript">
20379  */
20380
20381 /**
20382  * @class Roo.data.ScriptTagProxy
20383  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20384  * other than the originating domain of the running page.<br><br>
20385  * <p>
20386  * <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
20387  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20388  * <p>
20389  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20390  * source code that is used as the source inside a &lt;script> tag.<br><br>
20391  * <p>
20392  * In order for the browser to process the returned data, the server must wrap the data object
20393  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20394  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20395  * depending on whether the callback name was passed:
20396  * <p>
20397  * <pre><code>
20398 boolean scriptTag = false;
20399 String cb = request.getParameter("callback");
20400 if (cb != null) {
20401     scriptTag = true;
20402     response.setContentType("text/javascript");
20403 } else {
20404     response.setContentType("application/x-json");
20405 }
20406 Writer out = response.getWriter();
20407 if (scriptTag) {
20408     out.write(cb + "(");
20409 }
20410 out.print(dataBlock.toJsonString());
20411 if (scriptTag) {
20412     out.write(");");
20413 }
20414 </pre></code>
20415  *
20416  * @constructor
20417  * @param {Object} config A configuration object.
20418  */
20419 Roo.data.ScriptTagProxy = function(config){
20420     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20421     Roo.apply(this, config);
20422     this.head = document.getElementsByTagName("head")[0];
20423 };
20424
20425 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20426
20427 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20428     /**
20429      * @cfg {String} url The URL from which to request the data object.
20430      */
20431     /**
20432      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20433      */
20434     timeout : 30000,
20435     /**
20436      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20437      * the server the name of the callback function set up by the load call to process the returned data object.
20438      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20439      * javascript output which calls this named function passing the data object as its only parameter.
20440      */
20441     callbackParam : "callback",
20442     /**
20443      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20444      * name to the request.
20445      */
20446     nocache : true,
20447
20448     /**
20449      * Load data from the configured URL, read the data object into
20450      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20451      * process that block using the passed callback.
20452      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20453      * for the request to the remote server.
20454      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20455      * object into a block of Roo.data.Records.
20456      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20457      * The function must be passed <ul>
20458      * <li>The Record block object</li>
20459      * <li>The "arg" argument from the load function</li>
20460      * <li>A boolean success indicator</li>
20461      * </ul>
20462      * @param {Object} scope The scope in which to call the callback
20463      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20464      */
20465     load : function(params, reader, callback, scope, arg){
20466         if(this.fireEvent("beforeload", this, params) !== false){
20467
20468             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20469
20470             var url = this.url;
20471             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20472             if(this.nocache){
20473                 url += "&_dc=" + (new Date().getTime());
20474             }
20475             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20476             var trans = {
20477                 id : transId,
20478                 cb : "stcCallback"+transId,
20479                 scriptId : "stcScript"+transId,
20480                 params : params,
20481                 arg : arg,
20482                 url : url,
20483                 callback : callback,
20484                 scope : scope,
20485                 reader : reader
20486             };
20487             var conn = this;
20488
20489             window[trans.cb] = function(o){
20490                 conn.handleResponse(o, trans);
20491             };
20492
20493             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20494
20495             if(this.autoAbort !== false){
20496                 this.abort();
20497             }
20498
20499             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20500
20501             var script = document.createElement("script");
20502             script.setAttribute("src", url);
20503             script.setAttribute("type", "text/javascript");
20504             script.setAttribute("id", trans.scriptId);
20505             this.head.appendChild(script);
20506
20507             this.trans = trans;
20508         }else{
20509             callback.call(scope||this, null, arg, false);
20510         }
20511     },
20512
20513     // private
20514     isLoading : function(){
20515         return this.trans ? true : false;
20516     },
20517
20518     /**
20519      * Abort the current server request.
20520      */
20521     abort : function(){
20522         if(this.isLoading()){
20523             this.destroyTrans(this.trans);
20524         }
20525     },
20526
20527     // private
20528     destroyTrans : function(trans, isLoaded){
20529         this.head.removeChild(document.getElementById(trans.scriptId));
20530         clearTimeout(trans.timeoutId);
20531         if(isLoaded){
20532             window[trans.cb] = undefined;
20533             try{
20534                 delete window[trans.cb];
20535             }catch(e){}
20536         }else{
20537             // if hasn't been loaded, wait for load to remove it to prevent script error
20538             window[trans.cb] = function(){
20539                 window[trans.cb] = undefined;
20540                 try{
20541                     delete window[trans.cb];
20542                 }catch(e){}
20543             };
20544         }
20545     },
20546
20547     // private
20548     handleResponse : function(o, trans){
20549         this.trans = false;
20550         this.destroyTrans(trans, true);
20551         var result;
20552         try {
20553             result = trans.reader.readRecords(o);
20554         }catch(e){
20555             this.fireEvent("loadexception", this, o, trans.arg, e);
20556             trans.callback.call(trans.scope||window, null, trans.arg, false);
20557             return;
20558         }
20559         this.fireEvent("load", this, o, trans.arg);
20560         trans.callback.call(trans.scope||window, result, trans.arg, true);
20561     },
20562
20563     // private
20564     handleFailure : function(trans){
20565         this.trans = false;
20566         this.destroyTrans(trans, false);
20567         this.fireEvent("loadexception", this, null, trans.arg);
20568         trans.callback.call(trans.scope||window, null, trans.arg, false);
20569     }
20570 });/*
20571  * Based on:
20572  * Ext JS Library 1.1.1
20573  * Copyright(c) 2006-2007, Ext JS, LLC.
20574  *
20575  * Originally Released Under LGPL - original licence link has changed is not relivant.
20576  *
20577  * Fork - LGPL
20578  * <script type="text/javascript">
20579  */
20580
20581 /**
20582  * @class Roo.data.JsonReader
20583  * @extends Roo.data.DataReader
20584  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20585  * based on mappings in a provided Roo.data.Record constructor.
20586  * 
20587  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20588  * in the reply previously. 
20589  * 
20590  * <p>
20591  * Example code:
20592  * <pre><code>
20593 var RecordDef = Roo.data.Record.create([
20594     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20595     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20596 ]);
20597 var myReader = new Roo.data.JsonReader({
20598     totalProperty: "results",    // The property which contains the total dataset size (optional)
20599     root: "rows",                // The property which contains an Array of row objects
20600     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20601 }, RecordDef);
20602 </code></pre>
20603  * <p>
20604  * This would consume a JSON file like this:
20605  * <pre><code>
20606 { 'results': 2, 'rows': [
20607     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20608     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20609 }
20610 </code></pre>
20611  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20612  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20613  * paged from the remote server.
20614  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20615  * @cfg {String} root name of the property which contains the Array of row objects.
20616  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20617  * @constructor
20618  * Create a new JsonReader
20619  * @param {Object} meta Metadata configuration options
20620  * @param {Object} recordType Either an Array of field definition objects,
20621  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20622  */
20623 Roo.data.JsonReader = function(meta, recordType){
20624     
20625     meta = meta || {};
20626     // set some defaults:
20627     Roo.applyIf(meta, {
20628         totalProperty: 'total',
20629         successProperty : 'success',
20630         root : 'data',
20631         id : 'id'
20632     });
20633     
20634     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20635 };
20636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20637     
20638     /**
20639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20640      * Used by Store query builder to append _requestMeta to params.
20641      * 
20642      */
20643     metaFromRemote : false,
20644     /**
20645      * This method is only used by a DataProxy which has retrieved data from a remote server.
20646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20647      * @return {Object} data A data block which is used by an Roo.data.Store object as
20648      * a cache of Roo.data.Records.
20649      */
20650     read : function(response){
20651         var json = response.responseText;
20652        
20653         var o = /* eval:var:o */ eval("("+json+")");
20654         if(!o) {
20655             throw {message: "JsonReader.read: Json object not found"};
20656         }
20657         
20658         if(o.metaData){
20659             
20660             delete this.ef;
20661             this.metaFromRemote = true;
20662             this.meta = o.metaData;
20663             this.recordType = Roo.data.Record.create(o.metaData.fields);
20664             this.onMetaChange(this.meta, this.recordType, o);
20665         }
20666         return this.readRecords(o);
20667     },
20668
20669     // private function a store will implement
20670     onMetaChange : function(meta, recordType, o){
20671
20672     },
20673
20674     /**
20675          * @ignore
20676          */
20677     simpleAccess: function(obj, subsc) {
20678         return obj[subsc];
20679     },
20680
20681         /**
20682          * @ignore
20683          */
20684     getJsonAccessor: function(){
20685         var re = /[\[\.]/;
20686         return function(expr) {
20687             try {
20688                 return(re.test(expr))
20689                     ? new Function("obj", "return obj." + expr)
20690                     : function(obj){
20691                         return obj[expr];
20692                     };
20693             } catch(e){}
20694             return Roo.emptyFn;
20695         };
20696     }(),
20697
20698     /**
20699      * Create a data block containing Roo.data.Records from an XML document.
20700      * @param {Object} o An object which contains an Array of row objects in the property specified
20701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20702      * which contains the total size of the dataset.
20703      * @return {Object} data A data block which is used by an Roo.data.Store object as
20704      * a cache of Roo.data.Records.
20705      */
20706     readRecords : function(o){
20707         /**
20708          * After any data loads, the raw JSON data is available for further custom processing.
20709          * @type Object
20710          */
20711         this.jsonData = o;
20712         var s = this.meta, Record = this.recordType,
20713             f = Record.prototype.fields, fi = f.items, fl = f.length;
20714
20715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20716         if (!this.ef) {
20717             if(s.totalProperty) {
20718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20719                 }
20720                 if(s.successProperty) {
20721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20722                 }
20723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20724                 if (s.id) {
20725                         var g = this.getJsonAccessor(s.id);
20726                         this.getId = function(rec) {
20727                                 var r = g(rec);
20728                                 return (r === undefined || r === "") ? null : r;
20729                         };
20730                 } else {
20731                         this.getId = function(){return null;};
20732                 }
20733             this.ef = [];
20734             for(var jj = 0; jj < fl; jj++){
20735                 f = fi[jj];
20736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20737                 this.ef[jj] = this.getJsonAccessor(map);
20738             }
20739         }
20740
20741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20742         if(s.totalProperty){
20743             var vt = parseInt(this.getTotal(o), 10);
20744             if(!isNaN(vt)){
20745                 totalRecords = vt;
20746             }
20747         }
20748         if(s.successProperty){
20749             var vs = this.getSuccess(o);
20750             if(vs === false || vs === 'false'){
20751                 success = false;
20752             }
20753         }
20754         var records = [];
20755             for(var i = 0; i < c; i++){
20756                     var n = root[i];
20757                 var values = {};
20758                 var id = this.getId(n);
20759                 for(var j = 0; j < fl; j++){
20760                     f = fi[j];
20761                 var v = this.ef[j](n);
20762                 if (!f.convert) {
20763                     Roo.log('missing convert for ' + f.name);
20764                     Roo.log(f);
20765                     continue;
20766                 }
20767                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20768                 }
20769                 var record = new Record(values, id);
20770                 record.json = n;
20771                 records[i] = record;
20772             }
20773             return {
20774                 success : success,
20775                 records : records,
20776                 totalRecords : totalRecords
20777             };
20778     }
20779 });/*
20780  * Based on:
20781  * Ext JS Library 1.1.1
20782  * Copyright(c) 2006-2007, Ext JS, LLC.
20783  *
20784  * Originally Released Under LGPL - original licence link has changed is not relivant.
20785  *
20786  * Fork - LGPL
20787  * <script type="text/javascript">
20788  */
20789
20790 /**
20791  * @class Roo.data.XmlReader
20792  * @extends Roo.data.DataReader
20793  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20794  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20795  * <p>
20796  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20797  * header in the HTTP response must be set to "text/xml".</em>
20798  * <p>
20799  * Example code:
20800  * <pre><code>
20801 var RecordDef = Roo.data.Record.create([
20802    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20803    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20804 ]);
20805 var myReader = new Roo.data.XmlReader({
20806    totalRecords: "results", // The element which contains the total dataset size (optional)
20807    record: "row",           // The repeated element which contains row information
20808    id: "id"                 // The element within the row that provides an ID for the record (optional)
20809 }, RecordDef);
20810 </code></pre>
20811  * <p>
20812  * This would consume an XML file like this:
20813  * <pre><code>
20814 &lt;?xml?>
20815 &lt;dataset>
20816  &lt;results>2&lt;/results>
20817  &lt;row>
20818    &lt;id>1&lt;/id>
20819    &lt;name>Bill&lt;/name>
20820    &lt;occupation>Gardener&lt;/occupation>
20821  &lt;/row>
20822  &lt;row>
20823    &lt;id>2&lt;/id>
20824    &lt;name>Ben&lt;/name>
20825    &lt;occupation>Horticulturalist&lt;/occupation>
20826  &lt;/row>
20827 &lt;/dataset>
20828 </code></pre>
20829  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20830  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20831  * paged from the remote server.
20832  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20833  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20834  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20835  * a record identifier value.
20836  * @constructor
20837  * Create a new XmlReader
20838  * @param {Object} meta Metadata configuration options
20839  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20840  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20841  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20842  */
20843 Roo.data.XmlReader = function(meta, recordType){
20844     meta = meta || {};
20845     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20846 };
20847 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20848     /**
20849      * This method is only used by a DataProxy which has retrieved data from a remote server.
20850          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20851          * to contain a method called 'responseXML' that returns an XML document object.
20852      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20853      * a cache of Roo.data.Records.
20854      */
20855     read : function(response){
20856         var doc = response.responseXML;
20857         if(!doc) {
20858             throw {message: "XmlReader.read: XML Document not available"};
20859         }
20860         return this.readRecords(doc);
20861     },
20862
20863     /**
20864      * Create a data block containing Roo.data.Records from an XML document.
20865          * @param {Object} doc A parsed XML document.
20866      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20867      * a cache of Roo.data.Records.
20868      */
20869     readRecords : function(doc){
20870         /**
20871          * After any data loads/reads, the raw XML Document is available for further custom processing.
20872          * @type XMLDocument
20873          */
20874         this.xmlData = doc;
20875         var root = doc.documentElement || doc;
20876         var q = Roo.DomQuery;
20877         var recordType = this.recordType, fields = recordType.prototype.fields;
20878         var sid = this.meta.id;
20879         var totalRecords = 0, success = true;
20880         if(this.meta.totalRecords){
20881             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20882         }
20883         
20884         if(this.meta.success){
20885             var sv = q.selectValue(this.meta.success, root, true);
20886             success = sv !== false && sv !== 'false';
20887         }
20888         var records = [];
20889         var ns = q.select(this.meta.record, root);
20890         for(var i = 0, len = ns.length; i < len; i++) {
20891                 var n = ns[i];
20892                 var values = {};
20893                 var id = sid ? q.selectValue(sid, n) : undefined;
20894                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20895                     var f = fields.items[j];
20896                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20897                     v = f.convert(v);
20898                     values[f.name] = v;
20899                 }
20900                 var record = new recordType(values, id);
20901                 record.node = n;
20902                 records[records.length] = record;
20903             }
20904
20905             return {
20906                 success : success,
20907                 records : records,
20908                 totalRecords : totalRecords || records.length
20909             };
20910     }
20911 });/*
20912  * Based on:
20913  * Ext JS Library 1.1.1
20914  * Copyright(c) 2006-2007, Ext JS, LLC.
20915  *
20916  * Originally Released Under LGPL - original licence link has changed is not relivant.
20917  *
20918  * Fork - LGPL
20919  * <script type="text/javascript">
20920  */
20921
20922 /**
20923  * @class Roo.data.ArrayReader
20924  * @extends Roo.data.DataReader
20925  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20926  * Each element of that Array represents a row of data fields. The
20927  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20928  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20929  * <p>
20930  * Example code:.
20931  * <pre><code>
20932 var RecordDef = Roo.data.Record.create([
20933     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20934     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20935 ]);
20936 var myReader = new Roo.data.ArrayReader({
20937     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20938 }, RecordDef);
20939 </code></pre>
20940  * <p>
20941  * This would consume an Array like this:
20942  * <pre><code>
20943 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20944   </code></pre>
20945  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20946  * @constructor
20947  * Create a new JsonReader
20948  * @param {Object} meta Metadata configuration options.
20949  * @param {Object} recordType Either an Array of field definition objects
20950  * as specified to {@link Roo.data.Record#create},
20951  * or an {@link Roo.data.Record} object
20952  * created using {@link Roo.data.Record#create}.
20953  */
20954 Roo.data.ArrayReader = function(meta, recordType){
20955     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20956 };
20957
20958 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20959     /**
20960      * Create a data block containing Roo.data.Records from an XML document.
20961      * @param {Object} o An Array of row objects which represents the dataset.
20962      * @return {Object} data A data block which is used by an Roo.data.Store object as
20963      * a cache of Roo.data.Records.
20964      */
20965     readRecords : function(o){
20966         var sid = this.meta ? this.meta.id : null;
20967         var recordType = this.recordType, fields = recordType.prototype.fields;
20968         var records = [];
20969         var root = o;
20970             for(var i = 0; i < root.length; i++){
20971                     var n = root[i];
20972                 var values = {};
20973                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20974                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20975                 var f = fields.items[j];
20976                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20977                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20978                 v = f.convert(v);
20979                 values[f.name] = v;
20980             }
20981                 var record = new recordType(values, id);
20982                 record.json = n;
20983                 records[records.length] = record;
20984             }
20985             return {
20986                 records : records,
20987                 totalRecords : records.length
20988             };
20989     }
20990 });/*
20991  * Based on:
20992  * Ext JS Library 1.1.1
20993  * Copyright(c) 2006-2007, Ext JS, LLC.
20994  *
20995  * Originally Released Under LGPL - original licence link has changed is not relivant.
20996  *
20997  * Fork - LGPL
20998  * <script type="text/javascript">
20999  */
21000
21001
21002 /**
21003  * @class Roo.data.Tree
21004  * @extends Roo.util.Observable
21005  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21006  * in the tree have most standard DOM functionality.
21007  * @constructor
21008  * @param {Node} root (optional) The root node
21009  */
21010 Roo.data.Tree = function(root){
21011    this.nodeHash = {};
21012    /**
21013     * The root node for this tree
21014     * @type Node
21015     */
21016    this.root = null;
21017    if(root){
21018        this.setRootNode(root);
21019    }
21020    this.addEvents({
21021        /**
21022         * @event append
21023         * Fires when a new child node is appended to a node in this tree.
21024         * @param {Tree} tree The owner tree
21025         * @param {Node} parent The parent node
21026         * @param {Node} node The newly appended node
21027         * @param {Number} index The index of the newly appended node
21028         */
21029        "append" : true,
21030        /**
21031         * @event remove
21032         * Fires when a child node is removed from a node in this tree.
21033         * @param {Tree} tree The owner tree
21034         * @param {Node} parent The parent node
21035         * @param {Node} node The child node removed
21036         */
21037        "remove" : true,
21038        /**
21039         * @event move
21040         * Fires when a node is moved to a new location in the tree
21041         * @param {Tree} tree The owner tree
21042         * @param {Node} node The node moved
21043         * @param {Node} oldParent The old parent of this node
21044         * @param {Node} newParent The new parent of this node
21045         * @param {Number} index The index it was moved to
21046         */
21047        "move" : true,
21048        /**
21049         * @event insert
21050         * Fires when a new child node is inserted in a node in this tree.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node inserted
21054         * @param {Node} refNode The child node the node was inserted before
21055         */
21056        "insert" : true,
21057        /**
21058         * @event beforeappend
21059         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21060         * @param {Tree} tree The owner tree
21061         * @param {Node} parent The parent node
21062         * @param {Node} node The child node to be appended
21063         */
21064        "beforeappend" : true,
21065        /**
21066         * @event beforeremove
21067         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21068         * @param {Tree} tree The owner tree
21069         * @param {Node} parent The parent node
21070         * @param {Node} node The child node to be removed
21071         */
21072        "beforeremove" : true,
21073        /**
21074         * @event beforemove
21075         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21076         * @param {Tree} tree The owner tree
21077         * @param {Node} node The node being moved
21078         * @param {Node} oldParent The parent of the node
21079         * @param {Node} newParent The new parent the node is moving to
21080         * @param {Number} index The index it is being moved to
21081         */
21082        "beforemove" : true,
21083        /**
21084         * @event beforeinsert
21085         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21086         * @param {Tree} tree The owner tree
21087         * @param {Node} parent The parent node
21088         * @param {Node} node The child node to be inserted
21089         * @param {Node} refNode The child node the node is being inserted before
21090         */
21091        "beforeinsert" : true
21092    });
21093
21094     Roo.data.Tree.superclass.constructor.call(this);
21095 };
21096
21097 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21098     pathSeparator: "/",
21099
21100     proxyNodeEvent : function(){
21101         return this.fireEvent.apply(this, arguments);
21102     },
21103
21104     /**
21105      * Returns the root node for this tree.
21106      * @return {Node}
21107      */
21108     getRootNode : function(){
21109         return this.root;
21110     },
21111
21112     /**
21113      * Sets the root node for this tree.
21114      * @param {Node} node
21115      * @return {Node}
21116      */
21117     setRootNode : function(node){
21118         this.root = node;
21119         node.ownerTree = this;
21120         node.isRoot = true;
21121         this.registerNode(node);
21122         return node;
21123     },
21124
21125     /**
21126      * Gets a node in this tree by its id.
21127      * @param {String} id
21128      * @return {Node}
21129      */
21130     getNodeById : function(id){
21131         return this.nodeHash[id];
21132     },
21133
21134     registerNode : function(node){
21135         this.nodeHash[node.id] = node;
21136     },
21137
21138     unregisterNode : function(node){
21139         delete this.nodeHash[node.id];
21140     },
21141
21142     toString : function(){
21143         return "[Tree"+(this.id?" "+this.id:"")+"]";
21144     }
21145 });
21146
21147 /**
21148  * @class Roo.data.Node
21149  * @extends Roo.util.Observable
21150  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21151  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21152  * @constructor
21153  * @param {Object} attributes The attributes/config for the node
21154  */
21155 Roo.data.Node = function(attributes){
21156     /**
21157      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21158      * @type {Object}
21159      */
21160     this.attributes = attributes || {};
21161     this.leaf = this.attributes.leaf;
21162     /**
21163      * The node id. @type String
21164      */
21165     this.id = this.attributes.id;
21166     if(!this.id){
21167         this.id = Roo.id(null, "ynode-");
21168         this.attributes.id = this.id;
21169     }
21170     /**
21171      * All child nodes of this node. @type Array
21172      */
21173     this.childNodes = [];
21174     if(!this.childNodes.indexOf){ // indexOf is a must
21175         this.childNodes.indexOf = function(o){
21176             for(var i = 0, len = this.length; i < len; i++){
21177                 if(this[i] == o) {
21178                     return i;
21179                 }
21180             }
21181             return -1;
21182         };
21183     }
21184     /**
21185      * The parent node for this node. @type Node
21186      */
21187     this.parentNode = null;
21188     /**
21189      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21190      */
21191     this.firstChild = null;
21192     /**
21193      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21194      */
21195     this.lastChild = null;
21196     /**
21197      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21198      */
21199     this.previousSibling = null;
21200     /**
21201      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21202      */
21203     this.nextSibling = null;
21204
21205     this.addEvents({
21206        /**
21207         * @event append
21208         * Fires when a new child node is appended
21209         * @param {Tree} tree The owner tree
21210         * @param {Node} this This node
21211         * @param {Node} node The newly appended node
21212         * @param {Number} index The index of the newly appended node
21213         */
21214        "append" : true,
21215        /**
21216         * @event remove
21217         * Fires when a child node is removed
21218         * @param {Tree} tree The owner tree
21219         * @param {Node} this This node
21220         * @param {Node} node The removed node
21221         */
21222        "remove" : true,
21223        /**
21224         * @event move
21225         * Fires when this node is moved to a new location in the tree
21226         * @param {Tree} tree The owner tree
21227         * @param {Node} this This node
21228         * @param {Node} oldParent The old parent of this node
21229         * @param {Node} newParent The new parent of this node
21230         * @param {Number} index The index it was moved to
21231         */
21232        "move" : true,
21233        /**
21234         * @event insert
21235         * Fires when a new child node is inserted.
21236         * @param {Tree} tree The owner tree
21237         * @param {Node} this This node
21238         * @param {Node} node The child node inserted
21239         * @param {Node} refNode The child node the node was inserted before
21240         */
21241        "insert" : true,
21242        /**
21243         * @event beforeappend
21244         * Fires before a new child is appended, return false to cancel the append.
21245         * @param {Tree} tree The owner tree
21246         * @param {Node} this This node
21247         * @param {Node} node The child node to be appended
21248         */
21249        "beforeappend" : true,
21250        /**
21251         * @event beforeremove
21252         * Fires before a child is removed, return false to cancel the remove.
21253         * @param {Tree} tree The owner tree
21254         * @param {Node} this This node
21255         * @param {Node} node The child node to be removed
21256         */
21257        "beforeremove" : true,
21258        /**
21259         * @event beforemove
21260         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21261         * @param {Tree} tree The owner tree
21262         * @param {Node} this This node
21263         * @param {Node} oldParent The parent of this node
21264         * @param {Node} newParent The new parent this node is moving to
21265         * @param {Number} index The index it is being moved to
21266         */
21267        "beforemove" : true,
21268        /**
21269         * @event beforeinsert
21270         * Fires before a new child is inserted, return false to cancel the insert.
21271         * @param {Tree} tree The owner tree
21272         * @param {Node} this This node
21273         * @param {Node} node The child node to be inserted
21274         * @param {Node} refNode The child node the node is being inserted before
21275         */
21276        "beforeinsert" : true
21277    });
21278     this.listeners = this.attributes.listeners;
21279     Roo.data.Node.superclass.constructor.call(this);
21280 };
21281
21282 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21283     fireEvent : function(evtName){
21284         // first do standard event for this node
21285         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21286             return false;
21287         }
21288         // then bubble it up to the tree if the event wasn't cancelled
21289         var ot = this.getOwnerTree();
21290         if(ot){
21291             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21292                 return false;
21293             }
21294         }
21295         return true;
21296     },
21297
21298     /**
21299      * Returns true if this node is a leaf
21300      * @return {Boolean}
21301      */
21302     isLeaf : function(){
21303         return this.leaf === true;
21304     },
21305
21306     // private
21307     setFirstChild : function(node){
21308         this.firstChild = node;
21309     },
21310
21311     //private
21312     setLastChild : function(node){
21313         this.lastChild = node;
21314     },
21315
21316
21317     /**
21318      * Returns true if this node is the last child of its parent
21319      * @return {Boolean}
21320      */
21321     isLast : function(){
21322        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21323     },
21324
21325     /**
21326      * Returns true if this node is the first child of its parent
21327      * @return {Boolean}
21328      */
21329     isFirst : function(){
21330        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21331     },
21332
21333     hasChildNodes : function(){
21334         return !this.isLeaf() && this.childNodes.length > 0;
21335     },
21336
21337     /**
21338      * Insert node(s) as the last child node of this node.
21339      * @param {Node/Array} node The node or Array of nodes to append
21340      * @return {Node} The appended node if single append, or null if an array was passed
21341      */
21342     appendChild : function(node){
21343         var multi = false;
21344         if(node instanceof Array){
21345             multi = node;
21346         }else if(arguments.length > 1){
21347             multi = arguments;
21348         }
21349         // if passed an array or multiple args do them one by one
21350         if(multi){
21351             for(var i = 0, len = multi.length; i < len; i++) {
21352                 this.appendChild(multi[i]);
21353             }
21354         }else{
21355             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21356                 return false;
21357             }
21358             var index = this.childNodes.length;
21359             var oldParent = node.parentNode;
21360             // it's a move, make sure we move it cleanly
21361             if(oldParent){
21362                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21363                     return false;
21364                 }
21365                 oldParent.removeChild(node);
21366             }
21367             index = this.childNodes.length;
21368             if(index == 0){
21369                 this.setFirstChild(node);
21370             }
21371             this.childNodes.push(node);
21372             node.parentNode = this;
21373             var ps = this.childNodes[index-1];
21374             if(ps){
21375                 node.previousSibling = ps;
21376                 ps.nextSibling = node;
21377             }else{
21378                 node.previousSibling = null;
21379             }
21380             node.nextSibling = null;
21381             this.setLastChild(node);
21382             node.setOwnerTree(this.getOwnerTree());
21383             this.fireEvent("append", this.ownerTree, this, node, index);
21384             if(oldParent){
21385                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21386             }
21387             return node;
21388         }
21389     },
21390
21391     /**
21392      * Removes a child node from this node.
21393      * @param {Node} node The node to remove
21394      * @return {Node} The removed node
21395      */
21396     removeChild : function(node){
21397         var index = this.childNodes.indexOf(node);
21398         if(index == -1){
21399             return false;
21400         }
21401         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21402             return false;
21403         }
21404
21405         // remove it from childNodes collection
21406         this.childNodes.splice(index, 1);
21407
21408         // update siblings
21409         if(node.previousSibling){
21410             node.previousSibling.nextSibling = node.nextSibling;
21411         }
21412         if(node.nextSibling){
21413             node.nextSibling.previousSibling = node.previousSibling;
21414         }
21415
21416         // update child refs
21417         if(this.firstChild == node){
21418             this.setFirstChild(node.nextSibling);
21419         }
21420         if(this.lastChild == node){
21421             this.setLastChild(node.previousSibling);
21422         }
21423
21424         node.setOwnerTree(null);
21425         // clear any references from the node
21426         node.parentNode = null;
21427         node.previousSibling = null;
21428         node.nextSibling = null;
21429         this.fireEvent("remove", this.ownerTree, this, node);
21430         return node;
21431     },
21432
21433     /**
21434      * Inserts the first node before the second node in this nodes childNodes collection.
21435      * @param {Node} node The node to insert
21436      * @param {Node} refNode The node to insert before (if null the node is appended)
21437      * @return {Node} The inserted node
21438      */
21439     insertBefore : function(node, refNode){
21440         if(!refNode){ // like standard Dom, refNode can be null for append
21441             return this.appendChild(node);
21442         }
21443         // nothing to do
21444         if(node == refNode){
21445             return false;
21446         }
21447
21448         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21449             return false;
21450         }
21451         var index = this.childNodes.indexOf(refNode);
21452         var oldParent = node.parentNode;
21453         var refIndex = index;
21454
21455         // when moving internally, indexes will change after remove
21456         if(oldParent == this && this.childNodes.indexOf(node) < index){
21457             refIndex--;
21458         }
21459
21460         // it's a move, make sure we move it cleanly
21461         if(oldParent){
21462             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21463                 return false;
21464             }
21465             oldParent.removeChild(node);
21466         }
21467         if(refIndex == 0){
21468             this.setFirstChild(node);
21469         }
21470         this.childNodes.splice(refIndex, 0, node);
21471         node.parentNode = this;
21472         var ps = this.childNodes[refIndex-1];
21473         if(ps){
21474             node.previousSibling = ps;
21475             ps.nextSibling = node;
21476         }else{
21477             node.previousSibling = null;
21478         }
21479         node.nextSibling = refNode;
21480         refNode.previousSibling = node;
21481         node.setOwnerTree(this.getOwnerTree());
21482         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21483         if(oldParent){
21484             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21485         }
21486         return node;
21487     },
21488
21489     /**
21490      * Returns the child node at the specified index.
21491      * @param {Number} index
21492      * @return {Node}
21493      */
21494     item : function(index){
21495         return this.childNodes[index];
21496     },
21497
21498     /**
21499      * Replaces one child node in this node with another.
21500      * @param {Node} newChild The replacement node
21501      * @param {Node} oldChild The node to replace
21502      * @return {Node} The replaced node
21503      */
21504     replaceChild : function(newChild, oldChild){
21505         this.insertBefore(newChild, oldChild);
21506         this.removeChild(oldChild);
21507         return oldChild;
21508     },
21509
21510     /**
21511      * Returns the index of a child node
21512      * @param {Node} node
21513      * @return {Number} The index of the node or -1 if it was not found
21514      */
21515     indexOf : function(child){
21516         return this.childNodes.indexOf(child);
21517     },
21518
21519     /**
21520      * Returns the tree this node is in.
21521      * @return {Tree}
21522      */
21523     getOwnerTree : function(){
21524         // if it doesn't have one, look for one
21525         if(!this.ownerTree){
21526             var p = this;
21527             while(p){
21528                 if(p.ownerTree){
21529                     this.ownerTree = p.ownerTree;
21530                     break;
21531                 }
21532                 p = p.parentNode;
21533             }
21534         }
21535         return this.ownerTree;
21536     },
21537
21538     /**
21539      * Returns depth of this node (the root node has a depth of 0)
21540      * @return {Number}
21541      */
21542     getDepth : function(){
21543         var depth = 0;
21544         var p = this;
21545         while(p.parentNode){
21546             ++depth;
21547             p = p.parentNode;
21548         }
21549         return depth;
21550     },
21551
21552     // private
21553     setOwnerTree : function(tree){
21554         // if it's move, we need to update everyone
21555         if(tree != this.ownerTree){
21556             if(this.ownerTree){
21557                 this.ownerTree.unregisterNode(this);
21558             }
21559             this.ownerTree = tree;
21560             var cs = this.childNodes;
21561             for(var i = 0, len = cs.length; i < len; i++) {
21562                 cs[i].setOwnerTree(tree);
21563             }
21564             if(tree){
21565                 tree.registerNode(this);
21566             }
21567         }
21568     },
21569
21570     /**
21571      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21572      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21573      * @return {String} The path
21574      */
21575     getPath : function(attr){
21576         attr = attr || "id";
21577         var p = this.parentNode;
21578         var b = [this.attributes[attr]];
21579         while(p){
21580             b.unshift(p.attributes[attr]);
21581             p = p.parentNode;
21582         }
21583         var sep = this.getOwnerTree().pathSeparator;
21584         return sep + b.join(sep);
21585     },
21586
21587     /**
21588      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21589      * function call will be the scope provided or the current node. The arguments to the function
21590      * will be the args provided or the current node. If the function returns false at any point,
21591      * the bubble is stopped.
21592      * @param {Function} fn The function to call
21593      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21594      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21595      */
21596     bubble : function(fn, scope, args){
21597         var p = this;
21598         while(p){
21599             if(fn.call(scope || p, args || p) === false){
21600                 break;
21601             }
21602             p = p.parentNode;
21603         }
21604     },
21605
21606     /**
21607      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21608      * function call will be the scope provided or the current node. The arguments to the function
21609      * will be the args provided or the current node. If the function returns false at any point,
21610      * the cascade is stopped on that branch.
21611      * @param {Function} fn The function to call
21612      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21613      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21614      */
21615     cascade : function(fn, scope, args){
21616         if(fn.call(scope || this, args || this) !== false){
21617             var cs = this.childNodes;
21618             for(var i = 0, len = cs.length; i < len; i++) {
21619                 cs[i].cascade(fn, scope, args);
21620             }
21621         }
21622     },
21623
21624     /**
21625      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21626      * function call will be the scope provided or the current node. The arguments to the function
21627      * will be the args provided or the current node. If the function returns false at any point,
21628      * the iteration stops.
21629      * @param {Function} fn The function to call
21630      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21631      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21632      */
21633     eachChild : function(fn, scope, args){
21634         var cs = this.childNodes;
21635         for(var i = 0, len = cs.length; i < len; i++) {
21636                 if(fn.call(scope || this, args || cs[i]) === false){
21637                     break;
21638                 }
21639         }
21640     },
21641
21642     /**
21643      * Finds the first child that has the attribute with the specified value.
21644      * @param {String} attribute The attribute name
21645      * @param {Mixed} value The value to search for
21646      * @return {Node} The found child or null if none was found
21647      */
21648     findChild : function(attribute, value){
21649         var cs = this.childNodes;
21650         for(var i = 0, len = cs.length; i < len; i++) {
21651                 if(cs[i].attributes[attribute] == value){
21652                     return cs[i];
21653                 }
21654         }
21655         return null;
21656     },
21657
21658     /**
21659      * Finds the first child by a custom function. The child matches if the function passed
21660      * returns true.
21661      * @param {Function} fn
21662      * @param {Object} scope (optional)
21663      * @return {Node} The found child or null if none was found
21664      */
21665     findChildBy : function(fn, scope){
21666         var cs = this.childNodes;
21667         for(var i = 0, len = cs.length; i < len; i++) {
21668                 if(fn.call(scope||cs[i], cs[i]) === true){
21669                     return cs[i];
21670                 }
21671         }
21672         return null;
21673     },
21674
21675     /**
21676      * Sorts this nodes children using the supplied sort function
21677      * @param {Function} fn
21678      * @param {Object} scope (optional)
21679      */
21680     sort : function(fn, scope){
21681         var cs = this.childNodes;
21682         var len = cs.length;
21683         if(len > 0){
21684             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21685             cs.sort(sortFn);
21686             for(var i = 0; i < len; i++){
21687                 var n = cs[i];
21688                 n.previousSibling = cs[i-1];
21689                 n.nextSibling = cs[i+1];
21690                 if(i == 0){
21691                     this.setFirstChild(n);
21692                 }
21693                 if(i == len-1){
21694                     this.setLastChild(n);
21695                 }
21696             }
21697         }
21698     },
21699
21700     /**
21701      * Returns true if this node is an ancestor (at any point) of the passed node.
21702      * @param {Node} node
21703      * @return {Boolean}
21704      */
21705     contains : function(node){
21706         return node.isAncestor(this);
21707     },
21708
21709     /**
21710      * Returns true if the passed node is an ancestor (at any point) of this node.
21711      * @param {Node} node
21712      * @return {Boolean}
21713      */
21714     isAncestor : function(node){
21715         var p = this.parentNode;
21716         while(p){
21717             if(p == node){
21718                 return true;
21719             }
21720             p = p.parentNode;
21721         }
21722         return false;
21723     },
21724
21725     toString : function(){
21726         return "[Node"+(this.id?" "+this.id:"")+"]";
21727     }
21728 });/*
21729  * Based on:
21730  * Ext JS Library 1.1.1
21731  * Copyright(c) 2006-2007, Ext JS, LLC.
21732  *
21733  * Originally Released Under LGPL - original licence link has changed is not relivant.
21734  *
21735  * Fork - LGPL
21736  * <script type="text/javascript">
21737  */
21738  
21739
21740 /**
21741  * @class Roo.ComponentMgr
21742  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21743  * @singleton
21744  */
21745 Roo.ComponentMgr = function(){
21746     var all = new Roo.util.MixedCollection();
21747
21748     return {
21749         /**
21750          * Registers a component.
21751          * @param {Roo.Component} c The component
21752          */
21753         register : function(c){
21754             all.add(c);
21755         },
21756
21757         /**
21758          * Unregisters a component.
21759          * @param {Roo.Component} c The component
21760          */
21761         unregister : function(c){
21762             all.remove(c);
21763         },
21764
21765         /**
21766          * Returns a component by id
21767          * @param {String} id The component id
21768          */
21769         get : function(id){
21770             return all.get(id);
21771         },
21772
21773         /**
21774          * Registers a function that will be called when a specified component is added to ComponentMgr
21775          * @param {String} id The component id
21776          * @param {Funtction} fn The callback function
21777          * @param {Object} scope The scope of the callback
21778          */
21779         onAvailable : function(id, fn, scope){
21780             all.on("add", function(index, o){
21781                 if(o.id == id){
21782                     fn.call(scope || o, o);
21783                     all.un("add", fn, scope);
21784                 }
21785             });
21786         }
21787     };
21788 }();/*
21789  * Based on:
21790  * Ext JS Library 1.1.1
21791  * Copyright(c) 2006-2007, Ext JS, LLC.
21792  *
21793  * Originally Released Under LGPL - original licence link has changed is not relivant.
21794  *
21795  * Fork - LGPL
21796  * <script type="text/javascript">
21797  */
21798  
21799 /**
21800  * @class Roo.Component
21801  * @extends Roo.util.Observable
21802  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21803  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21804  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21805  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21806  * All visual components (widgets) that require rendering into a layout should subclass Component.
21807  * @constructor
21808  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21809  * 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
21810  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21811  */
21812 Roo.Component = function(config){
21813     config = config || {};
21814     if(config.tagName || config.dom || typeof config == "string"){ // element object
21815         config = {el: config, id: config.id || config};
21816     }
21817     this.initialConfig = config;
21818
21819     Roo.apply(this, config);
21820     this.addEvents({
21821         /**
21822          * @event disable
21823          * Fires after the component is disabled.
21824              * @param {Roo.Component} this
21825              */
21826         disable : true,
21827         /**
21828          * @event enable
21829          * Fires after the component is enabled.
21830              * @param {Roo.Component} this
21831              */
21832         enable : true,
21833         /**
21834          * @event beforeshow
21835          * Fires before the component is shown.  Return false to stop the show.
21836              * @param {Roo.Component} this
21837              */
21838         beforeshow : true,
21839         /**
21840          * @event show
21841          * Fires after the component is shown.
21842              * @param {Roo.Component} this
21843              */
21844         show : true,
21845         /**
21846          * @event beforehide
21847          * Fires before the component is hidden. Return false to stop the hide.
21848              * @param {Roo.Component} this
21849              */
21850         beforehide : true,
21851         /**
21852          * @event hide
21853          * Fires after the component is hidden.
21854              * @param {Roo.Component} this
21855              */
21856         hide : true,
21857         /**
21858          * @event beforerender
21859          * Fires before the component is rendered. Return false to stop the render.
21860              * @param {Roo.Component} this
21861              */
21862         beforerender : true,
21863         /**
21864          * @event render
21865          * Fires after the component is rendered.
21866              * @param {Roo.Component} this
21867              */
21868         render : true,
21869         /**
21870          * @event beforedestroy
21871          * Fires before the component is destroyed. Return false to stop the destroy.
21872              * @param {Roo.Component} this
21873              */
21874         beforedestroy : true,
21875         /**
21876          * @event destroy
21877          * Fires after the component is destroyed.
21878              * @param {Roo.Component} this
21879              */
21880         destroy : true
21881     });
21882     if(!this.id){
21883         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21884     }
21885     Roo.ComponentMgr.register(this);
21886     Roo.Component.superclass.constructor.call(this);
21887     this.initComponent();
21888     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21889         this.render(this.renderTo);
21890         delete this.renderTo;
21891     }
21892 };
21893
21894 /** @private */
21895 Roo.Component.AUTO_ID = 1000;
21896
21897 Roo.extend(Roo.Component, Roo.util.Observable, {
21898     /**
21899      * @scope Roo.Component.prototype
21900      * @type {Boolean}
21901      * true if this component is hidden. Read-only.
21902      */
21903     hidden : false,
21904     /**
21905      * @type {Boolean}
21906      * true if this component is disabled. Read-only.
21907      */
21908     disabled : false,
21909     /**
21910      * @type {Boolean}
21911      * true if this component has been rendered. Read-only.
21912      */
21913     rendered : false,
21914     
21915     /** @cfg {String} disableClass
21916      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21917      */
21918     disabledClass : "x-item-disabled",
21919         /** @cfg {Boolean} allowDomMove
21920          * Whether the component can move the Dom node when rendering (defaults to true).
21921          */
21922     allowDomMove : true,
21923     /** @cfg {String} hideMode
21924      * How this component should hidden. Supported values are
21925      * "visibility" (css visibility), "offsets" (negative offset position) and
21926      * "display" (css display) - defaults to "display".
21927      */
21928     hideMode: 'display',
21929
21930     /** @private */
21931     ctype : "Roo.Component",
21932
21933     /**
21934      * @cfg {String} actionMode 
21935      * which property holds the element that used for  hide() / show() / disable() / enable()
21936      * default is 'el' 
21937      */
21938     actionMode : "el",
21939
21940     /** @private */
21941     getActionEl : function(){
21942         return this[this.actionMode];
21943     },
21944
21945     initComponent : Roo.emptyFn,
21946     /**
21947      * If this is a lazy rendering component, render it to its container element.
21948      * @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.
21949      */
21950     render : function(container, position){
21951         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21952             if(!container && this.el){
21953                 this.el = Roo.get(this.el);
21954                 container = this.el.dom.parentNode;
21955                 this.allowDomMove = false;
21956             }
21957             this.container = Roo.get(container);
21958             this.rendered = true;
21959             if(position !== undefined){
21960                 if(typeof position == 'number'){
21961                     position = this.container.dom.childNodes[position];
21962                 }else{
21963                     position = Roo.getDom(position);
21964                 }
21965             }
21966             this.onRender(this.container, position || null);
21967             if(this.cls){
21968                 this.el.addClass(this.cls);
21969                 delete this.cls;
21970             }
21971             if(this.style){
21972                 this.el.applyStyles(this.style);
21973                 delete this.style;
21974             }
21975             this.fireEvent("render", this);
21976             this.afterRender(this.container);
21977             if(this.hidden){
21978                 this.hide();
21979             }
21980             if(this.disabled){
21981                 this.disable();
21982             }
21983         }
21984         return this;
21985     },
21986
21987     /** @private */
21988     // default function is not really useful
21989     onRender : function(ct, position){
21990         if(this.el){
21991             this.el = Roo.get(this.el);
21992             if(this.allowDomMove !== false){
21993                 ct.dom.insertBefore(this.el.dom, position);
21994             }
21995         }
21996     },
21997
21998     /** @private */
21999     getAutoCreate : function(){
22000         var cfg = typeof this.autoCreate == "object" ?
22001                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22002         if(this.id && !cfg.id){
22003             cfg.id = this.id;
22004         }
22005         return cfg;
22006     },
22007
22008     /** @private */
22009     afterRender : Roo.emptyFn,
22010
22011     /**
22012      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22013      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22014      */
22015     destroy : function(){
22016         if(this.fireEvent("beforedestroy", this) !== false){
22017             this.purgeListeners();
22018             this.beforeDestroy();
22019             if(this.rendered){
22020                 this.el.removeAllListeners();
22021                 this.el.remove();
22022                 if(this.actionMode == "container"){
22023                     this.container.remove();
22024                 }
22025             }
22026             this.onDestroy();
22027             Roo.ComponentMgr.unregister(this);
22028             this.fireEvent("destroy", this);
22029         }
22030     },
22031
22032         /** @private */
22033     beforeDestroy : function(){
22034
22035     },
22036
22037         /** @private */
22038         onDestroy : function(){
22039
22040     },
22041
22042     /**
22043      * Returns the underlying {@link Roo.Element}.
22044      * @return {Roo.Element} The element
22045      */
22046     getEl : function(){
22047         return this.el;
22048     },
22049
22050     /**
22051      * Returns the id of this component.
22052      * @return {String}
22053      */
22054     getId : function(){
22055         return this.id;
22056     },
22057
22058     /**
22059      * Try to focus this component.
22060      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22061      * @return {Roo.Component} this
22062      */
22063     focus : function(selectText){
22064         if(this.rendered){
22065             this.el.focus();
22066             if(selectText === true){
22067                 this.el.dom.select();
22068             }
22069         }
22070         return this;
22071     },
22072
22073     /** @private */
22074     blur : function(){
22075         if(this.rendered){
22076             this.el.blur();
22077         }
22078         return this;
22079     },
22080
22081     /**
22082      * Disable this component.
22083      * @return {Roo.Component} this
22084      */
22085     disable : function(){
22086         if(this.rendered){
22087             this.onDisable();
22088         }
22089         this.disabled = true;
22090         this.fireEvent("disable", this);
22091         return this;
22092     },
22093
22094         // private
22095     onDisable : function(){
22096         this.getActionEl().addClass(this.disabledClass);
22097         this.el.dom.disabled = true;
22098     },
22099
22100     /**
22101      * Enable this component.
22102      * @return {Roo.Component} this
22103      */
22104     enable : function(){
22105         if(this.rendered){
22106             this.onEnable();
22107         }
22108         this.disabled = false;
22109         this.fireEvent("enable", this);
22110         return this;
22111     },
22112
22113         // private
22114     onEnable : function(){
22115         this.getActionEl().removeClass(this.disabledClass);
22116         this.el.dom.disabled = false;
22117     },
22118
22119     /**
22120      * Convenience function for setting disabled/enabled by boolean.
22121      * @param {Boolean} disabled
22122      */
22123     setDisabled : function(disabled){
22124         this[disabled ? "disable" : "enable"]();
22125     },
22126
22127     /**
22128      * Show this component.
22129      * @return {Roo.Component} this
22130      */
22131     show: function(){
22132         if(this.fireEvent("beforeshow", this) !== false){
22133             this.hidden = false;
22134             if(this.rendered){
22135                 this.onShow();
22136             }
22137             this.fireEvent("show", this);
22138         }
22139         return this;
22140     },
22141
22142     // private
22143     onShow : function(){
22144         var ae = this.getActionEl();
22145         if(this.hideMode == 'visibility'){
22146             ae.dom.style.visibility = "visible";
22147         }else if(this.hideMode == 'offsets'){
22148             ae.removeClass('x-hidden');
22149         }else{
22150             ae.dom.style.display = "";
22151         }
22152     },
22153
22154     /**
22155      * Hide this component.
22156      * @return {Roo.Component} this
22157      */
22158     hide: function(){
22159         if(this.fireEvent("beforehide", this) !== false){
22160             this.hidden = true;
22161             if(this.rendered){
22162                 this.onHide();
22163             }
22164             this.fireEvent("hide", this);
22165         }
22166         return this;
22167     },
22168
22169     // private
22170     onHide : function(){
22171         var ae = this.getActionEl();
22172         if(this.hideMode == 'visibility'){
22173             ae.dom.style.visibility = "hidden";
22174         }else if(this.hideMode == 'offsets'){
22175             ae.addClass('x-hidden');
22176         }else{
22177             ae.dom.style.display = "none";
22178         }
22179     },
22180
22181     /**
22182      * Convenience function to hide or show this component by boolean.
22183      * @param {Boolean} visible True to show, false to hide
22184      * @return {Roo.Component} this
22185      */
22186     setVisible: function(visible){
22187         if(visible) {
22188             this.show();
22189         }else{
22190             this.hide();
22191         }
22192         return this;
22193     },
22194
22195     /**
22196      * Returns true if this component is visible.
22197      */
22198     isVisible : function(){
22199         return this.getActionEl().isVisible();
22200     },
22201
22202     cloneConfig : function(overrides){
22203         overrides = overrides || {};
22204         var id = overrides.id || Roo.id();
22205         var cfg = Roo.applyIf(overrides, this.initialConfig);
22206         cfg.id = id; // prevent dup id
22207         return new this.constructor(cfg);
22208     }
22209 });/*
22210  * Based on:
22211  * Ext JS Library 1.1.1
22212  * Copyright(c) 2006-2007, Ext JS, LLC.
22213  *
22214  * Originally Released Under LGPL - original licence link has changed is not relivant.
22215  *
22216  * Fork - LGPL
22217  * <script type="text/javascript">
22218  */
22219  (function(){ 
22220 /**
22221  * @class Roo.Layer
22222  * @extends Roo.Element
22223  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22224  * automatic maintaining of shadow/shim positions.
22225  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22226  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22227  * you can pass a string with a CSS class name. False turns off the shadow.
22228  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22229  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22230  * @cfg {String} cls CSS class to add to the element
22231  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22232  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22233  * @constructor
22234  * @param {Object} config An object with config options.
22235  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22236  */
22237
22238 Roo.Layer = function(config, existingEl){
22239     config = config || {};
22240     var dh = Roo.DomHelper;
22241     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22242     if(existingEl){
22243         this.dom = Roo.getDom(existingEl);
22244     }
22245     if(!this.dom){
22246         var o = config.dh || {tag: "div", cls: "x-layer"};
22247         this.dom = dh.append(pel, o);
22248     }
22249     if(config.cls){
22250         this.addClass(config.cls);
22251     }
22252     this.constrain = config.constrain !== false;
22253     this.visibilityMode = Roo.Element.VISIBILITY;
22254     if(config.id){
22255         this.id = this.dom.id = config.id;
22256     }else{
22257         this.id = Roo.id(this.dom);
22258     }
22259     this.zindex = config.zindex || this.getZIndex();
22260     this.position("absolute", this.zindex);
22261     if(config.shadow){
22262         this.shadowOffset = config.shadowOffset || 4;
22263         this.shadow = new Roo.Shadow({
22264             offset : this.shadowOffset,
22265             mode : config.shadow
22266         });
22267     }else{
22268         this.shadowOffset = 0;
22269     }
22270     this.useShim = config.shim !== false && Roo.useShims;
22271     this.useDisplay = config.useDisplay;
22272     this.hide();
22273 };
22274
22275 var supr = Roo.Element.prototype;
22276
22277 // shims are shared among layer to keep from having 100 iframes
22278 var shims = [];
22279
22280 Roo.extend(Roo.Layer, Roo.Element, {
22281
22282     getZIndex : function(){
22283         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22284     },
22285
22286     getShim : function(){
22287         if(!this.useShim){
22288             return null;
22289         }
22290         if(this.shim){
22291             return this.shim;
22292         }
22293         var shim = shims.shift();
22294         if(!shim){
22295             shim = this.createShim();
22296             shim.enableDisplayMode('block');
22297             shim.dom.style.display = 'none';
22298             shim.dom.style.visibility = 'visible';
22299         }
22300         var pn = this.dom.parentNode;
22301         if(shim.dom.parentNode != pn){
22302             pn.insertBefore(shim.dom, this.dom);
22303         }
22304         shim.setStyle('z-index', this.getZIndex()-2);
22305         this.shim = shim;
22306         return shim;
22307     },
22308
22309     hideShim : function(){
22310         if(this.shim){
22311             this.shim.setDisplayed(false);
22312             shims.push(this.shim);
22313             delete this.shim;
22314         }
22315     },
22316
22317     disableShadow : function(){
22318         if(this.shadow){
22319             this.shadowDisabled = true;
22320             this.shadow.hide();
22321             this.lastShadowOffset = this.shadowOffset;
22322             this.shadowOffset = 0;
22323         }
22324     },
22325
22326     enableShadow : function(show){
22327         if(this.shadow){
22328             this.shadowDisabled = false;
22329             this.shadowOffset = this.lastShadowOffset;
22330             delete this.lastShadowOffset;
22331             if(show){
22332                 this.sync(true);
22333             }
22334         }
22335     },
22336
22337     // private
22338     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22339     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22340     sync : function(doShow){
22341         var sw = this.shadow;
22342         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22343             var sh = this.getShim();
22344
22345             var w = this.getWidth(),
22346                 h = this.getHeight();
22347
22348             var l = this.getLeft(true),
22349                 t = this.getTop(true);
22350
22351             if(sw && !this.shadowDisabled){
22352                 if(doShow && !sw.isVisible()){
22353                     sw.show(this);
22354                 }else{
22355                     sw.realign(l, t, w, h);
22356                 }
22357                 if(sh){
22358                     if(doShow){
22359                        sh.show();
22360                     }
22361                     // fit the shim behind the shadow, so it is shimmed too
22362                     var a = sw.adjusts, s = sh.dom.style;
22363                     s.left = (Math.min(l, l+a.l))+"px";
22364                     s.top = (Math.min(t, t+a.t))+"px";
22365                     s.width = (w+a.w)+"px";
22366                     s.height = (h+a.h)+"px";
22367                 }
22368             }else if(sh){
22369                 if(doShow){
22370                    sh.show();
22371                 }
22372                 sh.setSize(w, h);
22373                 sh.setLeftTop(l, t);
22374             }
22375             
22376         }
22377     },
22378
22379     // private
22380     destroy : function(){
22381         this.hideShim();
22382         if(this.shadow){
22383             this.shadow.hide();
22384         }
22385         this.removeAllListeners();
22386         var pn = this.dom.parentNode;
22387         if(pn){
22388             pn.removeChild(this.dom);
22389         }
22390         Roo.Element.uncache(this.id);
22391     },
22392
22393     remove : function(){
22394         this.destroy();
22395     },
22396
22397     // private
22398     beginUpdate : function(){
22399         this.updating = true;
22400     },
22401
22402     // private
22403     endUpdate : function(){
22404         this.updating = false;
22405         this.sync(true);
22406     },
22407
22408     // private
22409     hideUnders : function(negOffset){
22410         if(this.shadow){
22411             this.shadow.hide();
22412         }
22413         this.hideShim();
22414     },
22415
22416     // private
22417     constrainXY : function(){
22418         if(this.constrain){
22419             var vw = Roo.lib.Dom.getViewWidth(),
22420                 vh = Roo.lib.Dom.getViewHeight();
22421             var s = Roo.get(document).getScroll();
22422
22423             var xy = this.getXY();
22424             var x = xy[0], y = xy[1];   
22425             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22426             // only move it if it needs it
22427             var moved = false;
22428             // first validate right/bottom
22429             if((x + w) > vw+s.left){
22430                 x = vw - w - this.shadowOffset;
22431                 moved = true;
22432             }
22433             if((y + h) > vh+s.top){
22434                 y = vh - h - this.shadowOffset;
22435                 moved = true;
22436             }
22437             // then make sure top/left isn't negative
22438             if(x < s.left){
22439                 x = s.left;
22440                 moved = true;
22441             }
22442             if(y < s.top){
22443                 y = s.top;
22444                 moved = true;
22445             }
22446             if(moved){
22447                 if(this.avoidY){
22448                     var ay = this.avoidY;
22449                     if(y <= ay && (y+h) >= ay){
22450                         y = ay-h-5;   
22451                     }
22452                 }
22453                 xy = [x, y];
22454                 this.storeXY(xy);
22455                 supr.setXY.call(this, xy);
22456                 this.sync();
22457             }
22458         }
22459     },
22460
22461     isVisible : function(){
22462         return this.visible;    
22463     },
22464
22465     // private
22466     showAction : function(){
22467         this.visible = true; // track visibility to prevent getStyle calls
22468         if(this.useDisplay === true){
22469             this.setDisplayed("");
22470         }else if(this.lastXY){
22471             supr.setXY.call(this, this.lastXY);
22472         }else if(this.lastLT){
22473             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22474         }
22475     },
22476
22477     // private
22478     hideAction : function(){
22479         this.visible = false;
22480         if(this.useDisplay === true){
22481             this.setDisplayed(false);
22482         }else{
22483             this.setLeftTop(-10000,-10000);
22484         }
22485     },
22486
22487     // overridden Element method
22488     setVisible : function(v, a, d, c, e){
22489         if(v){
22490             this.showAction();
22491         }
22492         if(a && v){
22493             var cb = function(){
22494                 this.sync(true);
22495                 if(c){
22496                     c();
22497                 }
22498             }.createDelegate(this);
22499             supr.setVisible.call(this, true, true, d, cb, e);
22500         }else{
22501             if(!v){
22502                 this.hideUnders(true);
22503             }
22504             var cb = c;
22505             if(a){
22506                 cb = function(){
22507                     this.hideAction();
22508                     if(c){
22509                         c();
22510                     }
22511                 }.createDelegate(this);
22512             }
22513             supr.setVisible.call(this, v, a, d, cb, e);
22514             if(v){
22515                 this.sync(true);
22516             }else if(!a){
22517                 this.hideAction();
22518             }
22519         }
22520     },
22521
22522     storeXY : function(xy){
22523         delete this.lastLT;
22524         this.lastXY = xy;
22525     },
22526
22527     storeLeftTop : function(left, top){
22528         delete this.lastXY;
22529         this.lastLT = [left, top];
22530     },
22531
22532     // private
22533     beforeFx : function(){
22534         this.beforeAction();
22535         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22536     },
22537
22538     // private
22539     afterFx : function(){
22540         Roo.Layer.superclass.afterFx.apply(this, arguments);
22541         this.sync(this.isVisible());
22542     },
22543
22544     // private
22545     beforeAction : function(){
22546         if(!this.updating && this.shadow){
22547             this.shadow.hide();
22548         }
22549     },
22550
22551     // overridden Element method
22552     setLeft : function(left){
22553         this.storeLeftTop(left, this.getTop(true));
22554         supr.setLeft.apply(this, arguments);
22555         this.sync();
22556     },
22557
22558     setTop : function(top){
22559         this.storeLeftTop(this.getLeft(true), top);
22560         supr.setTop.apply(this, arguments);
22561         this.sync();
22562     },
22563
22564     setLeftTop : function(left, top){
22565         this.storeLeftTop(left, top);
22566         supr.setLeftTop.apply(this, arguments);
22567         this.sync();
22568     },
22569
22570     setXY : function(xy, a, d, c, e){
22571         this.fixDisplay();
22572         this.beforeAction();
22573         this.storeXY(xy);
22574         var cb = this.createCB(c);
22575         supr.setXY.call(this, xy, a, d, cb, e);
22576         if(!a){
22577             cb();
22578         }
22579     },
22580
22581     // private
22582     createCB : function(c){
22583         var el = this;
22584         return function(){
22585             el.constrainXY();
22586             el.sync(true);
22587             if(c){
22588                 c();
22589             }
22590         };
22591     },
22592
22593     // overridden Element method
22594     setX : function(x, a, d, c, e){
22595         this.setXY([x, this.getY()], a, d, c, e);
22596     },
22597
22598     // overridden Element method
22599     setY : function(y, a, d, c, e){
22600         this.setXY([this.getX(), y], a, d, c, e);
22601     },
22602
22603     // overridden Element method
22604     setSize : function(w, h, a, d, c, e){
22605         this.beforeAction();
22606         var cb = this.createCB(c);
22607         supr.setSize.call(this, w, h, a, d, cb, e);
22608         if(!a){
22609             cb();
22610         }
22611     },
22612
22613     // overridden Element method
22614     setWidth : function(w, a, d, c, e){
22615         this.beforeAction();
22616         var cb = this.createCB(c);
22617         supr.setWidth.call(this, w, a, d, cb, e);
22618         if(!a){
22619             cb();
22620         }
22621     },
22622
22623     // overridden Element method
22624     setHeight : function(h, a, d, c, e){
22625         this.beforeAction();
22626         var cb = this.createCB(c);
22627         supr.setHeight.call(this, h, a, d, cb, e);
22628         if(!a){
22629             cb();
22630         }
22631     },
22632
22633     // overridden Element method
22634     setBounds : function(x, y, w, h, a, d, c, e){
22635         this.beforeAction();
22636         var cb = this.createCB(c);
22637         if(!a){
22638             this.storeXY([x, y]);
22639             supr.setXY.call(this, [x, y]);
22640             supr.setSize.call(this, w, h, a, d, cb, e);
22641             cb();
22642         }else{
22643             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22644         }
22645         return this;
22646     },
22647     
22648     /**
22649      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22650      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22651      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22652      * @param {Number} zindex The new z-index to set
22653      * @return {this} The Layer
22654      */
22655     setZIndex : function(zindex){
22656         this.zindex = zindex;
22657         this.setStyle("z-index", zindex + 2);
22658         if(this.shadow){
22659             this.shadow.setZIndex(zindex + 1);
22660         }
22661         if(this.shim){
22662             this.shim.setStyle("z-index", zindex);
22663         }
22664     }
22665 });
22666 })();/*
22667  * Based on:
22668  * Ext JS Library 1.1.1
22669  * Copyright(c) 2006-2007, Ext JS, LLC.
22670  *
22671  * Originally Released Under LGPL - original licence link has changed is not relivant.
22672  *
22673  * Fork - LGPL
22674  * <script type="text/javascript">
22675  */
22676
22677
22678 /**
22679  * @class Roo.Shadow
22680  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22681  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22682  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22683  * @constructor
22684  * Create a new Shadow
22685  * @param {Object} config The config object
22686  */
22687 Roo.Shadow = function(config){
22688     Roo.apply(this, config);
22689     if(typeof this.mode != "string"){
22690         this.mode = this.defaultMode;
22691     }
22692     var o = this.offset, a = {h: 0};
22693     var rad = Math.floor(this.offset/2);
22694     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22695         case "drop":
22696             a.w = 0;
22697             a.l = a.t = o;
22698             a.t -= 1;
22699             if(Roo.isIE){
22700                 a.l -= this.offset + rad;
22701                 a.t -= this.offset + rad;
22702                 a.w -= rad;
22703                 a.h -= rad;
22704                 a.t += 1;
22705             }
22706         break;
22707         case "sides":
22708             a.w = (o*2);
22709             a.l = -o;
22710             a.t = o-1;
22711             if(Roo.isIE){
22712                 a.l -= (this.offset - rad);
22713                 a.t -= this.offset + rad;
22714                 a.l += 1;
22715                 a.w -= (this.offset - rad)*2;
22716                 a.w -= rad + 1;
22717                 a.h -= 1;
22718             }
22719         break;
22720         case "frame":
22721             a.w = a.h = (o*2);
22722             a.l = a.t = -o;
22723             a.t += 1;
22724             a.h -= 2;
22725             if(Roo.isIE){
22726                 a.l -= (this.offset - rad);
22727                 a.t -= (this.offset - rad);
22728                 a.l += 1;
22729                 a.w -= (this.offset + rad + 1);
22730                 a.h -= (this.offset + rad);
22731                 a.h += 1;
22732             }
22733         break;
22734     };
22735
22736     this.adjusts = a;
22737 };
22738
22739 Roo.Shadow.prototype = {
22740     /**
22741      * @cfg {String} mode
22742      * The shadow display mode.  Supports the following options:<br />
22743      * sides: Shadow displays on both sides and bottom only<br />
22744      * frame: Shadow displays equally on all four sides<br />
22745      * drop: Traditional bottom-right drop shadow (default)
22746      */
22747     /**
22748      * @cfg {String} offset
22749      * The number of pixels to offset the shadow from the element (defaults to 4)
22750      */
22751     offset: 4,
22752
22753     // private
22754     defaultMode: "drop",
22755
22756     /**
22757      * Displays the shadow under the target element
22758      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22759      */
22760     show : function(target){
22761         target = Roo.get(target);
22762         if(!this.el){
22763             this.el = Roo.Shadow.Pool.pull();
22764             if(this.el.dom.nextSibling != target.dom){
22765                 this.el.insertBefore(target);
22766             }
22767         }
22768         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22769         if(Roo.isIE){
22770             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22771         }
22772         this.realign(
22773             target.getLeft(true),
22774             target.getTop(true),
22775             target.getWidth(),
22776             target.getHeight()
22777         );
22778         this.el.dom.style.display = "block";
22779     },
22780
22781     /**
22782      * Returns true if the shadow is visible, else false
22783      */
22784     isVisible : function(){
22785         return this.el ? true : false;  
22786     },
22787
22788     /**
22789      * Direct alignment when values are already available. Show must be called at least once before
22790      * calling this method to ensure it is initialized.
22791      * @param {Number} left The target element left position
22792      * @param {Number} top The target element top position
22793      * @param {Number} width The target element width
22794      * @param {Number} height The target element height
22795      */
22796     realign : function(l, t, w, h){
22797         if(!this.el){
22798             return;
22799         }
22800         var a = this.adjusts, d = this.el.dom, s = d.style;
22801         var iea = 0;
22802         s.left = (l+a.l)+"px";
22803         s.top = (t+a.t)+"px";
22804         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22805  
22806         if(s.width != sws || s.height != shs){
22807             s.width = sws;
22808             s.height = shs;
22809             if(!Roo.isIE){
22810                 var cn = d.childNodes;
22811                 var sww = Math.max(0, (sw-12))+"px";
22812                 cn[0].childNodes[1].style.width = sww;
22813                 cn[1].childNodes[1].style.width = sww;
22814                 cn[2].childNodes[1].style.width = sww;
22815                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22816             }
22817         }
22818     },
22819
22820     /**
22821      * Hides this shadow
22822      */
22823     hide : function(){
22824         if(this.el){
22825             this.el.dom.style.display = "none";
22826             Roo.Shadow.Pool.push(this.el);
22827             delete this.el;
22828         }
22829     },
22830
22831     /**
22832      * Adjust the z-index of this shadow
22833      * @param {Number} zindex The new z-index
22834      */
22835     setZIndex : function(z){
22836         this.zIndex = z;
22837         if(this.el){
22838             this.el.setStyle("z-index", z);
22839         }
22840     }
22841 };
22842
22843 // Private utility class that manages the internal Shadow cache
22844 Roo.Shadow.Pool = function(){
22845     var p = [];
22846     var markup = Roo.isIE ?
22847                  '<div class="x-ie-shadow"></div>' :
22848                  '<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>';
22849     return {
22850         pull : function(){
22851             var sh = p.shift();
22852             if(!sh){
22853                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22854                 sh.autoBoxAdjust = false;
22855             }
22856             return sh;
22857         },
22858
22859         push : function(sh){
22860             p.push(sh);
22861         }
22862     };
22863 }();/*
22864  * Based on:
22865  * Ext JS Library 1.1.1
22866  * Copyright(c) 2006-2007, Ext JS, LLC.
22867  *
22868  * Originally Released Under LGPL - original licence link has changed is not relivant.
22869  *
22870  * Fork - LGPL
22871  * <script type="text/javascript">
22872  */
22873
22874 /**
22875  * @class Roo.BoxComponent
22876  * @extends Roo.Component
22877  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22878  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22879  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22880  * layout containers.
22881  * @constructor
22882  * @param {Roo.Element/String/Object} config The configuration options.
22883  */
22884 Roo.BoxComponent = function(config){
22885     Roo.Component.call(this, config);
22886     this.addEvents({
22887         /**
22888          * @event resize
22889          * Fires after the component is resized.
22890              * @param {Roo.Component} this
22891              * @param {Number} adjWidth The box-adjusted width that was set
22892              * @param {Number} adjHeight The box-adjusted height that was set
22893              * @param {Number} rawWidth The width that was originally specified
22894              * @param {Number} rawHeight The height that was originally specified
22895              */
22896         resize : true,
22897         /**
22898          * @event move
22899          * Fires after the component is moved.
22900              * @param {Roo.Component} this
22901              * @param {Number} x The new x position
22902              * @param {Number} y The new y position
22903              */
22904         move : true
22905     });
22906 };
22907
22908 Roo.extend(Roo.BoxComponent, Roo.Component, {
22909     // private, set in afterRender to signify that the component has been rendered
22910     boxReady : false,
22911     // private, used to defer height settings to subclasses
22912     deferHeight: false,
22913     /** @cfg {Number} width
22914      * width (optional) size of component
22915      */
22916      /** @cfg {Number} height
22917      * height (optional) size of component
22918      */
22919      
22920     /**
22921      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22922      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22923      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22924      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22925      * @return {Roo.BoxComponent} this
22926      */
22927     setSize : function(w, h){
22928         // support for standard size objects
22929         if(typeof w == 'object'){
22930             h = w.height;
22931             w = w.width;
22932         }
22933         // not rendered
22934         if(!this.boxReady){
22935             this.width = w;
22936             this.height = h;
22937             return this;
22938         }
22939
22940         // prevent recalcs when not needed
22941         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22942             return this;
22943         }
22944         this.lastSize = {width: w, height: h};
22945
22946         var adj = this.adjustSize(w, h);
22947         var aw = adj.width, ah = adj.height;
22948         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22949             var rz = this.getResizeEl();
22950             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22951                 rz.setSize(aw, ah);
22952             }else if(!this.deferHeight && ah !== undefined){
22953                 rz.setHeight(ah);
22954             }else if(aw !== undefined){
22955                 rz.setWidth(aw);
22956             }
22957             this.onResize(aw, ah, w, h);
22958             this.fireEvent('resize', this, aw, ah, w, h);
22959         }
22960         return this;
22961     },
22962
22963     /**
22964      * Gets the current size of the component's underlying element.
22965      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22966      */
22967     getSize : function(){
22968         return this.el.getSize();
22969     },
22970
22971     /**
22972      * Gets the current XY position of the component's underlying element.
22973      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22974      * @return {Array} The XY position of the element (e.g., [100, 200])
22975      */
22976     getPosition : function(local){
22977         if(local === true){
22978             return [this.el.getLeft(true), this.el.getTop(true)];
22979         }
22980         return this.xy || this.el.getXY();
22981     },
22982
22983     /**
22984      * Gets the current box measurements of the component's underlying element.
22985      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22986      * @returns {Object} box An object in the format {x, y, width, height}
22987      */
22988     getBox : function(local){
22989         var s = this.el.getSize();
22990         if(local){
22991             s.x = this.el.getLeft(true);
22992             s.y = this.el.getTop(true);
22993         }else{
22994             var xy = this.xy || this.el.getXY();
22995             s.x = xy[0];
22996             s.y = xy[1];
22997         }
22998         return s;
22999     },
23000
23001     /**
23002      * Sets the current box measurements of the component's underlying element.
23003      * @param {Object} box An object in the format {x, y, width, height}
23004      * @returns {Roo.BoxComponent} this
23005      */
23006     updateBox : function(box){
23007         this.setSize(box.width, box.height);
23008         this.setPagePosition(box.x, box.y);
23009         return this;
23010     },
23011
23012     // protected
23013     getResizeEl : function(){
23014         return this.resizeEl || this.el;
23015     },
23016
23017     // protected
23018     getPositionEl : function(){
23019         return this.positionEl || this.el;
23020     },
23021
23022     /**
23023      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23024      * This method fires the move event.
23025      * @param {Number} left The new left
23026      * @param {Number} top The new top
23027      * @returns {Roo.BoxComponent} this
23028      */
23029     setPosition : function(x, y){
23030         this.x = x;
23031         this.y = y;
23032         if(!this.boxReady){
23033             return this;
23034         }
23035         var adj = this.adjustPosition(x, y);
23036         var ax = adj.x, ay = adj.y;
23037
23038         var el = this.getPositionEl();
23039         if(ax !== undefined || ay !== undefined){
23040             if(ax !== undefined && ay !== undefined){
23041                 el.setLeftTop(ax, ay);
23042             }else if(ax !== undefined){
23043                 el.setLeft(ax);
23044             }else if(ay !== undefined){
23045                 el.setTop(ay);
23046             }
23047             this.onPosition(ax, ay);
23048             this.fireEvent('move', this, ax, ay);
23049         }
23050         return this;
23051     },
23052
23053     /**
23054      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23055      * This method fires the move event.
23056      * @param {Number} x The new x position
23057      * @param {Number} y The new y position
23058      * @returns {Roo.BoxComponent} this
23059      */
23060     setPagePosition : function(x, y){
23061         this.pageX = x;
23062         this.pageY = y;
23063         if(!this.boxReady){
23064             return;
23065         }
23066         if(x === undefined || y === undefined){ // cannot translate undefined points
23067             return;
23068         }
23069         var p = this.el.translatePoints(x, y);
23070         this.setPosition(p.left, p.top);
23071         return this;
23072     },
23073
23074     // private
23075     onRender : function(ct, position){
23076         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23077         if(this.resizeEl){
23078             this.resizeEl = Roo.get(this.resizeEl);
23079         }
23080         if(this.positionEl){
23081             this.positionEl = Roo.get(this.positionEl);
23082         }
23083     },
23084
23085     // private
23086     afterRender : function(){
23087         Roo.BoxComponent.superclass.afterRender.call(this);
23088         this.boxReady = true;
23089         this.setSize(this.width, this.height);
23090         if(this.x || this.y){
23091             this.setPosition(this.x, this.y);
23092         }
23093         if(this.pageX || this.pageY){
23094             this.setPagePosition(this.pageX, this.pageY);
23095         }
23096     },
23097
23098     /**
23099      * Force the component's size to recalculate based on the underlying element's current height and width.
23100      * @returns {Roo.BoxComponent} this
23101      */
23102     syncSize : function(){
23103         delete this.lastSize;
23104         this.setSize(this.el.getWidth(), this.el.getHeight());
23105         return this;
23106     },
23107
23108     /**
23109      * Called after the component is resized, this method is empty by default but can be implemented by any
23110      * subclass that needs to perform custom logic after a resize occurs.
23111      * @param {Number} adjWidth The box-adjusted width that was set
23112      * @param {Number} adjHeight The box-adjusted height that was set
23113      * @param {Number} rawWidth The width that was originally specified
23114      * @param {Number} rawHeight The height that was originally specified
23115      */
23116     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23117
23118     },
23119
23120     /**
23121      * Called after the component is moved, this method is empty by default but can be implemented by any
23122      * subclass that needs to perform custom logic after a move occurs.
23123      * @param {Number} x The new x position
23124      * @param {Number} y The new y position
23125      */
23126     onPosition : function(x, y){
23127
23128     },
23129
23130     // private
23131     adjustSize : function(w, h){
23132         if(this.autoWidth){
23133             w = 'auto';
23134         }
23135         if(this.autoHeight){
23136             h = 'auto';
23137         }
23138         return {width : w, height: h};
23139     },
23140
23141     // private
23142     adjustPosition : function(x, y){
23143         return {x : x, y: y};
23144     }
23145 });/*
23146  * Based on:
23147  * Ext JS Library 1.1.1
23148  * Copyright(c) 2006-2007, Ext JS, LLC.
23149  *
23150  * Originally Released Under LGPL - original licence link has changed is not relivant.
23151  *
23152  * Fork - LGPL
23153  * <script type="text/javascript">
23154  */
23155
23156
23157 /**
23158  * @class Roo.SplitBar
23159  * @extends Roo.util.Observable
23160  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23161  * <br><br>
23162  * Usage:
23163  * <pre><code>
23164 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23165                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23166 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23167 split.minSize = 100;
23168 split.maxSize = 600;
23169 split.animate = true;
23170 split.on('moved', splitterMoved);
23171 </code></pre>
23172  * @constructor
23173  * Create a new SplitBar
23174  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23175  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23176  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23177  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23178                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23179                         position of the SplitBar).
23180  */
23181 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23182     
23183     /** @private */
23184     this.el = Roo.get(dragElement, true);
23185     this.el.dom.unselectable = "on";
23186     /** @private */
23187     this.resizingEl = Roo.get(resizingElement, true);
23188
23189     /**
23190      * @private
23191      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23192      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23193      * @type Number
23194      */
23195     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23196     
23197     /**
23198      * The minimum size of the resizing element. (Defaults to 0)
23199      * @type Number
23200      */
23201     this.minSize = 0;
23202     
23203     /**
23204      * The maximum size of the resizing element. (Defaults to 2000)
23205      * @type Number
23206      */
23207     this.maxSize = 2000;
23208     
23209     /**
23210      * Whether to animate the transition to the new size
23211      * @type Boolean
23212      */
23213     this.animate = false;
23214     
23215     /**
23216      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23217      * @type Boolean
23218      */
23219     this.useShim = false;
23220     
23221     /** @private */
23222     this.shim = null;
23223     
23224     if(!existingProxy){
23225         /** @private */
23226         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23227     }else{
23228         this.proxy = Roo.get(existingProxy).dom;
23229     }
23230     /** @private */
23231     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23232     
23233     /** @private */
23234     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23235     
23236     /** @private */
23237     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23238     
23239     /** @private */
23240     this.dragSpecs = {};
23241     
23242     /**
23243      * @private The adapter to use to positon and resize elements
23244      */
23245     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23246     this.adapter.init(this);
23247     
23248     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23249         /** @private */
23250         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23251         this.el.addClass("x-splitbar-h");
23252     }else{
23253         /** @private */
23254         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23255         this.el.addClass("x-splitbar-v");
23256     }
23257     
23258     this.addEvents({
23259         /**
23260          * @event resize
23261          * Fires when the splitter is moved (alias for {@link #event-moved})
23262          * @param {Roo.SplitBar} this
23263          * @param {Number} newSize the new width or height
23264          */
23265         "resize" : true,
23266         /**
23267          * @event moved
23268          * Fires when the splitter is moved
23269          * @param {Roo.SplitBar} this
23270          * @param {Number} newSize the new width or height
23271          */
23272         "moved" : true,
23273         /**
23274          * @event beforeresize
23275          * Fires before the splitter is dragged
23276          * @param {Roo.SplitBar} this
23277          */
23278         "beforeresize" : true,
23279
23280         "beforeapply" : true
23281     });
23282
23283     Roo.util.Observable.call(this);
23284 };
23285
23286 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23287     onStartProxyDrag : function(x, y){
23288         this.fireEvent("beforeresize", this);
23289         if(!this.overlay){
23290             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23291             o.unselectable();
23292             o.enableDisplayMode("block");
23293             // all splitbars share the same overlay
23294             Roo.SplitBar.prototype.overlay = o;
23295         }
23296         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23297         this.overlay.show();
23298         Roo.get(this.proxy).setDisplayed("block");
23299         var size = this.adapter.getElementSize(this);
23300         this.activeMinSize = this.getMinimumSize();;
23301         this.activeMaxSize = this.getMaximumSize();;
23302         var c1 = size - this.activeMinSize;
23303         var c2 = Math.max(this.activeMaxSize - size, 0);
23304         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23305             this.dd.resetConstraints();
23306             this.dd.setXConstraint(
23307                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23308                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23309             );
23310             this.dd.setYConstraint(0, 0);
23311         }else{
23312             this.dd.resetConstraints();
23313             this.dd.setXConstraint(0, 0);
23314             this.dd.setYConstraint(
23315                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23316                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23317             );
23318          }
23319         this.dragSpecs.startSize = size;
23320         this.dragSpecs.startPoint = [x, y];
23321         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23322     },
23323     
23324     /** 
23325      * @private Called after the drag operation by the DDProxy
23326      */
23327     onEndProxyDrag : function(e){
23328         Roo.get(this.proxy).setDisplayed(false);
23329         var endPoint = Roo.lib.Event.getXY(e);
23330         if(this.overlay){
23331             this.overlay.hide();
23332         }
23333         var newSize;
23334         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23335             newSize = this.dragSpecs.startSize + 
23336                 (this.placement == Roo.SplitBar.LEFT ?
23337                     endPoint[0] - this.dragSpecs.startPoint[0] :
23338                     this.dragSpecs.startPoint[0] - endPoint[0]
23339                 );
23340         }else{
23341             newSize = this.dragSpecs.startSize + 
23342                 (this.placement == Roo.SplitBar.TOP ?
23343                     endPoint[1] - this.dragSpecs.startPoint[1] :
23344                     this.dragSpecs.startPoint[1] - endPoint[1]
23345                 );
23346         }
23347         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23348         if(newSize != this.dragSpecs.startSize){
23349             if(this.fireEvent('beforeapply', this, newSize) !== false){
23350                 this.adapter.setElementSize(this, newSize);
23351                 this.fireEvent("moved", this, newSize);
23352                 this.fireEvent("resize", this, newSize);
23353             }
23354         }
23355     },
23356     
23357     /**
23358      * Get the adapter this SplitBar uses
23359      * @return The adapter object
23360      */
23361     getAdapter : function(){
23362         return this.adapter;
23363     },
23364     
23365     /**
23366      * Set the adapter this SplitBar uses
23367      * @param {Object} adapter A SplitBar adapter object
23368      */
23369     setAdapter : function(adapter){
23370         this.adapter = adapter;
23371         this.adapter.init(this);
23372     },
23373     
23374     /**
23375      * Gets the minimum size for the resizing element
23376      * @return {Number} The minimum size
23377      */
23378     getMinimumSize : function(){
23379         return this.minSize;
23380     },
23381     
23382     /**
23383      * Sets the minimum size for the resizing element
23384      * @param {Number} minSize The minimum size
23385      */
23386     setMinimumSize : function(minSize){
23387         this.minSize = minSize;
23388     },
23389     
23390     /**
23391      * Gets the maximum size for the resizing element
23392      * @return {Number} The maximum size
23393      */
23394     getMaximumSize : function(){
23395         return this.maxSize;
23396     },
23397     
23398     /**
23399      * Sets the maximum size for the resizing element
23400      * @param {Number} maxSize The maximum size
23401      */
23402     setMaximumSize : function(maxSize){
23403         this.maxSize = maxSize;
23404     },
23405     
23406     /**
23407      * Sets the initialize size for the resizing element
23408      * @param {Number} size The initial size
23409      */
23410     setCurrentSize : function(size){
23411         var oldAnimate = this.animate;
23412         this.animate = false;
23413         this.adapter.setElementSize(this, size);
23414         this.animate = oldAnimate;
23415     },
23416     
23417     /**
23418      * Destroy this splitbar. 
23419      * @param {Boolean} removeEl True to remove the element
23420      */
23421     destroy : function(removeEl){
23422         if(this.shim){
23423             this.shim.remove();
23424         }
23425         this.dd.unreg();
23426         this.proxy.parentNode.removeChild(this.proxy);
23427         if(removeEl){
23428             this.el.remove();
23429         }
23430     }
23431 });
23432
23433 /**
23434  * @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.
23435  */
23436 Roo.SplitBar.createProxy = function(dir){
23437     var proxy = new Roo.Element(document.createElement("div"));
23438     proxy.unselectable();
23439     var cls = 'x-splitbar-proxy';
23440     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23441     document.body.appendChild(proxy.dom);
23442     return proxy.dom;
23443 };
23444
23445 /** 
23446  * @class Roo.SplitBar.BasicLayoutAdapter
23447  * Default Adapter. It assumes the splitter and resizing element are not positioned
23448  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23449  */
23450 Roo.SplitBar.BasicLayoutAdapter = function(){
23451 };
23452
23453 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23454     // do nothing for now
23455     init : function(s){
23456     
23457     },
23458     /**
23459      * Called before drag operations to get the current size of the resizing element. 
23460      * @param {Roo.SplitBar} s The SplitBar using this adapter
23461      */
23462      getElementSize : function(s){
23463         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23464             return s.resizingEl.getWidth();
23465         }else{
23466             return s.resizingEl.getHeight();
23467         }
23468     },
23469     
23470     /**
23471      * Called after drag operations to set the size of the resizing element.
23472      * @param {Roo.SplitBar} s The SplitBar using this adapter
23473      * @param {Number} newSize The new size to set
23474      * @param {Function} onComplete A function to be invoked when resizing is complete
23475      */
23476     setElementSize : function(s, newSize, onComplete){
23477         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23478             if(!s.animate){
23479                 s.resizingEl.setWidth(newSize);
23480                 if(onComplete){
23481                     onComplete(s, newSize);
23482                 }
23483             }else{
23484                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23485             }
23486         }else{
23487             
23488             if(!s.animate){
23489                 s.resizingEl.setHeight(newSize);
23490                 if(onComplete){
23491                     onComplete(s, newSize);
23492                 }
23493             }else{
23494                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23495             }
23496         }
23497     }
23498 };
23499
23500 /** 
23501  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23502  * @extends Roo.SplitBar.BasicLayoutAdapter
23503  * Adapter that  moves the splitter element to align with the resized sizing element. 
23504  * Used with an absolute positioned SplitBar.
23505  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23506  * document.body, make sure you assign an id to the body element.
23507  */
23508 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23509     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23510     this.container = Roo.get(container);
23511 };
23512
23513 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23514     init : function(s){
23515         this.basic.init(s);
23516     },
23517     
23518     getElementSize : function(s){
23519         return this.basic.getElementSize(s);
23520     },
23521     
23522     setElementSize : function(s, newSize, onComplete){
23523         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23524     },
23525     
23526     moveSplitter : function(s){
23527         var yes = Roo.SplitBar;
23528         switch(s.placement){
23529             case yes.LEFT:
23530                 s.el.setX(s.resizingEl.getRight());
23531                 break;
23532             case yes.RIGHT:
23533                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23534                 break;
23535             case yes.TOP:
23536                 s.el.setY(s.resizingEl.getBottom());
23537                 break;
23538             case yes.BOTTOM:
23539                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23540                 break;
23541         }
23542     }
23543 };
23544
23545 /**
23546  * Orientation constant - Create a vertical SplitBar
23547  * @static
23548  * @type Number
23549  */
23550 Roo.SplitBar.VERTICAL = 1;
23551
23552 /**
23553  * Orientation constant - Create a horizontal SplitBar
23554  * @static
23555  * @type Number
23556  */
23557 Roo.SplitBar.HORIZONTAL = 2;
23558
23559 /**
23560  * Placement constant - The resizing element is to the left of the splitter element
23561  * @static
23562  * @type Number
23563  */
23564 Roo.SplitBar.LEFT = 1;
23565
23566 /**
23567  * Placement constant - The resizing element is to the right of the splitter element
23568  * @static
23569  * @type Number
23570  */
23571 Roo.SplitBar.RIGHT = 2;
23572
23573 /**
23574  * Placement constant - The resizing element is positioned above the splitter element
23575  * @static
23576  * @type Number
23577  */
23578 Roo.SplitBar.TOP = 3;
23579
23580 /**
23581  * Placement constant - The resizing element is positioned under splitter element
23582  * @static
23583  * @type Number
23584  */
23585 Roo.SplitBar.BOTTOM = 4;
23586 /*
23587  * Based on:
23588  * Ext JS Library 1.1.1
23589  * Copyright(c) 2006-2007, Ext JS, LLC.
23590  *
23591  * Originally Released Under LGPL - original licence link has changed is not relivant.
23592  *
23593  * Fork - LGPL
23594  * <script type="text/javascript">
23595  */
23596
23597 /**
23598  * @class Roo.View
23599  * @extends Roo.util.Observable
23600  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23601  * This class also supports single and multi selection modes. <br>
23602  * Create a data model bound view:
23603  <pre><code>
23604  var store = new Roo.data.Store(...);
23605
23606  var view = new Roo.View({
23607     el : "my-element",
23608     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23609  
23610     singleSelect: true,
23611     selectedClass: "ydataview-selected",
23612     store: store
23613  });
23614
23615  // listen for node click?
23616  view.on("click", function(vw, index, node, e){
23617  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23618  });
23619
23620  // load XML data
23621  dataModel.load("foobar.xml");
23622  </code></pre>
23623  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23624  * <br><br>
23625  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23626  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23627  * 
23628  * Note: old style constructor is still suported (container, template, config)
23629  * 
23630  * @constructor
23631  * Create a new View
23632  * @param {Object} config The config object
23633  * 
23634  */
23635 Roo.View = function(config, depreciated_tpl, depreciated_config){
23636     
23637     if (typeof(depreciated_tpl) == 'undefined') {
23638         // new way.. - universal constructor.
23639         Roo.apply(this, config);
23640         this.el  = Roo.get(this.el);
23641     } else {
23642         // old format..
23643         this.el  = Roo.get(config);
23644         this.tpl = depreciated_tpl;
23645         Roo.apply(this, depreciated_config);
23646     }
23647      
23648     
23649     if(typeof(this.tpl) == "string"){
23650         this.tpl = new Roo.Template(this.tpl);
23651     } else {
23652         // support xtype ctors..
23653         this.tpl = new Roo.factory(this.tpl, Roo);
23654     }
23655     
23656     
23657     this.tpl.compile();
23658    
23659
23660      
23661     /** @private */
23662     this.addEvents({
23663         /**
23664          * @event beforeclick
23665          * Fires before a click is processed. Returns false to cancel the default action.
23666          * @param {Roo.View} this
23667          * @param {Number} index The index of the target node
23668          * @param {HTMLElement} node The target node
23669          * @param {Roo.EventObject} e The raw event object
23670          */
23671             "beforeclick" : true,
23672         /**
23673          * @event click
23674          * Fires when a template node is clicked.
23675          * @param {Roo.View} this
23676          * @param {Number} index The index of the target node
23677          * @param {HTMLElement} node The target node
23678          * @param {Roo.EventObject} e The raw event object
23679          */
23680             "click" : true,
23681         /**
23682          * @event dblclick
23683          * Fires when a template node is double clicked.
23684          * @param {Roo.View} this
23685          * @param {Number} index The index of the target node
23686          * @param {HTMLElement} node The target node
23687          * @param {Roo.EventObject} e The raw event object
23688          */
23689             "dblclick" : true,
23690         /**
23691          * @event contextmenu
23692          * Fires when a template node is right clicked.
23693          * @param {Roo.View} this
23694          * @param {Number} index The index of the target node
23695          * @param {HTMLElement} node The target node
23696          * @param {Roo.EventObject} e The raw event object
23697          */
23698             "contextmenu" : true,
23699         /**
23700          * @event selectionchange
23701          * Fires when the selected nodes change.
23702          * @param {Roo.View} this
23703          * @param {Array} selections Array of the selected nodes
23704          */
23705             "selectionchange" : true,
23706     
23707         /**
23708          * @event beforeselect
23709          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23710          * @param {Roo.View} this
23711          * @param {HTMLElement} node The node to be selected
23712          * @param {Array} selections Array of currently selected nodes
23713          */
23714             "beforeselect" : true,
23715         /**
23716          * @event preparedata
23717          * Fires on every row to render, to allow you to change the data.
23718          * @param {Roo.View} this
23719          * @param {Object} data to be rendered (change this)
23720          */
23721           "preparedata" : true
23722         });
23723
23724     this.el.on({
23725         "click": this.onClick,
23726         "dblclick": this.onDblClick,
23727         "contextmenu": this.onContextMenu,
23728         scope:this
23729     });
23730
23731     this.selections = [];
23732     this.nodes = [];
23733     this.cmp = new Roo.CompositeElementLite([]);
23734     if(this.store){
23735         this.store = Roo.factory(this.store, Roo.data);
23736         this.setStore(this.store, true);
23737     }
23738     Roo.View.superclass.constructor.call(this);
23739 };
23740
23741 Roo.extend(Roo.View, Roo.util.Observable, {
23742     
23743      /**
23744      * @cfg {Roo.data.Store} store Data store to load data from.
23745      */
23746     store : false,
23747     
23748     /**
23749      * @cfg {String|Roo.Element} el The container element.
23750      */
23751     el : '',
23752     
23753     /**
23754      * @cfg {String|Roo.Template} tpl The template used by this View 
23755      */
23756     tpl : false,
23757     
23758     /**
23759      * @cfg {String} selectedClass The css class to add to selected nodes
23760      */
23761     selectedClass : "x-view-selected",
23762      /**
23763      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23764      */
23765     emptyText : "",
23766     /**
23767      * @cfg {Boolean} multiSelect Allow multiple selection
23768      */
23769     multiSelect : false,
23770     /**
23771      * @cfg {Boolean} singleSelect Allow single selection
23772      */
23773     singleSelect:  false,
23774     
23775     /**
23776      * @cfg {Boolean} toggleSelect - selecting 
23777      */
23778     toggleSelect : false,
23779     
23780     /**
23781      * Returns the element this view is bound to.
23782      * @return {Roo.Element}
23783      */
23784     getEl : function(){
23785         return this.el;
23786     },
23787
23788     /**
23789      * Refreshes the view.
23790      */
23791     refresh : function(){
23792         var t = this.tpl;
23793         this.clearSelections();
23794         this.el.update("");
23795         var html = [];
23796         var records = this.store.getRange();
23797         if(records.length < 1){
23798             this.el.update(this.emptyText);
23799             return;
23800         }
23801         for(var i = 0, len = records.length; i < len; i++){
23802             var data = this.prepareData(records[i].data, i, records[i]);
23803             this.fireEvent("preparedata", this, data, i, records[i]);
23804             html[html.length] = t.apply(data);
23805         }
23806         this.el.update(html.join(""));
23807         this.nodes = this.el.dom.childNodes;
23808         this.updateIndexes(0);
23809     },
23810
23811     /**
23812      * Function to override to reformat the data that is sent to
23813      * the template for each node.
23814      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23815      * a JSON object for an UpdateManager bound view).
23816      */
23817     prepareData : function(data){
23818         return data;
23819     },
23820
23821     onUpdate : function(ds, record){
23822         this.clearSelections();
23823         var index = this.store.indexOf(record);
23824         var n = this.nodes[index];
23825         this.tpl.insertBefore(n, this.prepareData(record.data));
23826         n.parentNode.removeChild(n);
23827         this.updateIndexes(index, index);
23828     },
23829
23830     onAdd : function(ds, records, index){
23831         this.clearSelections();
23832         if(this.nodes.length == 0){
23833             this.refresh();
23834             return;
23835         }
23836         var n = this.nodes[index];
23837         for(var i = 0, len = records.length; i < len; i++){
23838             var d = this.prepareData(records[i].data);
23839             if(n){
23840                 this.tpl.insertBefore(n, d);
23841             }else{
23842                 this.tpl.append(this.el, d);
23843             }
23844         }
23845         this.updateIndexes(index);
23846     },
23847
23848     onRemove : function(ds, record, index){
23849         this.clearSelections();
23850         this.el.dom.removeChild(this.nodes[index]);
23851         this.updateIndexes(index);
23852     },
23853
23854     /**
23855      * Refresh an individual node.
23856      * @param {Number} index
23857      */
23858     refreshNode : function(index){
23859         this.onUpdate(this.store, this.store.getAt(index));
23860     },
23861
23862     updateIndexes : function(startIndex, endIndex){
23863         var ns = this.nodes;
23864         startIndex = startIndex || 0;
23865         endIndex = endIndex || ns.length - 1;
23866         for(var i = startIndex; i <= endIndex; i++){
23867             ns[i].nodeIndex = i;
23868         }
23869     },
23870
23871     /**
23872      * Changes the data store this view uses and refresh the view.
23873      * @param {Store} store
23874      */
23875     setStore : function(store, initial){
23876         if(!initial && this.store){
23877             this.store.un("datachanged", this.refresh);
23878             this.store.un("add", this.onAdd);
23879             this.store.un("remove", this.onRemove);
23880             this.store.un("update", this.onUpdate);
23881             this.store.un("clear", this.refresh);
23882         }
23883         if(store){
23884           
23885             store.on("datachanged", this.refresh, this);
23886             store.on("add", this.onAdd, this);
23887             store.on("remove", this.onRemove, this);
23888             store.on("update", this.onUpdate, this);
23889             store.on("clear", this.refresh, this);
23890         }
23891         
23892         if(store){
23893             this.refresh();
23894         }
23895     },
23896
23897     /**
23898      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23899      * @param {HTMLElement} node
23900      * @return {HTMLElement} The template node
23901      */
23902     findItemFromChild : function(node){
23903         var el = this.el.dom;
23904         if(!node || node.parentNode == el){
23905                     return node;
23906             }
23907             var p = node.parentNode;
23908             while(p && p != el){
23909             if(p.parentNode == el){
23910                 return p;
23911             }
23912             p = p.parentNode;
23913         }
23914             return null;
23915     },
23916
23917     /** @ignore */
23918     onClick : function(e){
23919         var item = this.findItemFromChild(e.getTarget());
23920         if(item){
23921             var index = this.indexOf(item);
23922             if(this.onItemClick(item, index, e) !== false){
23923                 this.fireEvent("click", this, index, item, e);
23924             }
23925         }else{
23926             this.clearSelections();
23927         }
23928     },
23929
23930     /** @ignore */
23931     onContextMenu : function(e){
23932         var item = this.findItemFromChild(e.getTarget());
23933         if(item){
23934             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23935         }
23936     },
23937
23938     /** @ignore */
23939     onDblClick : function(e){
23940         var item = this.findItemFromChild(e.getTarget());
23941         if(item){
23942             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23943         }
23944     },
23945
23946     onItemClick : function(item, index, e)
23947     {
23948         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23949             return false;
23950         }
23951         if (this.toggleSelect) {
23952             var m = this.isSelected(item) ? 'unselect' : 'select';
23953             Roo.log(m);
23954             var _t = this;
23955             _t[m](item, true, false);
23956             return true;
23957         }
23958         if(this.multiSelect || this.singleSelect){
23959             if(this.multiSelect && e.shiftKey && this.lastSelection){
23960                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23961             }else{
23962                 this.select(item, this.multiSelect && e.ctrlKey);
23963                 this.lastSelection = item;
23964             }
23965             e.preventDefault();
23966         }
23967         return true;
23968     },
23969
23970     /**
23971      * Get the number of selected nodes.
23972      * @return {Number}
23973      */
23974     getSelectionCount : function(){
23975         return this.selections.length;
23976     },
23977
23978     /**
23979      * Get the currently selected nodes.
23980      * @return {Array} An array of HTMLElements
23981      */
23982     getSelectedNodes : function(){
23983         return this.selections;
23984     },
23985
23986     /**
23987      * Get the indexes of the selected nodes.
23988      * @return {Array}
23989      */
23990     getSelectedIndexes : function(){
23991         var indexes = [], s = this.selections;
23992         for(var i = 0, len = s.length; i < len; i++){
23993             indexes.push(s[i].nodeIndex);
23994         }
23995         return indexes;
23996     },
23997
23998     /**
23999      * Clear all selections
24000      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24001      */
24002     clearSelections : function(suppressEvent){
24003         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24004             this.cmp.elements = this.selections;
24005             this.cmp.removeClass(this.selectedClass);
24006             this.selections = [];
24007             if(!suppressEvent){
24008                 this.fireEvent("selectionchange", this, this.selections);
24009             }
24010         }
24011     },
24012
24013     /**
24014      * Returns true if the passed node is selected
24015      * @param {HTMLElement/Number} node The node or node index
24016      * @return {Boolean}
24017      */
24018     isSelected : function(node){
24019         var s = this.selections;
24020         if(s.length < 1){
24021             return false;
24022         }
24023         node = this.getNode(node);
24024         return s.indexOf(node) !== -1;
24025     },
24026
24027     /**
24028      * Selects nodes.
24029      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24030      * @param {Boolean} keepExisting (optional) true to keep existing selections
24031      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24032      */
24033     select : function(nodeInfo, keepExisting, suppressEvent){
24034         if(nodeInfo instanceof Array){
24035             if(!keepExisting){
24036                 this.clearSelections(true);
24037             }
24038             for(var i = 0, len = nodeInfo.length; i < len; i++){
24039                 this.select(nodeInfo[i], true, true);
24040             }
24041             return;
24042         } 
24043         var node = this.getNode(nodeInfo);
24044         if(!node || this.isSelected(node)){
24045             return; // already selected.
24046         }
24047         if(!keepExisting){
24048             this.clearSelections(true);
24049         }
24050         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24051             Roo.fly(node).addClass(this.selectedClass);
24052             this.selections.push(node);
24053             if(!suppressEvent){
24054                 this.fireEvent("selectionchange", this, this.selections);
24055             }
24056         }
24057         
24058         
24059     },
24060       /**
24061      * Unselects nodes.
24062      * @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
24063      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24064      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24065      */
24066     unselect : function(nodeInfo, keepExisting, suppressEvent)
24067     {
24068         if(nodeInfo instanceof Array){
24069             Roo.each(this.selections, function(s) {
24070                 this.unselect(s, nodeInfo);
24071             }, this);
24072             return;
24073         }
24074         var node = this.getNode(nodeInfo);
24075         if(!node || !this.isSelected(node)){
24076             Roo.log("not selected");
24077             return; // not selected.
24078         }
24079         // fireevent???
24080         var ns = [];
24081         Roo.each(this.selections, function(s) {
24082             if (s == node ) {
24083                 Roo.fly(node).removeClass(this.selectedClass);
24084
24085                 return;
24086             }
24087             ns.push(s);
24088         },this);
24089         
24090         this.selections= ns;
24091         this.fireEvent("selectionchange", this, this.selections);
24092     },
24093
24094     /**
24095      * Gets a template node.
24096      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24097      * @return {HTMLElement} The node or null if it wasn't found
24098      */
24099     getNode : function(nodeInfo){
24100         if(typeof nodeInfo == "string"){
24101             return document.getElementById(nodeInfo);
24102         }else if(typeof nodeInfo == "number"){
24103             return this.nodes[nodeInfo];
24104         }
24105         return nodeInfo;
24106     },
24107
24108     /**
24109      * Gets a range template nodes.
24110      * @param {Number} startIndex
24111      * @param {Number} endIndex
24112      * @return {Array} An array of nodes
24113      */
24114     getNodes : function(start, end){
24115         var ns = this.nodes;
24116         start = start || 0;
24117         end = typeof end == "undefined" ? ns.length - 1 : end;
24118         var nodes = [];
24119         if(start <= end){
24120             for(var i = start; i <= end; i++){
24121                 nodes.push(ns[i]);
24122             }
24123         } else{
24124             for(var i = start; i >= end; i--){
24125                 nodes.push(ns[i]);
24126             }
24127         }
24128         return nodes;
24129     },
24130
24131     /**
24132      * Finds the index of the passed node
24133      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24134      * @return {Number} The index of the node or -1
24135      */
24136     indexOf : function(node){
24137         node = this.getNode(node);
24138         if(typeof node.nodeIndex == "number"){
24139             return node.nodeIndex;
24140         }
24141         var ns = this.nodes;
24142         for(var i = 0, len = ns.length; i < len; i++){
24143             if(ns[i] == node){
24144                 return i;
24145             }
24146         }
24147         return -1;
24148     }
24149 });
24150 /*
24151  * Based on:
24152  * Ext JS Library 1.1.1
24153  * Copyright(c) 2006-2007, Ext JS, LLC.
24154  *
24155  * Originally Released Under LGPL - original licence link has changed is not relivant.
24156  *
24157  * Fork - LGPL
24158  * <script type="text/javascript">
24159  */
24160
24161 /**
24162  * @class Roo.JsonView
24163  * @extends Roo.View
24164  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24165 <pre><code>
24166 var view = new Roo.JsonView({
24167     container: "my-element",
24168     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24169     multiSelect: true, 
24170     jsonRoot: "data" 
24171 });
24172
24173 // listen for node click?
24174 view.on("click", function(vw, index, node, e){
24175     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24176 });
24177
24178 // direct load of JSON data
24179 view.load("foobar.php");
24180
24181 // Example from my blog list
24182 var tpl = new Roo.Template(
24183     '&lt;div class="entry"&gt;' +
24184     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24185     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24186     "&lt;/div&gt;&lt;hr /&gt;"
24187 );
24188
24189 var moreView = new Roo.JsonView({
24190     container :  "entry-list", 
24191     template : tpl,
24192     jsonRoot: "posts"
24193 });
24194 moreView.on("beforerender", this.sortEntries, this);
24195 moreView.load({
24196     url: "/blog/get-posts.php",
24197     params: "allposts=true",
24198     text: "Loading Blog Entries..."
24199 });
24200 </code></pre>
24201
24202 * Note: old code is supported with arguments : (container, template, config)
24203
24204
24205  * @constructor
24206  * Create a new JsonView
24207  * 
24208  * @param {Object} config The config object
24209  * 
24210  */
24211 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24212     
24213     
24214     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24215
24216     var um = this.el.getUpdateManager();
24217     um.setRenderer(this);
24218     um.on("update", this.onLoad, this);
24219     um.on("failure", this.onLoadException, this);
24220
24221     /**
24222      * @event beforerender
24223      * Fires before rendering of the downloaded JSON data.
24224      * @param {Roo.JsonView} this
24225      * @param {Object} data The JSON data loaded
24226      */
24227     /**
24228      * @event load
24229      * Fires when data is loaded.
24230      * @param {Roo.JsonView} this
24231      * @param {Object} data The JSON data loaded
24232      * @param {Object} response The raw Connect response object
24233      */
24234     /**
24235      * @event loadexception
24236      * Fires when loading fails.
24237      * @param {Roo.JsonView} this
24238      * @param {Object} response The raw Connect response object
24239      */
24240     this.addEvents({
24241         'beforerender' : true,
24242         'load' : true,
24243         'loadexception' : true
24244     });
24245 };
24246 Roo.extend(Roo.JsonView, Roo.View, {
24247     /**
24248      * @type {String} The root property in the loaded JSON object that contains the data
24249      */
24250     jsonRoot : "",
24251
24252     /**
24253      * Refreshes the view.
24254      */
24255     refresh : function(){
24256         this.clearSelections();
24257         this.el.update("");
24258         var html = [];
24259         var o = this.jsonData;
24260         if(o && o.length > 0){
24261             for(var i = 0, len = o.length; i < len; i++){
24262                 var data = this.prepareData(o[i], i, o);
24263                 html[html.length] = this.tpl.apply(data);
24264             }
24265         }else{
24266             html.push(this.emptyText);
24267         }
24268         this.el.update(html.join(""));
24269         this.nodes = this.el.dom.childNodes;
24270         this.updateIndexes(0);
24271     },
24272
24273     /**
24274      * 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.
24275      * @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:
24276      <pre><code>
24277      view.load({
24278          url: "your-url.php",
24279          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24280          callback: yourFunction,
24281          scope: yourObject, //(optional scope)
24282          discardUrl: false,
24283          nocache: false,
24284          text: "Loading...",
24285          timeout: 30,
24286          scripts: false
24287      });
24288      </code></pre>
24289      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24290      * 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.
24291      * @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}
24292      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24293      * @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.
24294      */
24295     load : function(){
24296         var um = this.el.getUpdateManager();
24297         um.update.apply(um, arguments);
24298     },
24299
24300     render : function(el, response){
24301         this.clearSelections();
24302         this.el.update("");
24303         var o;
24304         try{
24305             o = Roo.util.JSON.decode(response.responseText);
24306             if(this.jsonRoot){
24307                 
24308                 o = o[this.jsonRoot];
24309             }
24310         } catch(e){
24311         }
24312         /**
24313          * The current JSON data or null
24314          */
24315         this.jsonData = o;
24316         this.beforeRender();
24317         this.refresh();
24318     },
24319
24320 /**
24321  * Get the number of records in the current JSON dataset
24322  * @return {Number}
24323  */
24324     getCount : function(){
24325         return this.jsonData ? this.jsonData.length : 0;
24326     },
24327
24328 /**
24329  * Returns the JSON object for the specified node(s)
24330  * @param {HTMLElement/Array} node The node or an array of nodes
24331  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24332  * you get the JSON object for the node
24333  */
24334     getNodeData : function(node){
24335         if(node instanceof Array){
24336             var data = [];
24337             for(var i = 0, len = node.length; i < len; i++){
24338                 data.push(this.getNodeData(node[i]));
24339             }
24340             return data;
24341         }
24342         return this.jsonData[this.indexOf(node)] || null;
24343     },
24344
24345     beforeRender : function(){
24346         this.snapshot = this.jsonData;
24347         if(this.sortInfo){
24348             this.sort.apply(this, this.sortInfo);
24349         }
24350         this.fireEvent("beforerender", this, this.jsonData);
24351     },
24352
24353     onLoad : function(el, o){
24354         this.fireEvent("load", this, this.jsonData, o);
24355     },
24356
24357     onLoadException : function(el, o){
24358         this.fireEvent("loadexception", this, o);
24359     },
24360
24361 /**
24362  * Filter the data by a specific property.
24363  * @param {String} property A property on your JSON objects
24364  * @param {String/RegExp} value Either string that the property values
24365  * should start with, or a RegExp to test against the property
24366  */
24367     filter : function(property, value){
24368         if(this.jsonData){
24369             var data = [];
24370             var ss = this.snapshot;
24371             if(typeof value == "string"){
24372                 var vlen = value.length;
24373                 if(vlen == 0){
24374                     this.clearFilter();
24375                     return;
24376                 }
24377                 value = value.toLowerCase();
24378                 for(var i = 0, len = ss.length; i < len; i++){
24379                     var o = ss[i];
24380                     if(o[property].substr(0, vlen).toLowerCase() == value){
24381                         data.push(o);
24382                     }
24383                 }
24384             } else if(value.exec){ // regex?
24385                 for(var i = 0, len = ss.length; i < len; i++){
24386                     var o = ss[i];
24387                     if(value.test(o[property])){
24388                         data.push(o);
24389                     }
24390                 }
24391             } else{
24392                 return;
24393             }
24394             this.jsonData = data;
24395             this.refresh();
24396         }
24397     },
24398
24399 /**
24400  * Filter by a function. The passed function will be called with each
24401  * object in the current dataset. If the function returns true the value is kept,
24402  * otherwise it is filtered.
24403  * @param {Function} fn
24404  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24405  */
24406     filterBy : function(fn, scope){
24407         if(this.jsonData){
24408             var data = [];
24409             var ss = this.snapshot;
24410             for(var i = 0, len = ss.length; i < len; i++){
24411                 var o = ss[i];
24412                 if(fn.call(scope || this, o)){
24413                     data.push(o);
24414                 }
24415             }
24416             this.jsonData = data;
24417             this.refresh();
24418         }
24419     },
24420
24421 /**
24422  * Clears the current filter.
24423  */
24424     clearFilter : function(){
24425         if(this.snapshot && this.jsonData != this.snapshot){
24426             this.jsonData = this.snapshot;
24427             this.refresh();
24428         }
24429     },
24430
24431
24432 /**
24433  * Sorts the data for this view and refreshes it.
24434  * @param {String} property A property on your JSON objects to sort on
24435  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24436  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24437  */
24438     sort : function(property, dir, sortType){
24439         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24440         if(this.jsonData){
24441             var p = property;
24442             var dsc = dir && dir.toLowerCase() == "desc";
24443             var f = function(o1, o2){
24444                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24445                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24446                 ;
24447                 if(v1 < v2){
24448                     return dsc ? +1 : -1;
24449                 } else if(v1 > v2){
24450                     return dsc ? -1 : +1;
24451                 } else{
24452                     return 0;
24453                 }
24454             };
24455             this.jsonData.sort(f);
24456             this.refresh();
24457             if(this.jsonData != this.snapshot){
24458                 this.snapshot.sort(f);
24459             }
24460         }
24461     }
24462 });/*
24463  * Based on:
24464  * Ext JS Library 1.1.1
24465  * Copyright(c) 2006-2007, Ext JS, LLC.
24466  *
24467  * Originally Released Under LGPL - original licence link has changed is not relivant.
24468  *
24469  * Fork - LGPL
24470  * <script type="text/javascript">
24471  */
24472  
24473
24474 /**
24475  * @class Roo.ColorPalette
24476  * @extends Roo.Component
24477  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24478  * Here's an example of typical usage:
24479  * <pre><code>
24480 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24481 cp.render('my-div');
24482
24483 cp.on('select', function(palette, selColor){
24484     // do something with selColor
24485 });
24486 </code></pre>
24487  * @constructor
24488  * Create a new ColorPalette
24489  * @param {Object} config The config object
24490  */
24491 Roo.ColorPalette = function(config){
24492     Roo.ColorPalette.superclass.constructor.call(this, config);
24493     this.addEvents({
24494         /**
24495              * @event select
24496              * Fires when a color is selected
24497              * @param {ColorPalette} this
24498              * @param {String} color The 6-digit color hex code (without the # symbol)
24499              */
24500         select: true
24501     });
24502
24503     if(this.handler){
24504         this.on("select", this.handler, this.scope, true);
24505     }
24506 };
24507 Roo.extend(Roo.ColorPalette, Roo.Component, {
24508     /**
24509      * @cfg {String} itemCls
24510      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24511      */
24512     itemCls : "x-color-palette",
24513     /**
24514      * @cfg {String} value
24515      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24516      * the hex codes are case-sensitive.
24517      */
24518     value : null,
24519     clickEvent:'click',
24520     // private
24521     ctype: "Roo.ColorPalette",
24522
24523     /**
24524      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24525      */
24526     allowReselect : false,
24527
24528     /**
24529      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24530      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24531      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24532      * of colors with the width setting until the box is symmetrical.</p>
24533      * <p>You can override individual colors if needed:</p>
24534      * <pre><code>
24535 var cp = new Roo.ColorPalette();
24536 cp.colors[0] = "FF0000";  // change the first box to red
24537 </code></pre>
24538
24539 Or you can provide a custom array of your own for complete control:
24540 <pre><code>
24541 var cp = new Roo.ColorPalette();
24542 cp.colors = ["000000", "993300", "333300"];
24543 </code></pre>
24544      * @type Array
24545      */
24546     colors : [
24547         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24548         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24549         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24550         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24551         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24552     ],
24553
24554     // private
24555     onRender : function(container, position){
24556         var t = new Roo.MasterTemplate(
24557             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24558         );
24559         var c = this.colors;
24560         for(var i = 0, len = c.length; i < len; i++){
24561             t.add([c[i]]);
24562         }
24563         var el = document.createElement("div");
24564         el.className = this.itemCls;
24565         t.overwrite(el);
24566         container.dom.insertBefore(el, position);
24567         this.el = Roo.get(el);
24568         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24569         if(this.clickEvent != 'click'){
24570             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24571         }
24572     },
24573
24574     // private
24575     afterRender : function(){
24576         Roo.ColorPalette.superclass.afterRender.call(this);
24577         if(this.value){
24578             var s = this.value;
24579             this.value = null;
24580             this.select(s);
24581         }
24582     },
24583
24584     // private
24585     handleClick : function(e, t){
24586         e.preventDefault();
24587         if(!this.disabled){
24588             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24589             this.select(c.toUpperCase());
24590         }
24591     },
24592
24593     /**
24594      * Selects the specified color in the palette (fires the select event)
24595      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24596      */
24597     select : function(color){
24598         color = color.replace("#", "");
24599         if(color != this.value || this.allowReselect){
24600             var el = this.el;
24601             if(this.value){
24602                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24603             }
24604             el.child("a.color-"+color).addClass("x-color-palette-sel");
24605             this.value = color;
24606             this.fireEvent("select", this, color);
24607         }
24608     }
24609 });/*
24610  * Based on:
24611  * Ext JS Library 1.1.1
24612  * Copyright(c) 2006-2007, Ext JS, LLC.
24613  *
24614  * Originally Released Under LGPL - original licence link has changed is not relivant.
24615  *
24616  * Fork - LGPL
24617  * <script type="text/javascript">
24618  */
24619  
24620 /**
24621  * @class Roo.DatePicker
24622  * @extends Roo.Component
24623  * Simple date picker class.
24624  * @constructor
24625  * Create a new DatePicker
24626  * @param {Object} config The config object
24627  */
24628 Roo.DatePicker = function(config){
24629     Roo.DatePicker.superclass.constructor.call(this, config);
24630
24631     this.value = config && config.value ?
24632                  config.value.clearTime() : new Date().clearTime();
24633
24634     this.addEvents({
24635         /**
24636              * @event select
24637              * Fires when a date is selected
24638              * @param {DatePicker} this
24639              * @param {Date} date The selected date
24640              */
24641         'select': true,
24642         /**
24643              * @event monthchange
24644              * Fires when the displayed month changes 
24645              * @param {DatePicker} this
24646              * @param {Date} date The selected month
24647              */
24648         'monthchange': true
24649     });
24650
24651     if(this.handler){
24652         this.on("select", this.handler,  this.scope || this);
24653     }
24654     // build the disabledDatesRE
24655     if(!this.disabledDatesRE && this.disabledDates){
24656         var dd = this.disabledDates;
24657         var re = "(?:";
24658         for(var i = 0; i < dd.length; i++){
24659             re += dd[i];
24660             if(i != dd.length-1) re += "|";
24661         }
24662         this.disabledDatesRE = new RegExp(re + ")");
24663     }
24664 };
24665
24666 Roo.extend(Roo.DatePicker, Roo.Component, {
24667     /**
24668      * @cfg {String} todayText
24669      * The text to display on the button that selects the current date (defaults to "Today")
24670      */
24671     todayText : "Today",
24672     /**
24673      * @cfg {String} okText
24674      * The text to display on the ok button
24675      */
24676     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24677     /**
24678      * @cfg {String} cancelText
24679      * The text to display on the cancel button
24680      */
24681     cancelText : "Cancel",
24682     /**
24683      * @cfg {String} todayTip
24684      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24685      */
24686     todayTip : "{0} (Spacebar)",
24687     /**
24688      * @cfg {Date} minDate
24689      * Minimum allowable date (JavaScript date object, defaults to null)
24690      */
24691     minDate : null,
24692     /**
24693      * @cfg {Date} maxDate
24694      * Maximum allowable date (JavaScript date object, defaults to null)
24695      */
24696     maxDate : null,
24697     /**
24698      * @cfg {String} minText
24699      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24700      */
24701     minText : "This date is before the minimum date",
24702     /**
24703      * @cfg {String} maxText
24704      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24705      */
24706     maxText : "This date is after the maximum date",
24707     /**
24708      * @cfg {String} format
24709      * The default date format string which can be overriden for localization support.  The format must be
24710      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24711      */
24712     format : "m/d/y",
24713     /**
24714      * @cfg {Array} disabledDays
24715      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24716      */
24717     disabledDays : null,
24718     /**
24719      * @cfg {String} disabledDaysText
24720      * The tooltip to display when the date falls on a disabled day (defaults to "")
24721      */
24722     disabledDaysText : "",
24723     /**
24724      * @cfg {RegExp} disabledDatesRE
24725      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24726      */
24727     disabledDatesRE : null,
24728     /**
24729      * @cfg {String} disabledDatesText
24730      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24731      */
24732     disabledDatesText : "",
24733     /**
24734      * @cfg {Boolean} constrainToViewport
24735      * True to constrain the date picker to the viewport (defaults to true)
24736      */
24737     constrainToViewport : true,
24738     /**
24739      * @cfg {Array} monthNames
24740      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24741      */
24742     monthNames : Date.monthNames,
24743     /**
24744      * @cfg {Array} dayNames
24745      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24746      */
24747     dayNames : Date.dayNames,
24748     /**
24749      * @cfg {String} nextText
24750      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24751      */
24752     nextText: 'Next Month (Control+Right)',
24753     /**
24754      * @cfg {String} prevText
24755      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24756      */
24757     prevText: 'Previous Month (Control+Left)',
24758     /**
24759      * @cfg {String} monthYearText
24760      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24761      */
24762     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24763     /**
24764      * @cfg {Number} startDay
24765      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24766      */
24767     startDay : 0,
24768     /**
24769      * @cfg {Bool} showClear
24770      * Show a clear button (usefull for date form elements that can be blank.)
24771      */
24772     
24773     showClear: false,
24774     
24775     /**
24776      * Sets the value of the date field
24777      * @param {Date} value The date to set
24778      */
24779     setValue : function(value){
24780         var old = this.value;
24781         this.value = value.clearTime(true);
24782         if(this.el){
24783             this.update(this.value);
24784         }
24785     },
24786
24787     /**
24788      * Gets the current selected value of the date field
24789      * @return {Date} The selected date
24790      */
24791     getValue : function(){
24792         return this.value;
24793     },
24794
24795     // private
24796     focus : function(){
24797         if(this.el){
24798             this.update(this.activeDate);
24799         }
24800     },
24801
24802     // private
24803     onRender : function(container, position){
24804         var m = [
24805              '<table cellspacing="0">',
24806                 '<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>',
24807                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24808         var dn = this.dayNames;
24809         for(var i = 0; i < 7; i++){
24810             var d = this.startDay+i;
24811             if(d > 6){
24812                 d = d-7;
24813             }
24814             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24815         }
24816         m[m.length] = "</tr></thead><tbody><tr>";
24817         for(var i = 0; i < 42; i++) {
24818             if(i % 7 == 0 && i != 0){
24819                 m[m.length] = "</tr><tr>";
24820             }
24821             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24822         }
24823         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24824             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24825
24826         var el = document.createElement("div");
24827         el.className = "x-date-picker";
24828         el.innerHTML = m.join("");
24829
24830         container.dom.insertBefore(el, position);
24831
24832         this.el = Roo.get(el);
24833         this.eventEl = Roo.get(el.firstChild);
24834
24835         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24836             handler: this.showPrevMonth,
24837             scope: this,
24838             preventDefault:true,
24839             stopDefault:true
24840         });
24841
24842         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24843             handler: this.showNextMonth,
24844             scope: this,
24845             preventDefault:true,
24846             stopDefault:true
24847         });
24848
24849         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24850
24851         this.monthPicker = this.el.down('div.x-date-mp');
24852         this.monthPicker.enableDisplayMode('block');
24853         
24854         var kn = new Roo.KeyNav(this.eventEl, {
24855             "left" : function(e){
24856                 e.ctrlKey ?
24857                     this.showPrevMonth() :
24858                     this.update(this.activeDate.add("d", -1));
24859             },
24860
24861             "right" : function(e){
24862                 e.ctrlKey ?
24863                     this.showNextMonth() :
24864                     this.update(this.activeDate.add("d", 1));
24865             },
24866
24867             "up" : function(e){
24868                 e.ctrlKey ?
24869                     this.showNextYear() :
24870                     this.update(this.activeDate.add("d", -7));
24871             },
24872
24873             "down" : function(e){
24874                 e.ctrlKey ?
24875                     this.showPrevYear() :
24876                     this.update(this.activeDate.add("d", 7));
24877             },
24878
24879             "pageUp" : function(e){
24880                 this.showNextMonth();
24881             },
24882
24883             "pageDown" : function(e){
24884                 this.showPrevMonth();
24885             },
24886
24887             "enter" : function(e){
24888                 e.stopPropagation();
24889                 return true;
24890             },
24891
24892             scope : this
24893         });
24894
24895         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24896
24897         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24898
24899         this.el.unselectable();
24900         
24901         this.cells = this.el.select("table.x-date-inner tbody td");
24902         this.textNodes = this.el.query("table.x-date-inner tbody span");
24903
24904         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24905             text: "&#160;",
24906             tooltip: this.monthYearText
24907         });
24908
24909         this.mbtn.on('click', this.showMonthPicker, this);
24910         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24911
24912
24913         var today = (new Date()).dateFormat(this.format);
24914         
24915         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24916         if (this.showClear) {
24917             baseTb.add( new Roo.Toolbar.Fill());
24918         }
24919         baseTb.add({
24920             text: String.format(this.todayText, today),
24921             tooltip: String.format(this.todayTip, today),
24922             handler: this.selectToday,
24923             scope: this
24924         });
24925         
24926         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24927             
24928         //});
24929         if (this.showClear) {
24930             
24931             baseTb.add( new Roo.Toolbar.Fill());
24932             baseTb.add({
24933                 text: '&#160;',
24934                 cls: 'x-btn-icon x-btn-clear',
24935                 handler: function() {
24936                     //this.value = '';
24937                     this.fireEvent("select", this, '');
24938                 },
24939                 scope: this
24940             });
24941         }
24942         
24943         
24944         if(Roo.isIE){
24945             this.el.repaint();
24946         }
24947         this.update(this.value);
24948     },
24949
24950     createMonthPicker : function(){
24951         if(!this.monthPicker.dom.firstChild){
24952             var buf = ['<table border="0" cellspacing="0">'];
24953             for(var i = 0; i < 6; i++){
24954                 buf.push(
24955                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24956                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24957                     i == 0 ?
24958                     '<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>' :
24959                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24960                 );
24961             }
24962             buf.push(
24963                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24964                     this.okText,
24965                     '</button><button type="button" class="x-date-mp-cancel">',
24966                     this.cancelText,
24967                     '</button></td></tr>',
24968                 '</table>'
24969             );
24970             this.monthPicker.update(buf.join(''));
24971             this.monthPicker.on('click', this.onMonthClick, this);
24972             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24973
24974             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24975             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24976
24977             this.mpMonths.each(function(m, a, i){
24978                 i += 1;
24979                 if((i%2) == 0){
24980                     m.dom.xmonth = 5 + Math.round(i * .5);
24981                 }else{
24982                     m.dom.xmonth = Math.round((i-1) * .5);
24983                 }
24984             });
24985         }
24986     },
24987
24988     showMonthPicker : function(){
24989         this.createMonthPicker();
24990         var size = this.el.getSize();
24991         this.monthPicker.setSize(size);
24992         this.monthPicker.child('table').setSize(size);
24993
24994         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24995         this.updateMPMonth(this.mpSelMonth);
24996         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24997         this.updateMPYear(this.mpSelYear);
24998
24999         this.monthPicker.slideIn('t', {duration:.2});
25000     },
25001
25002     updateMPYear : function(y){
25003         this.mpyear = y;
25004         var ys = this.mpYears.elements;
25005         for(var i = 1; i <= 10; i++){
25006             var td = ys[i-1], y2;
25007             if((i%2) == 0){
25008                 y2 = y + Math.round(i * .5);
25009                 td.firstChild.innerHTML = y2;
25010                 td.xyear = y2;
25011             }else{
25012                 y2 = y - (5-Math.round(i * .5));
25013                 td.firstChild.innerHTML = y2;
25014                 td.xyear = y2;
25015             }
25016             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25017         }
25018     },
25019
25020     updateMPMonth : function(sm){
25021         this.mpMonths.each(function(m, a, i){
25022             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25023         });
25024     },
25025
25026     selectMPMonth: function(m){
25027         
25028     },
25029
25030     onMonthClick : function(e, t){
25031         e.stopEvent();
25032         var el = new Roo.Element(t), pn;
25033         if(el.is('button.x-date-mp-cancel')){
25034             this.hideMonthPicker();
25035         }
25036         else if(el.is('button.x-date-mp-ok')){
25037             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25038             this.hideMonthPicker();
25039         }
25040         else if(pn = el.up('td.x-date-mp-month', 2)){
25041             this.mpMonths.removeClass('x-date-mp-sel');
25042             pn.addClass('x-date-mp-sel');
25043             this.mpSelMonth = pn.dom.xmonth;
25044         }
25045         else if(pn = el.up('td.x-date-mp-year', 2)){
25046             this.mpYears.removeClass('x-date-mp-sel');
25047             pn.addClass('x-date-mp-sel');
25048             this.mpSelYear = pn.dom.xyear;
25049         }
25050         else if(el.is('a.x-date-mp-prev')){
25051             this.updateMPYear(this.mpyear-10);
25052         }
25053         else if(el.is('a.x-date-mp-next')){
25054             this.updateMPYear(this.mpyear+10);
25055         }
25056     },
25057
25058     onMonthDblClick : function(e, t){
25059         e.stopEvent();
25060         var el = new Roo.Element(t), pn;
25061         if(pn = el.up('td.x-date-mp-month', 2)){
25062             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25063             this.hideMonthPicker();
25064         }
25065         else if(pn = el.up('td.x-date-mp-year', 2)){
25066             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25067             this.hideMonthPicker();
25068         }
25069     },
25070
25071     hideMonthPicker : function(disableAnim){
25072         if(this.monthPicker){
25073             if(disableAnim === true){
25074                 this.monthPicker.hide();
25075             }else{
25076                 this.monthPicker.slideOut('t', {duration:.2});
25077             }
25078         }
25079     },
25080
25081     // private
25082     showPrevMonth : function(e){
25083         this.update(this.activeDate.add("mo", -1));
25084     },
25085
25086     // private
25087     showNextMonth : function(e){
25088         this.update(this.activeDate.add("mo", 1));
25089     },
25090
25091     // private
25092     showPrevYear : function(){
25093         this.update(this.activeDate.add("y", -1));
25094     },
25095
25096     // private
25097     showNextYear : function(){
25098         this.update(this.activeDate.add("y", 1));
25099     },
25100
25101     // private
25102     handleMouseWheel : function(e){
25103         var delta = e.getWheelDelta();
25104         if(delta > 0){
25105             this.showPrevMonth();
25106             e.stopEvent();
25107         } else if(delta < 0){
25108             this.showNextMonth();
25109             e.stopEvent();
25110         }
25111     },
25112
25113     // private
25114     handleDateClick : function(e, t){
25115         e.stopEvent();
25116         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25117             this.setValue(new Date(t.dateValue));
25118             this.fireEvent("select", this, this.value);
25119         }
25120     },
25121
25122     // private
25123     selectToday : function(){
25124         this.setValue(new Date().clearTime());
25125         this.fireEvent("select", this, this.value);
25126     },
25127
25128     // private
25129     update : function(date)
25130     {
25131         var vd = this.activeDate;
25132         this.activeDate = date;
25133         if(vd && this.el){
25134             var t = date.getTime();
25135             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25136                 this.cells.removeClass("x-date-selected");
25137                 this.cells.each(function(c){
25138                    if(c.dom.firstChild.dateValue == t){
25139                        c.addClass("x-date-selected");
25140                        setTimeout(function(){
25141                             try{c.dom.firstChild.focus();}catch(e){}
25142                        }, 50);
25143                        return false;
25144                    }
25145                 });
25146                 return;
25147             }
25148         }
25149         
25150         var days = date.getDaysInMonth();
25151         var firstOfMonth = date.getFirstDateOfMonth();
25152         var startingPos = firstOfMonth.getDay()-this.startDay;
25153
25154         if(startingPos <= this.startDay){
25155             startingPos += 7;
25156         }
25157
25158         var pm = date.add("mo", -1);
25159         var prevStart = pm.getDaysInMonth()-startingPos;
25160
25161         var cells = this.cells.elements;
25162         var textEls = this.textNodes;
25163         days += startingPos;
25164
25165         // convert everything to numbers so it's fast
25166         var day = 86400000;
25167         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25168         var today = new Date().clearTime().getTime();
25169         var sel = date.clearTime().getTime();
25170         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25171         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25172         var ddMatch = this.disabledDatesRE;
25173         var ddText = this.disabledDatesText;
25174         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25175         var ddaysText = this.disabledDaysText;
25176         var format = this.format;
25177
25178         var setCellClass = function(cal, cell){
25179             cell.title = "";
25180             var t = d.getTime();
25181             cell.firstChild.dateValue = t;
25182             if(t == today){
25183                 cell.className += " x-date-today";
25184                 cell.title = cal.todayText;
25185             }
25186             if(t == sel){
25187                 cell.className += " x-date-selected";
25188                 setTimeout(function(){
25189                     try{cell.firstChild.focus();}catch(e){}
25190                 }, 50);
25191             }
25192             // disabling
25193             if(t < min) {
25194                 cell.className = " x-date-disabled";
25195                 cell.title = cal.minText;
25196                 return;
25197             }
25198             if(t > max) {
25199                 cell.className = " x-date-disabled";
25200                 cell.title = cal.maxText;
25201                 return;
25202             }
25203             if(ddays){
25204                 if(ddays.indexOf(d.getDay()) != -1){
25205                     cell.title = ddaysText;
25206                     cell.className = " x-date-disabled";
25207                 }
25208             }
25209             if(ddMatch && format){
25210                 var fvalue = d.dateFormat(format);
25211                 if(ddMatch.test(fvalue)){
25212                     cell.title = ddText.replace("%0", fvalue);
25213                     cell.className = " x-date-disabled";
25214                 }
25215             }
25216         };
25217
25218         var i = 0;
25219         for(; i < startingPos; i++) {
25220             textEls[i].innerHTML = (++prevStart);
25221             d.setDate(d.getDate()+1);
25222             cells[i].className = "x-date-prevday";
25223             setCellClass(this, cells[i]);
25224         }
25225         for(; i < days; i++){
25226             intDay = i - startingPos + 1;
25227             textEls[i].innerHTML = (intDay);
25228             d.setDate(d.getDate()+1);
25229             cells[i].className = "x-date-active";
25230             setCellClass(this, cells[i]);
25231         }
25232         var extraDays = 0;
25233         for(; i < 42; i++) {
25234              textEls[i].innerHTML = (++extraDays);
25235              d.setDate(d.getDate()+1);
25236              cells[i].className = "x-date-nextday";
25237              setCellClass(this, cells[i]);
25238         }
25239
25240         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25241         this.fireEvent('monthchange', this, date);
25242         
25243         if(!this.internalRender){
25244             var main = this.el.dom.firstChild;
25245             var w = main.offsetWidth;
25246             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25247             Roo.fly(main).setWidth(w);
25248             this.internalRender = true;
25249             // opera does not respect the auto grow header center column
25250             // then, after it gets a width opera refuses to recalculate
25251             // without a second pass
25252             if(Roo.isOpera && !this.secondPass){
25253                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25254                 this.secondPass = true;
25255                 this.update.defer(10, this, [date]);
25256             }
25257         }
25258         
25259         
25260     }
25261 });        /*
25262  * Based on:
25263  * Ext JS Library 1.1.1
25264  * Copyright(c) 2006-2007, Ext JS, LLC.
25265  *
25266  * Originally Released Under LGPL - original licence link has changed is not relivant.
25267  *
25268  * Fork - LGPL
25269  * <script type="text/javascript">
25270  */
25271 /**
25272  * @class Roo.TabPanel
25273  * @extends Roo.util.Observable
25274  * A lightweight tab container.
25275  * <br><br>
25276  * Usage:
25277  * <pre><code>
25278 // basic tabs 1, built from existing content
25279 var tabs = new Roo.TabPanel("tabs1");
25280 tabs.addTab("script", "View Script");
25281 tabs.addTab("markup", "View Markup");
25282 tabs.activate("script");
25283
25284 // more advanced tabs, built from javascript
25285 var jtabs = new Roo.TabPanel("jtabs");
25286 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25287
25288 // set up the UpdateManager
25289 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25290 var updater = tab2.getUpdateManager();
25291 updater.setDefaultUrl("ajax1.htm");
25292 tab2.on('activate', updater.refresh, updater, true);
25293
25294 // Use setUrl for Ajax loading
25295 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25296 tab3.setUrl("ajax2.htm", null, true);
25297
25298 // Disabled tab
25299 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25300 tab4.disable();
25301
25302 jtabs.activate("jtabs-1");
25303  * </code></pre>
25304  * @constructor
25305  * Create a new TabPanel.
25306  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25307  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25308  */
25309 Roo.TabPanel = function(container, config){
25310     /**
25311     * The container element for this TabPanel.
25312     * @type Roo.Element
25313     */
25314     this.el = Roo.get(container, true);
25315     if(config){
25316         if(typeof config == "boolean"){
25317             this.tabPosition = config ? "bottom" : "top";
25318         }else{
25319             Roo.apply(this, config);
25320         }
25321     }
25322     if(this.tabPosition == "bottom"){
25323         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25324         this.el.addClass("x-tabs-bottom");
25325     }
25326     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25327     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25328     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25329     if(Roo.isIE){
25330         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25331     }
25332     if(this.tabPosition != "bottom"){
25333         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25334          * @type Roo.Element
25335          */
25336         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25337         this.el.addClass("x-tabs-top");
25338     }
25339     this.items = [];
25340
25341     this.bodyEl.setStyle("position", "relative");
25342
25343     this.active = null;
25344     this.activateDelegate = this.activate.createDelegate(this);
25345
25346     this.addEvents({
25347         /**
25348          * @event tabchange
25349          * Fires when the active tab changes
25350          * @param {Roo.TabPanel} this
25351          * @param {Roo.TabPanelItem} activePanel The new active tab
25352          */
25353         "tabchange": true,
25354         /**
25355          * @event beforetabchange
25356          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25357          * @param {Roo.TabPanel} this
25358          * @param {Object} e Set cancel to true on this object to cancel the tab change
25359          * @param {Roo.TabPanelItem} tab The tab being changed to
25360          */
25361         "beforetabchange" : true
25362     });
25363
25364     Roo.EventManager.onWindowResize(this.onResize, this);
25365     this.cpad = this.el.getPadding("lr");
25366     this.hiddenCount = 0;
25367
25368
25369     // toolbar on the tabbar support...
25370     if (this.toolbar) {
25371         var tcfg = this.toolbar;
25372         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25373         this.toolbar = new Roo.Toolbar(tcfg);
25374         if (Roo.isSafari) {
25375             var tbl = tcfg.container.child('table', true);
25376             tbl.setAttribute('width', '100%');
25377         }
25378         
25379     }
25380    
25381
25382
25383     Roo.TabPanel.superclass.constructor.call(this);
25384 };
25385
25386 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25387     /*
25388      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25389      */
25390     tabPosition : "top",
25391     /*
25392      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25393      */
25394     currentTabWidth : 0,
25395     /*
25396      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25397      */
25398     minTabWidth : 40,
25399     /*
25400      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25401      */
25402     maxTabWidth : 250,
25403     /*
25404      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25405      */
25406     preferredTabWidth : 175,
25407     /*
25408      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25409      */
25410     resizeTabs : false,
25411     /*
25412      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25413      */
25414     monitorResize : true,
25415     /*
25416      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25417      */
25418     toolbar : false,
25419
25420     /**
25421      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25422      * @param {String} id The id of the div to use <b>or create</b>
25423      * @param {String} text The text for the tab
25424      * @param {String} content (optional) Content to put in the TabPanelItem body
25425      * @param {Boolean} closable (optional) True to create a close icon on the tab
25426      * @return {Roo.TabPanelItem} The created TabPanelItem
25427      */
25428     addTab : function(id, text, content, closable){
25429         var item = new Roo.TabPanelItem(this, id, text, closable);
25430         this.addTabItem(item);
25431         if(content){
25432             item.setContent(content);
25433         }
25434         return item;
25435     },
25436
25437     /**
25438      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25439      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25440      * @return {Roo.TabPanelItem}
25441      */
25442     getTab : function(id){
25443         return this.items[id];
25444     },
25445
25446     /**
25447      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25448      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25449      */
25450     hideTab : function(id){
25451         var t = this.items[id];
25452         if(!t.isHidden()){
25453            t.setHidden(true);
25454            this.hiddenCount++;
25455            this.autoSizeTabs();
25456         }
25457     },
25458
25459     /**
25460      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25461      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25462      */
25463     unhideTab : function(id){
25464         var t = this.items[id];
25465         if(t.isHidden()){
25466            t.setHidden(false);
25467            this.hiddenCount--;
25468            this.autoSizeTabs();
25469         }
25470     },
25471
25472     /**
25473      * Adds an existing {@link Roo.TabPanelItem}.
25474      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25475      */
25476     addTabItem : function(item){
25477         this.items[item.id] = item;
25478         this.items.push(item);
25479         if(this.resizeTabs){
25480            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25481            this.autoSizeTabs();
25482         }else{
25483             item.autoSize();
25484         }
25485     },
25486
25487     /**
25488      * Removes a {@link Roo.TabPanelItem}.
25489      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25490      */
25491     removeTab : function(id){
25492         var items = this.items;
25493         var tab = items[id];
25494         if(!tab) { return; }
25495         var index = items.indexOf(tab);
25496         if(this.active == tab && items.length > 1){
25497             var newTab = this.getNextAvailable(index);
25498             if(newTab) {
25499                 newTab.activate();
25500             }
25501         }
25502         this.stripEl.dom.removeChild(tab.pnode.dom);
25503         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25504             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25505         }
25506         items.splice(index, 1);
25507         delete this.items[tab.id];
25508         tab.fireEvent("close", tab);
25509         tab.purgeListeners();
25510         this.autoSizeTabs();
25511     },
25512
25513     getNextAvailable : function(start){
25514         var items = this.items;
25515         var index = start;
25516         // look for a next tab that will slide over to
25517         // replace the one being removed
25518         while(index < items.length){
25519             var item = items[++index];
25520             if(item && !item.isHidden()){
25521                 return item;
25522             }
25523         }
25524         // if one isn't found select the previous tab (on the left)
25525         index = start;
25526         while(index >= 0){
25527             var item = items[--index];
25528             if(item && !item.isHidden()){
25529                 return item;
25530             }
25531         }
25532         return null;
25533     },
25534
25535     /**
25536      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25537      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25538      */
25539     disableTab : function(id){
25540         var tab = this.items[id];
25541         if(tab && this.active != tab){
25542             tab.disable();
25543         }
25544     },
25545
25546     /**
25547      * Enables a {@link Roo.TabPanelItem} that is disabled.
25548      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25549      */
25550     enableTab : function(id){
25551         var tab = this.items[id];
25552         tab.enable();
25553     },
25554
25555     /**
25556      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25557      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25558      * @return {Roo.TabPanelItem} The TabPanelItem.
25559      */
25560     activate : function(id){
25561         var tab = this.items[id];
25562         if(!tab){
25563             return null;
25564         }
25565         if(tab == this.active || tab.disabled){
25566             return tab;
25567         }
25568         var e = {};
25569         this.fireEvent("beforetabchange", this, e, tab);
25570         if(e.cancel !== true && !tab.disabled){
25571             if(this.active){
25572                 this.active.hide();
25573             }
25574             this.active = this.items[id];
25575             this.active.show();
25576             this.fireEvent("tabchange", this, this.active);
25577         }
25578         return tab;
25579     },
25580
25581     /**
25582      * Gets the active {@link Roo.TabPanelItem}.
25583      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25584      */
25585     getActiveTab : function(){
25586         return this.active;
25587     },
25588
25589     /**
25590      * Updates the tab body element to fit the height of the container element
25591      * for overflow scrolling
25592      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25593      */
25594     syncHeight : function(targetHeight){
25595         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25596         var bm = this.bodyEl.getMargins();
25597         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25598         this.bodyEl.setHeight(newHeight);
25599         return newHeight;
25600     },
25601
25602     onResize : function(){
25603         if(this.monitorResize){
25604             this.autoSizeTabs();
25605         }
25606     },
25607
25608     /**
25609      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25610      */
25611     beginUpdate : function(){
25612         this.updating = true;
25613     },
25614
25615     /**
25616      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25617      */
25618     endUpdate : function(){
25619         this.updating = false;
25620         this.autoSizeTabs();
25621     },
25622
25623     /**
25624      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25625      */
25626     autoSizeTabs : function(){
25627         var count = this.items.length;
25628         var vcount = count - this.hiddenCount;
25629         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25630         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25631         var availWidth = Math.floor(w / vcount);
25632         var b = this.stripBody;
25633         if(b.getWidth() > w){
25634             var tabs = this.items;
25635             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25636             if(availWidth < this.minTabWidth){
25637                 /*if(!this.sleft){    // incomplete scrolling code
25638                     this.createScrollButtons();
25639                 }
25640                 this.showScroll();
25641                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25642             }
25643         }else{
25644             if(this.currentTabWidth < this.preferredTabWidth){
25645                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25646             }
25647         }
25648     },
25649
25650     /**
25651      * Returns the number of tabs in this TabPanel.
25652      * @return {Number}
25653      */
25654      getCount : function(){
25655          return this.items.length;
25656      },
25657
25658     /**
25659      * Resizes all the tabs to the passed width
25660      * @param {Number} The new width
25661      */
25662     setTabWidth : function(width){
25663         this.currentTabWidth = width;
25664         for(var i = 0, len = this.items.length; i < len; i++) {
25665                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25666         }
25667     },
25668
25669     /**
25670      * Destroys this TabPanel
25671      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25672      */
25673     destroy : function(removeEl){
25674         Roo.EventManager.removeResizeListener(this.onResize, this);
25675         for(var i = 0, len = this.items.length; i < len; i++){
25676             this.items[i].purgeListeners();
25677         }
25678         if(removeEl === true){
25679             this.el.update("");
25680             this.el.remove();
25681         }
25682     }
25683 });
25684
25685 /**
25686  * @class Roo.TabPanelItem
25687  * @extends Roo.util.Observable
25688  * Represents an individual item (tab plus body) in a TabPanel.
25689  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25690  * @param {String} id The id of this TabPanelItem
25691  * @param {String} text The text for the tab of this TabPanelItem
25692  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25693  */
25694 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25695     /**
25696      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25697      * @type Roo.TabPanel
25698      */
25699     this.tabPanel = tabPanel;
25700     /**
25701      * The id for this TabPanelItem
25702      * @type String
25703      */
25704     this.id = id;
25705     /** @private */
25706     this.disabled = false;
25707     /** @private */
25708     this.text = text;
25709     /** @private */
25710     this.loaded = false;
25711     this.closable = closable;
25712
25713     /**
25714      * The body element for this TabPanelItem.
25715      * @type Roo.Element
25716      */
25717     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25718     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25719     this.bodyEl.setStyle("display", "block");
25720     this.bodyEl.setStyle("zoom", "1");
25721     this.hideAction();
25722
25723     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25724     /** @private */
25725     this.el = Roo.get(els.el, true);
25726     this.inner = Roo.get(els.inner, true);
25727     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25728     this.pnode = Roo.get(els.el.parentNode, true);
25729     this.el.on("mousedown", this.onTabMouseDown, this);
25730     this.el.on("click", this.onTabClick, this);
25731     /** @private */
25732     if(closable){
25733         var c = Roo.get(els.close, true);
25734         c.dom.title = this.closeText;
25735         c.addClassOnOver("close-over");
25736         c.on("click", this.closeClick, this);
25737      }
25738
25739     this.addEvents({
25740          /**
25741          * @event activate
25742          * Fires when this tab becomes the active tab.
25743          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25744          * @param {Roo.TabPanelItem} this
25745          */
25746         "activate": true,
25747         /**
25748          * @event beforeclose
25749          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25750          * @param {Roo.TabPanelItem} this
25751          * @param {Object} e Set cancel to true on this object to cancel the close.
25752          */
25753         "beforeclose": true,
25754         /**
25755          * @event close
25756          * Fires when this tab is closed.
25757          * @param {Roo.TabPanelItem} this
25758          */
25759          "close": true,
25760         /**
25761          * @event deactivate
25762          * Fires when this tab is no longer the active tab.
25763          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25764          * @param {Roo.TabPanelItem} this
25765          */
25766          "deactivate" : true
25767     });
25768     this.hidden = false;
25769
25770     Roo.TabPanelItem.superclass.constructor.call(this);
25771 };
25772
25773 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25774     purgeListeners : function(){
25775        Roo.util.Observable.prototype.purgeListeners.call(this);
25776        this.el.removeAllListeners();
25777     },
25778     /**
25779      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25780      */
25781     show : function(){
25782         this.pnode.addClass("on");
25783         this.showAction();
25784         if(Roo.isOpera){
25785             this.tabPanel.stripWrap.repaint();
25786         }
25787         this.fireEvent("activate", this.tabPanel, this);
25788     },
25789
25790     /**
25791      * Returns true if this tab is the active tab.
25792      * @return {Boolean}
25793      */
25794     isActive : function(){
25795         return this.tabPanel.getActiveTab() == this;
25796     },
25797
25798     /**
25799      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25800      */
25801     hide : function(){
25802         this.pnode.removeClass("on");
25803         this.hideAction();
25804         this.fireEvent("deactivate", this.tabPanel, this);
25805     },
25806
25807     hideAction : function(){
25808         this.bodyEl.hide();
25809         this.bodyEl.setStyle("position", "absolute");
25810         this.bodyEl.setLeft("-20000px");
25811         this.bodyEl.setTop("-20000px");
25812     },
25813
25814     showAction : function(){
25815         this.bodyEl.setStyle("position", "relative");
25816         this.bodyEl.setTop("");
25817         this.bodyEl.setLeft("");
25818         this.bodyEl.show();
25819     },
25820
25821     /**
25822      * Set the tooltip for the tab.
25823      * @param {String} tooltip The tab's tooltip
25824      */
25825     setTooltip : function(text){
25826         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25827             this.textEl.dom.qtip = text;
25828             this.textEl.dom.removeAttribute('title');
25829         }else{
25830             this.textEl.dom.title = text;
25831         }
25832     },
25833
25834     onTabClick : function(e){
25835         e.preventDefault();
25836         this.tabPanel.activate(this.id);
25837     },
25838
25839     onTabMouseDown : function(e){
25840         e.preventDefault();
25841         this.tabPanel.activate(this.id);
25842     },
25843
25844     getWidth : function(){
25845         return this.inner.getWidth();
25846     },
25847
25848     setWidth : function(width){
25849         var iwidth = width - this.pnode.getPadding("lr");
25850         this.inner.setWidth(iwidth);
25851         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25852         this.pnode.setWidth(width);
25853     },
25854
25855     /**
25856      * Show or hide the tab
25857      * @param {Boolean} hidden True to hide or false to show.
25858      */
25859     setHidden : function(hidden){
25860         this.hidden = hidden;
25861         this.pnode.setStyle("display", hidden ? "none" : "");
25862     },
25863
25864     /**
25865      * Returns true if this tab is "hidden"
25866      * @return {Boolean}
25867      */
25868     isHidden : function(){
25869         return this.hidden;
25870     },
25871
25872     /**
25873      * Returns the text for this tab
25874      * @return {String}
25875      */
25876     getText : function(){
25877         return this.text;
25878     },
25879
25880     autoSize : function(){
25881         //this.el.beginMeasure();
25882         this.textEl.setWidth(1);
25883         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25884         //this.el.endMeasure();
25885     },
25886
25887     /**
25888      * Sets the text for the tab (Note: this also sets the tooltip text)
25889      * @param {String} text The tab's text and tooltip
25890      */
25891     setText : function(text){
25892         this.text = text;
25893         this.textEl.update(text);
25894         this.setTooltip(text);
25895         if(!this.tabPanel.resizeTabs){
25896             this.autoSize();
25897         }
25898     },
25899     /**
25900      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25901      */
25902     activate : function(){
25903         this.tabPanel.activate(this.id);
25904     },
25905
25906     /**
25907      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25908      */
25909     disable : function(){
25910         if(this.tabPanel.active != this){
25911             this.disabled = true;
25912             this.pnode.addClass("disabled");
25913         }
25914     },
25915
25916     /**
25917      * Enables this TabPanelItem if it was previously disabled.
25918      */
25919     enable : function(){
25920         this.disabled = false;
25921         this.pnode.removeClass("disabled");
25922     },
25923
25924     /**
25925      * Sets the content for this TabPanelItem.
25926      * @param {String} content The content
25927      * @param {Boolean} loadScripts true to look for and load scripts
25928      */
25929     setContent : function(content, loadScripts){
25930         this.bodyEl.update(content, loadScripts);
25931     },
25932
25933     /**
25934      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25935      * @return {Roo.UpdateManager} The UpdateManager
25936      */
25937     getUpdateManager : function(){
25938         return this.bodyEl.getUpdateManager();
25939     },
25940
25941     /**
25942      * Set a URL to be used to load the content for this TabPanelItem.
25943      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25944      * @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)
25945      * @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)
25946      * @return {Roo.UpdateManager} The UpdateManager
25947      */
25948     setUrl : function(url, params, loadOnce){
25949         if(this.refreshDelegate){
25950             this.un('activate', this.refreshDelegate);
25951         }
25952         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25953         this.on("activate", this.refreshDelegate);
25954         return this.bodyEl.getUpdateManager();
25955     },
25956
25957     /** @private */
25958     _handleRefresh : function(url, params, loadOnce){
25959         if(!loadOnce || !this.loaded){
25960             var updater = this.bodyEl.getUpdateManager();
25961             updater.update(url, params, this._setLoaded.createDelegate(this));
25962         }
25963     },
25964
25965     /**
25966      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25967      *   Will fail silently if the setUrl method has not been called.
25968      *   This does not activate the panel, just updates its content.
25969      */
25970     refresh : function(){
25971         if(this.refreshDelegate){
25972            this.loaded = false;
25973            this.refreshDelegate();
25974         }
25975     },
25976
25977     /** @private */
25978     _setLoaded : function(){
25979         this.loaded = true;
25980     },
25981
25982     /** @private */
25983     closeClick : function(e){
25984         var o = {};
25985         e.stopEvent();
25986         this.fireEvent("beforeclose", this, o);
25987         if(o.cancel !== true){
25988             this.tabPanel.removeTab(this.id);
25989         }
25990     },
25991     /**
25992      * The text displayed in the tooltip for the close icon.
25993      * @type String
25994      */
25995     closeText : "Close this tab"
25996 });
25997
25998 /** @private */
25999 Roo.TabPanel.prototype.createStrip = function(container){
26000     var strip = document.createElement("div");
26001     strip.className = "x-tabs-wrap";
26002     container.appendChild(strip);
26003     return strip;
26004 };
26005 /** @private */
26006 Roo.TabPanel.prototype.createStripList = function(strip){
26007     // div wrapper for retard IE
26008     // returns the "tr" element.
26009     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26010         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26011         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26012     return strip.firstChild.firstChild.firstChild.firstChild;
26013 };
26014 /** @private */
26015 Roo.TabPanel.prototype.createBody = function(container){
26016     var body = document.createElement("div");
26017     Roo.id(body, "tab-body");
26018     Roo.fly(body).addClass("x-tabs-body");
26019     container.appendChild(body);
26020     return body;
26021 };
26022 /** @private */
26023 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26024     var body = Roo.getDom(id);
26025     if(!body){
26026         body = document.createElement("div");
26027         body.id = id;
26028     }
26029     Roo.fly(body).addClass("x-tabs-item-body");
26030     bodyEl.insertBefore(body, bodyEl.firstChild);
26031     return body;
26032 };
26033 /** @private */
26034 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26035     var td = document.createElement("td");
26036     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26037     //stripEl.appendChild(td);
26038     if(closable){
26039         td.className = "x-tabs-closable";
26040         if(!this.closeTpl){
26041             this.closeTpl = new Roo.Template(
26042                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26043                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26044                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26045             );
26046         }
26047         var el = this.closeTpl.overwrite(td, {"text": text});
26048         var close = el.getElementsByTagName("div")[0];
26049         var inner = el.getElementsByTagName("em")[0];
26050         return {"el": el, "close": close, "inner": inner};
26051     } else {
26052         if(!this.tabTpl){
26053             this.tabTpl = new Roo.Template(
26054                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26055                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26056             );
26057         }
26058         var el = this.tabTpl.overwrite(td, {"text": text});
26059         var inner = el.getElementsByTagName("em")[0];
26060         return {"el": el, "inner": inner};
26061     }
26062 };/*
26063  * Based on:
26064  * Ext JS Library 1.1.1
26065  * Copyright(c) 2006-2007, Ext JS, LLC.
26066  *
26067  * Originally Released Under LGPL - original licence link has changed is not relivant.
26068  *
26069  * Fork - LGPL
26070  * <script type="text/javascript">
26071  */
26072
26073 /**
26074  * @class Roo.Button
26075  * @extends Roo.util.Observable
26076  * Simple Button class
26077  * @cfg {String} text The button text
26078  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26079  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26080  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26081  * @cfg {Object} scope The scope of the handler
26082  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26083  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26084  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26085  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26086  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26087  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26088    applies if enableToggle = true)
26089  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26090  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26091   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26092  * @constructor
26093  * Create a new button
26094  * @param {Object} config The config object
26095  */
26096 Roo.Button = function(renderTo, config)
26097 {
26098     if (!config) {
26099         config = renderTo;
26100         renderTo = config.renderTo || false;
26101     }
26102     
26103     Roo.apply(this, config);
26104     this.addEvents({
26105         /**
26106              * @event click
26107              * Fires when this button is clicked
26108              * @param {Button} this
26109              * @param {EventObject} e The click event
26110              */
26111             "click" : true,
26112         /**
26113              * @event toggle
26114              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26115              * @param {Button} this
26116              * @param {Boolean} pressed
26117              */
26118             "toggle" : true,
26119         /**
26120              * @event mouseover
26121              * Fires when the mouse hovers over the button
26122              * @param {Button} this
26123              * @param {Event} e The event object
26124              */
26125         'mouseover' : true,
26126         /**
26127              * @event mouseout
26128              * Fires when the mouse exits the button
26129              * @param {Button} this
26130              * @param {Event} e The event object
26131              */
26132         'mouseout': true,
26133          /**
26134              * @event render
26135              * Fires when the button is rendered
26136              * @param {Button} this
26137              */
26138         'render': true
26139     });
26140     if(this.menu){
26141         this.menu = Roo.menu.MenuMgr.get(this.menu);
26142     }
26143     // register listeners first!!  - so render can be captured..
26144     Roo.util.Observable.call(this);
26145     if(renderTo){
26146         this.render(renderTo);
26147     }
26148     
26149   
26150 };
26151
26152 Roo.extend(Roo.Button, Roo.util.Observable, {
26153     /**
26154      * 
26155      */
26156     
26157     /**
26158      * Read-only. True if this button is hidden
26159      * @type Boolean
26160      */
26161     hidden : false,
26162     /**
26163      * Read-only. True if this button is disabled
26164      * @type Boolean
26165      */
26166     disabled : false,
26167     /**
26168      * Read-only. True if this button is pressed (only if enableToggle = true)
26169      * @type Boolean
26170      */
26171     pressed : false,
26172
26173     /**
26174      * @cfg {Number} tabIndex 
26175      * The DOM tabIndex for this button (defaults to undefined)
26176      */
26177     tabIndex : undefined,
26178
26179     /**
26180      * @cfg {Boolean} enableToggle
26181      * True to enable pressed/not pressed toggling (defaults to false)
26182      */
26183     enableToggle: false,
26184     /**
26185      * @cfg {Mixed} menu
26186      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26187      */
26188     menu : undefined,
26189     /**
26190      * @cfg {String} menuAlign
26191      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26192      */
26193     menuAlign : "tl-bl?",
26194
26195     /**
26196      * @cfg {String} iconCls
26197      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26198      */
26199     iconCls : undefined,
26200     /**
26201      * @cfg {String} type
26202      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26203      */
26204     type : 'button',
26205
26206     // private
26207     menuClassTarget: 'tr',
26208
26209     /**
26210      * @cfg {String} clickEvent
26211      * The type of event to map to the button's event handler (defaults to 'click')
26212      */
26213     clickEvent : 'click',
26214
26215     /**
26216      * @cfg {Boolean} handleMouseEvents
26217      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26218      */
26219     handleMouseEvents : true,
26220
26221     /**
26222      * @cfg {String} tooltipType
26223      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26224      */
26225     tooltipType : 'qtip',
26226
26227     /**
26228      * @cfg {String} cls
26229      * A CSS class to apply to the button's main element.
26230      */
26231     
26232     /**
26233      * @cfg {Roo.Template} template (Optional)
26234      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26235      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26236      * require code modifications if required elements (e.g. a button) aren't present.
26237      */
26238
26239     // private
26240     render : function(renderTo){
26241         var btn;
26242         if(this.hideParent){
26243             this.parentEl = Roo.get(renderTo);
26244         }
26245         if(!this.dhconfig){
26246             if(!this.template){
26247                 if(!Roo.Button.buttonTemplate){
26248                     // hideous table template
26249                     Roo.Button.buttonTemplate = new Roo.Template(
26250                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26251                         '<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>',
26252                         "</tr></tbody></table>");
26253                 }
26254                 this.template = Roo.Button.buttonTemplate;
26255             }
26256             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26257             var btnEl = btn.child("button:first");
26258             btnEl.on('focus', this.onFocus, this);
26259             btnEl.on('blur', this.onBlur, this);
26260             if(this.cls){
26261                 btn.addClass(this.cls);
26262             }
26263             if(this.icon){
26264                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26265             }
26266             if(this.iconCls){
26267                 btnEl.addClass(this.iconCls);
26268                 if(!this.cls){
26269                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26270                 }
26271             }
26272             if(this.tabIndex !== undefined){
26273                 btnEl.dom.tabIndex = this.tabIndex;
26274             }
26275             if(this.tooltip){
26276                 if(typeof this.tooltip == 'object'){
26277                     Roo.QuickTips.tips(Roo.apply({
26278                           target: btnEl.id
26279                     }, this.tooltip));
26280                 } else {
26281                     btnEl.dom[this.tooltipType] = this.tooltip;
26282                 }
26283             }
26284         }else{
26285             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26286         }
26287         this.el = btn;
26288         if(this.id){
26289             this.el.dom.id = this.el.id = this.id;
26290         }
26291         if(this.menu){
26292             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26293             this.menu.on("show", this.onMenuShow, this);
26294             this.menu.on("hide", this.onMenuHide, this);
26295         }
26296         btn.addClass("x-btn");
26297         if(Roo.isIE && !Roo.isIE7){
26298             this.autoWidth.defer(1, this);
26299         }else{
26300             this.autoWidth();
26301         }
26302         if(this.handleMouseEvents){
26303             btn.on("mouseover", this.onMouseOver, this);
26304             btn.on("mouseout", this.onMouseOut, this);
26305             btn.on("mousedown", this.onMouseDown, this);
26306         }
26307         btn.on(this.clickEvent, this.onClick, this);
26308         //btn.on("mouseup", this.onMouseUp, this);
26309         if(this.hidden){
26310             this.hide();
26311         }
26312         if(this.disabled){
26313             this.disable();
26314         }
26315         Roo.ButtonToggleMgr.register(this);
26316         if(this.pressed){
26317             this.el.addClass("x-btn-pressed");
26318         }
26319         if(this.repeat){
26320             var repeater = new Roo.util.ClickRepeater(btn,
26321                 typeof this.repeat == "object" ? this.repeat : {}
26322             );
26323             repeater.on("click", this.onClick,  this);
26324         }
26325         
26326         this.fireEvent('render', this);
26327         
26328     },
26329     /**
26330      * Returns the button's underlying element
26331      * @return {Roo.Element} The element
26332      */
26333     getEl : function(){
26334         return this.el;  
26335     },
26336     
26337     /**
26338      * Destroys this Button and removes any listeners.
26339      */
26340     destroy : function(){
26341         Roo.ButtonToggleMgr.unregister(this);
26342         this.el.removeAllListeners();
26343         this.purgeListeners();
26344         this.el.remove();
26345     },
26346
26347     // private
26348     autoWidth : function(){
26349         if(this.el){
26350             this.el.setWidth("auto");
26351             if(Roo.isIE7 && Roo.isStrict){
26352                 var ib = this.el.child('button');
26353                 if(ib && ib.getWidth() > 20){
26354                     ib.clip();
26355                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26356                 }
26357             }
26358             if(this.minWidth){
26359                 if(this.hidden){
26360                     this.el.beginMeasure();
26361                 }
26362                 if(this.el.getWidth() < this.minWidth){
26363                     this.el.setWidth(this.minWidth);
26364                 }
26365                 if(this.hidden){
26366                     this.el.endMeasure();
26367                 }
26368             }
26369         }
26370     },
26371
26372     /**
26373      * Assigns this button's click handler
26374      * @param {Function} handler The function to call when the button is clicked
26375      * @param {Object} scope (optional) Scope for the function passed in
26376      */
26377     setHandler : function(handler, scope){
26378         this.handler = handler;
26379         this.scope = scope;  
26380     },
26381     
26382     /**
26383      * Sets this button's text
26384      * @param {String} text The button text
26385      */
26386     setText : function(text){
26387         this.text = text;
26388         if(this.el){
26389             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26390         }
26391         this.autoWidth();
26392     },
26393     
26394     /**
26395      * Gets the text for this button
26396      * @return {String} The button text
26397      */
26398     getText : function(){
26399         return this.text;  
26400     },
26401     
26402     /**
26403      * Show this button
26404      */
26405     show: function(){
26406         this.hidden = false;
26407         if(this.el){
26408             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26409         }
26410     },
26411     
26412     /**
26413      * Hide this button
26414      */
26415     hide: function(){
26416         this.hidden = true;
26417         if(this.el){
26418             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26419         }
26420     },
26421     
26422     /**
26423      * Convenience function for boolean show/hide
26424      * @param {Boolean} visible True to show, false to hide
26425      */
26426     setVisible: function(visible){
26427         if(visible) {
26428             this.show();
26429         }else{
26430             this.hide();
26431         }
26432     },
26433     
26434     /**
26435      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26436      * @param {Boolean} state (optional) Force a particular state
26437      */
26438     toggle : function(state){
26439         state = state === undefined ? !this.pressed : state;
26440         if(state != this.pressed){
26441             if(state){
26442                 this.el.addClass("x-btn-pressed");
26443                 this.pressed = true;
26444                 this.fireEvent("toggle", this, true);
26445             }else{
26446                 this.el.removeClass("x-btn-pressed");
26447                 this.pressed = false;
26448                 this.fireEvent("toggle", this, false);
26449             }
26450             if(this.toggleHandler){
26451                 this.toggleHandler.call(this.scope || this, this, state);
26452             }
26453         }
26454     },
26455     
26456     /**
26457      * Focus the button
26458      */
26459     focus : function(){
26460         this.el.child('button:first').focus();
26461     },
26462     
26463     /**
26464      * Disable this button
26465      */
26466     disable : function(){
26467         if(this.el){
26468             this.el.addClass("x-btn-disabled");
26469         }
26470         this.disabled = true;
26471     },
26472     
26473     /**
26474      * Enable this button
26475      */
26476     enable : function(){
26477         if(this.el){
26478             this.el.removeClass("x-btn-disabled");
26479         }
26480         this.disabled = false;
26481     },
26482
26483     /**
26484      * Convenience function for boolean enable/disable
26485      * @param {Boolean} enabled True to enable, false to disable
26486      */
26487     setDisabled : function(v){
26488         this[v !== true ? "enable" : "disable"]();
26489     },
26490
26491     // private
26492     onClick : function(e){
26493         if(e){
26494             e.preventDefault();
26495         }
26496         if(e.button != 0){
26497             return;
26498         }
26499         if(!this.disabled){
26500             if(this.enableToggle){
26501                 this.toggle();
26502             }
26503             if(this.menu && !this.menu.isVisible()){
26504                 this.menu.show(this.el, this.menuAlign);
26505             }
26506             this.fireEvent("click", this, e);
26507             if(this.handler){
26508                 this.el.removeClass("x-btn-over");
26509                 this.handler.call(this.scope || this, this, e);
26510             }
26511         }
26512     },
26513     // private
26514     onMouseOver : function(e){
26515         if(!this.disabled){
26516             this.el.addClass("x-btn-over");
26517             this.fireEvent('mouseover', this, e);
26518         }
26519     },
26520     // private
26521     onMouseOut : function(e){
26522         if(!e.within(this.el,  true)){
26523             this.el.removeClass("x-btn-over");
26524             this.fireEvent('mouseout', this, e);
26525         }
26526     },
26527     // private
26528     onFocus : function(e){
26529         if(!this.disabled){
26530             this.el.addClass("x-btn-focus");
26531         }
26532     },
26533     // private
26534     onBlur : function(e){
26535         this.el.removeClass("x-btn-focus");
26536     },
26537     // private
26538     onMouseDown : function(e){
26539         if(!this.disabled && e.button == 0){
26540             this.el.addClass("x-btn-click");
26541             Roo.get(document).on('mouseup', this.onMouseUp, this);
26542         }
26543     },
26544     // private
26545     onMouseUp : function(e){
26546         if(e.button == 0){
26547             this.el.removeClass("x-btn-click");
26548             Roo.get(document).un('mouseup', this.onMouseUp, this);
26549         }
26550     },
26551     // private
26552     onMenuShow : function(e){
26553         this.el.addClass("x-btn-menu-active");
26554     },
26555     // private
26556     onMenuHide : function(e){
26557         this.el.removeClass("x-btn-menu-active");
26558     }   
26559 });
26560
26561 // Private utility class used by Button
26562 Roo.ButtonToggleMgr = function(){
26563    var groups = {};
26564    
26565    function toggleGroup(btn, state){
26566        if(state){
26567            var g = groups[btn.toggleGroup];
26568            for(var i = 0, l = g.length; i < l; i++){
26569                if(g[i] != btn){
26570                    g[i].toggle(false);
26571                }
26572            }
26573        }
26574    }
26575    
26576    return {
26577        register : function(btn){
26578            if(!btn.toggleGroup){
26579                return;
26580            }
26581            var g = groups[btn.toggleGroup];
26582            if(!g){
26583                g = groups[btn.toggleGroup] = [];
26584            }
26585            g.push(btn);
26586            btn.on("toggle", toggleGroup);
26587        },
26588        
26589        unregister : function(btn){
26590            if(!btn.toggleGroup){
26591                return;
26592            }
26593            var g = groups[btn.toggleGroup];
26594            if(g){
26595                g.remove(btn);
26596                btn.un("toggle", toggleGroup);
26597            }
26598        }
26599    };
26600 }();/*
26601  * Based on:
26602  * Ext JS Library 1.1.1
26603  * Copyright(c) 2006-2007, Ext JS, LLC.
26604  *
26605  * Originally Released Under LGPL - original licence link has changed is not relivant.
26606  *
26607  * Fork - LGPL
26608  * <script type="text/javascript">
26609  */
26610  
26611 /**
26612  * @class Roo.SplitButton
26613  * @extends Roo.Button
26614  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26615  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26616  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26617  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26618  * @cfg {String} arrowTooltip The title attribute of the arrow
26619  * @constructor
26620  * Create a new menu button
26621  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26622  * @param {Object} config The config object
26623  */
26624 Roo.SplitButton = function(renderTo, config){
26625     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26626     /**
26627      * @event arrowclick
26628      * Fires when this button's arrow is clicked
26629      * @param {SplitButton} this
26630      * @param {EventObject} e The click event
26631      */
26632     this.addEvents({"arrowclick":true});
26633 };
26634
26635 Roo.extend(Roo.SplitButton, Roo.Button, {
26636     render : function(renderTo){
26637         // this is one sweet looking template!
26638         var tpl = new Roo.Template(
26639             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26640             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26641             '<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>',
26642             "</tbody></table></td><td>",
26643             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26644             '<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>',
26645             "</tbody></table></td></tr></table>"
26646         );
26647         var btn = tpl.append(renderTo, [this.text, this.type], true);
26648         var btnEl = btn.child("button");
26649         if(this.cls){
26650             btn.addClass(this.cls);
26651         }
26652         if(this.icon){
26653             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26654         }
26655         if(this.iconCls){
26656             btnEl.addClass(this.iconCls);
26657             if(!this.cls){
26658                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26659             }
26660         }
26661         this.el = btn;
26662         if(this.handleMouseEvents){
26663             btn.on("mouseover", this.onMouseOver, this);
26664             btn.on("mouseout", this.onMouseOut, this);
26665             btn.on("mousedown", this.onMouseDown, this);
26666             btn.on("mouseup", this.onMouseUp, this);
26667         }
26668         btn.on(this.clickEvent, this.onClick, this);
26669         if(this.tooltip){
26670             if(typeof this.tooltip == 'object'){
26671                 Roo.QuickTips.tips(Roo.apply({
26672                       target: btnEl.id
26673                 }, this.tooltip));
26674             } else {
26675                 btnEl.dom[this.tooltipType] = this.tooltip;
26676             }
26677         }
26678         if(this.arrowTooltip){
26679             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26680         }
26681         if(this.hidden){
26682             this.hide();
26683         }
26684         if(this.disabled){
26685             this.disable();
26686         }
26687         if(this.pressed){
26688             this.el.addClass("x-btn-pressed");
26689         }
26690         if(Roo.isIE && !Roo.isIE7){
26691             this.autoWidth.defer(1, this);
26692         }else{
26693             this.autoWidth();
26694         }
26695         if(this.menu){
26696             this.menu.on("show", this.onMenuShow, this);
26697             this.menu.on("hide", this.onMenuHide, this);
26698         }
26699         this.fireEvent('render', this);
26700     },
26701
26702     // private
26703     autoWidth : function(){
26704         if(this.el){
26705             var tbl = this.el.child("table:first");
26706             var tbl2 = this.el.child("table:last");
26707             this.el.setWidth("auto");
26708             tbl.setWidth("auto");
26709             if(Roo.isIE7 && Roo.isStrict){
26710                 var ib = this.el.child('button:first');
26711                 if(ib && ib.getWidth() > 20){
26712                     ib.clip();
26713                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26714                 }
26715             }
26716             if(this.minWidth){
26717                 if(this.hidden){
26718                     this.el.beginMeasure();
26719                 }
26720                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26721                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26722                 }
26723                 if(this.hidden){
26724                     this.el.endMeasure();
26725                 }
26726             }
26727             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26728         } 
26729     },
26730     /**
26731      * Sets this button's click handler
26732      * @param {Function} handler The function to call when the button is clicked
26733      * @param {Object} scope (optional) Scope for the function passed above
26734      */
26735     setHandler : function(handler, scope){
26736         this.handler = handler;
26737         this.scope = scope;  
26738     },
26739     
26740     /**
26741      * Sets this button's arrow click handler
26742      * @param {Function} handler The function to call when the arrow is clicked
26743      * @param {Object} scope (optional) Scope for the function passed above
26744      */
26745     setArrowHandler : function(handler, scope){
26746         this.arrowHandler = handler;
26747         this.scope = scope;  
26748     },
26749     
26750     /**
26751      * Focus the button
26752      */
26753     focus : function(){
26754         if(this.el){
26755             this.el.child("button:first").focus();
26756         }
26757     },
26758
26759     // private
26760     onClick : function(e){
26761         e.preventDefault();
26762         if(!this.disabled){
26763             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26764                 if(this.menu && !this.menu.isVisible()){
26765                     this.menu.show(this.el, this.menuAlign);
26766                 }
26767                 this.fireEvent("arrowclick", this, e);
26768                 if(this.arrowHandler){
26769                     this.arrowHandler.call(this.scope || this, this, e);
26770                 }
26771             }else{
26772                 this.fireEvent("click", this, e);
26773                 if(this.handler){
26774                     this.handler.call(this.scope || this, this, e);
26775                 }
26776             }
26777         }
26778     },
26779     // private
26780     onMouseDown : function(e){
26781         if(!this.disabled){
26782             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26783         }
26784     },
26785     // private
26786     onMouseUp : function(e){
26787         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26788     }   
26789 });
26790
26791
26792 // backwards compat
26793 Roo.MenuButton = Roo.SplitButton;/*
26794  * Based on:
26795  * Ext JS Library 1.1.1
26796  * Copyright(c) 2006-2007, Ext JS, LLC.
26797  *
26798  * Originally Released Under LGPL - original licence link has changed is not relivant.
26799  *
26800  * Fork - LGPL
26801  * <script type="text/javascript">
26802  */
26803
26804 /**
26805  * @class Roo.Toolbar
26806  * Basic Toolbar class.
26807  * @constructor
26808  * Creates a new Toolbar
26809  * @param {Object} container The config object
26810  */ 
26811 Roo.Toolbar = function(container, buttons, config)
26812 {
26813     /// old consturctor format still supported..
26814     if(container instanceof Array){ // omit the container for later rendering
26815         buttons = container;
26816         config = buttons;
26817         container = null;
26818     }
26819     if (typeof(container) == 'object' && container.xtype) {
26820         config = container;
26821         container = config.container;
26822         buttons = config.buttons || []; // not really - use items!!
26823     }
26824     var xitems = [];
26825     if (config && config.items) {
26826         xitems = config.items;
26827         delete config.items;
26828     }
26829     Roo.apply(this, config);
26830     this.buttons = buttons;
26831     
26832     if(container){
26833         this.render(container);
26834     }
26835     this.xitems = xitems;
26836     Roo.each(xitems, function(b) {
26837         this.add(b);
26838     }, this);
26839     
26840 };
26841
26842 Roo.Toolbar.prototype = {
26843     /**
26844      * @cfg {Array} items
26845      * array of button configs or elements to add (will be converted to a MixedCollection)
26846      */
26847     
26848     /**
26849      * @cfg {String/HTMLElement/Element} container
26850      * The id or element that will contain the toolbar
26851      */
26852     // private
26853     render : function(ct){
26854         this.el = Roo.get(ct);
26855         if(this.cls){
26856             this.el.addClass(this.cls);
26857         }
26858         // using a table allows for vertical alignment
26859         // 100% width is needed by Safari...
26860         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26861         this.tr = this.el.child("tr", true);
26862         var autoId = 0;
26863         this.items = new Roo.util.MixedCollection(false, function(o){
26864             return o.id || ("item" + (++autoId));
26865         });
26866         if(this.buttons){
26867             this.add.apply(this, this.buttons);
26868             delete this.buttons;
26869         }
26870     },
26871
26872     /**
26873      * Adds element(s) to the toolbar -- this function takes a variable number of 
26874      * arguments of mixed type and adds them to the toolbar.
26875      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26876      * <ul>
26877      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26878      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26879      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26880      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26881      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26882      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26883      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26884      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26885      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26886      * </ul>
26887      * @param {Mixed} arg2
26888      * @param {Mixed} etc.
26889      */
26890     add : function(){
26891         var a = arguments, l = a.length;
26892         for(var i = 0; i < l; i++){
26893             this._add(a[i]);
26894         }
26895     },
26896     // private..
26897     _add : function(el) {
26898         
26899         if (el.xtype) {
26900             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26901         }
26902         
26903         if (el.applyTo){ // some kind of form field
26904             return this.addField(el);
26905         } 
26906         if (el.render){ // some kind of Toolbar.Item
26907             return this.addItem(el);
26908         }
26909         if (typeof el == "string"){ // string
26910             if(el == "separator" || el == "-"){
26911                 return this.addSeparator();
26912             }
26913             if (el == " "){
26914                 return this.addSpacer();
26915             }
26916             if(el == "->"){
26917                 return this.addFill();
26918             }
26919             return this.addText(el);
26920             
26921         }
26922         if(el.tagName){ // element
26923             return this.addElement(el);
26924         }
26925         if(typeof el == "object"){ // must be button config?
26926             return this.addButton(el);
26927         }
26928         // and now what?!?!
26929         return false;
26930         
26931     },
26932     
26933     /**
26934      * Add an Xtype element
26935      * @param {Object} xtype Xtype Object
26936      * @return {Object} created Object
26937      */
26938     addxtype : function(e){
26939         return this.add(e);  
26940     },
26941     
26942     /**
26943      * Returns the Element for this toolbar.
26944      * @return {Roo.Element}
26945      */
26946     getEl : function(){
26947         return this.el;  
26948     },
26949     
26950     /**
26951      * Adds a separator
26952      * @return {Roo.Toolbar.Item} The separator item
26953      */
26954     addSeparator : function(){
26955         return this.addItem(new Roo.Toolbar.Separator());
26956     },
26957
26958     /**
26959      * Adds a spacer element
26960      * @return {Roo.Toolbar.Spacer} The spacer item
26961      */
26962     addSpacer : function(){
26963         return this.addItem(new Roo.Toolbar.Spacer());
26964     },
26965
26966     /**
26967      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26968      * @return {Roo.Toolbar.Fill} The fill item
26969      */
26970     addFill : function(){
26971         return this.addItem(new Roo.Toolbar.Fill());
26972     },
26973
26974     /**
26975      * Adds any standard HTML element to the toolbar
26976      * @param {String/HTMLElement/Element} el The element or id of the element to add
26977      * @return {Roo.Toolbar.Item} The element's item
26978      */
26979     addElement : function(el){
26980         return this.addItem(new Roo.Toolbar.Item(el));
26981     },
26982     /**
26983      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26984      * @type Roo.util.MixedCollection  
26985      */
26986     items : false,
26987      
26988     /**
26989      * Adds any Toolbar.Item or subclass
26990      * @param {Roo.Toolbar.Item} item
26991      * @return {Roo.Toolbar.Item} The item
26992      */
26993     addItem : function(item){
26994         var td = this.nextBlock();
26995         item.render(td);
26996         this.items.add(item);
26997         return item;
26998     },
26999     
27000     /**
27001      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27002      * @param {Object/Array} config A button config or array of configs
27003      * @return {Roo.Toolbar.Button/Array}
27004      */
27005     addButton : function(config){
27006         if(config instanceof Array){
27007             var buttons = [];
27008             for(var i = 0, len = config.length; i < len; i++) {
27009                 buttons.push(this.addButton(config[i]));
27010             }
27011             return buttons;
27012         }
27013         var b = config;
27014         if(!(config instanceof Roo.Toolbar.Button)){
27015             b = config.split ?
27016                 new Roo.Toolbar.SplitButton(config) :
27017                 new Roo.Toolbar.Button(config);
27018         }
27019         var td = this.nextBlock();
27020         b.render(td);
27021         this.items.add(b);
27022         return b;
27023     },
27024     
27025     /**
27026      * Adds text to the toolbar
27027      * @param {String} text The text to add
27028      * @return {Roo.Toolbar.Item} The element's item
27029      */
27030     addText : function(text){
27031         return this.addItem(new Roo.Toolbar.TextItem(text));
27032     },
27033     
27034     /**
27035      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27036      * @param {Number} index The index where the item is to be inserted
27037      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27038      * @return {Roo.Toolbar.Button/Item}
27039      */
27040     insertButton : function(index, item){
27041         if(item instanceof Array){
27042             var buttons = [];
27043             for(var i = 0, len = item.length; i < len; i++) {
27044                buttons.push(this.insertButton(index + i, item[i]));
27045             }
27046             return buttons;
27047         }
27048         if (!(item instanceof Roo.Toolbar.Button)){
27049            item = new Roo.Toolbar.Button(item);
27050         }
27051         var td = document.createElement("td");
27052         this.tr.insertBefore(td, this.tr.childNodes[index]);
27053         item.render(td);
27054         this.items.insert(index, item);
27055         return item;
27056     },
27057     
27058     /**
27059      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27060      * @param {Object} config
27061      * @return {Roo.Toolbar.Item} The element's item
27062      */
27063     addDom : function(config, returnEl){
27064         var td = this.nextBlock();
27065         Roo.DomHelper.overwrite(td, config);
27066         var ti = new Roo.Toolbar.Item(td.firstChild);
27067         ti.render(td);
27068         this.items.add(ti);
27069         return ti;
27070     },
27071
27072     /**
27073      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27074      * @type Roo.util.MixedCollection  
27075      */
27076     fields : false,
27077     
27078     /**
27079      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27080      * Note: the field should not have been rendered yet. For a field that has already been
27081      * rendered, use {@link #addElement}.
27082      * @param {Roo.form.Field} field
27083      * @return {Roo.ToolbarItem}
27084      */
27085      
27086       
27087     addField : function(field) {
27088         if (!this.fields) {
27089             var autoId = 0;
27090             this.fields = new Roo.util.MixedCollection(false, function(o){
27091                 return o.id || ("item" + (++autoId));
27092             });
27093
27094         }
27095         
27096         var td = this.nextBlock();
27097         field.render(td);
27098         var ti = new Roo.Toolbar.Item(td.firstChild);
27099         ti.render(td);
27100         this.items.add(ti);
27101         this.fields.add(field);
27102         return ti;
27103     },
27104     /**
27105      * Hide the toolbar
27106      * @method hide
27107      */
27108      
27109       
27110     hide : function()
27111     {
27112         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27113         this.el.child('div').hide();
27114     },
27115     /**
27116      * Show the toolbar
27117      * @method show
27118      */
27119     show : function()
27120     {
27121         this.el.child('div').show();
27122     },
27123       
27124     // private
27125     nextBlock : function(){
27126         var td = document.createElement("td");
27127         this.tr.appendChild(td);
27128         return td;
27129     },
27130
27131     // private
27132     destroy : function(){
27133         if(this.items){ // rendered?
27134             Roo.destroy.apply(Roo, this.items.items);
27135         }
27136         if(this.fields){ // rendered?
27137             Roo.destroy.apply(Roo, this.fields.items);
27138         }
27139         Roo.Element.uncache(this.el, this.tr);
27140     }
27141 };
27142
27143 /**
27144  * @class Roo.Toolbar.Item
27145  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27146  * @constructor
27147  * Creates a new Item
27148  * @param {HTMLElement} el 
27149  */
27150 Roo.Toolbar.Item = function(el){
27151     this.el = Roo.getDom(el);
27152     this.id = Roo.id(this.el);
27153     this.hidden = false;
27154 };
27155
27156 Roo.Toolbar.Item.prototype = {
27157     
27158     /**
27159      * Get this item's HTML Element
27160      * @return {HTMLElement}
27161      */
27162     getEl : function(){
27163        return this.el;  
27164     },
27165
27166     // private
27167     render : function(td){
27168         this.td = td;
27169         td.appendChild(this.el);
27170     },
27171     
27172     /**
27173      * Removes and destroys this item.
27174      */
27175     destroy : function(){
27176         this.td.parentNode.removeChild(this.td);
27177     },
27178     
27179     /**
27180      * Shows this item.
27181      */
27182     show: function(){
27183         this.hidden = false;
27184         this.td.style.display = "";
27185     },
27186     
27187     /**
27188      * Hides this item.
27189      */
27190     hide: function(){
27191         this.hidden = true;
27192         this.td.style.display = "none";
27193     },
27194     
27195     /**
27196      * Convenience function for boolean show/hide.
27197      * @param {Boolean} visible true to show/false to hide
27198      */
27199     setVisible: function(visible){
27200         if(visible) {
27201             this.show();
27202         }else{
27203             this.hide();
27204         }
27205     },
27206     
27207     /**
27208      * Try to focus this item.
27209      */
27210     focus : function(){
27211         Roo.fly(this.el).focus();
27212     },
27213     
27214     /**
27215      * Disables this item.
27216      */
27217     disable : function(){
27218         Roo.fly(this.td).addClass("x-item-disabled");
27219         this.disabled = true;
27220         this.el.disabled = true;
27221     },
27222     
27223     /**
27224      * Enables this item.
27225      */
27226     enable : function(){
27227         Roo.fly(this.td).removeClass("x-item-disabled");
27228         this.disabled = false;
27229         this.el.disabled = false;
27230     }
27231 };
27232
27233
27234 /**
27235  * @class Roo.Toolbar.Separator
27236  * @extends Roo.Toolbar.Item
27237  * A simple toolbar separator class
27238  * @constructor
27239  * Creates a new Separator
27240  */
27241 Roo.Toolbar.Separator = function(){
27242     var s = document.createElement("span");
27243     s.className = "ytb-sep";
27244     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27245 };
27246 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27247     enable:Roo.emptyFn,
27248     disable:Roo.emptyFn,
27249     focus:Roo.emptyFn
27250 });
27251
27252 /**
27253  * @class Roo.Toolbar.Spacer
27254  * @extends Roo.Toolbar.Item
27255  * A simple element that adds extra horizontal space to a toolbar.
27256  * @constructor
27257  * Creates a new Spacer
27258  */
27259 Roo.Toolbar.Spacer = function(){
27260     var s = document.createElement("div");
27261     s.className = "ytb-spacer";
27262     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27263 };
27264 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27265     enable:Roo.emptyFn,
27266     disable:Roo.emptyFn,
27267     focus:Roo.emptyFn
27268 });
27269
27270 /**
27271  * @class Roo.Toolbar.Fill
27272  * @extends Roo.Toolbar.Spacer
27273  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27274  * @constructor
27275  * Creates a new Spacer
27276  */
27277 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27278     // private
27279     render : function(td){
27280         td.style.width = '100%';
27281         Roo.Toolbar.Fill.superclass.render.call(this, td);
27282     }
27283 });
27284
27285 /**
27286  * @class Roo.Toolbar.TextItem
27287  * @extends Roo.Toolbar.Item
27288  * A simple class that renders text directly into a toolbar.
27289  * @constructor
27290  * Creates a new TextItem
27291  * @param {String} text
27292  */
27293 Roo.Toolbar.TextItem = function(text){
27294     if (typeof(text) == 'object') {
27295         text = text.text;
27296     }
27297     var s = document.createElement("span");
27298     s.className = "ytb-text";
27299     s.innerHTML = text;
27300     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27301 };
27302 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27303     enable:Roo.emptyFn,
27304     disable:Roo.emptyFn,
27305     focus:Roo.emptyFn
27306 });
27307
27308 /**
27309  * @class Roo.Toolbar.Button
27310  * @extends Roo.Button
27311  * A button that renders into a toolbar.
27312  * @constructor
27313  * Creates a new Button
27314  * @param {Object} config A standard {@link Roo.Button} config object
27315  */
27316 Roo.Toolbar.Button = function(config){
27317     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27318 };
27319 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27320     render : function(td){
27321         this.td = td;
27322         Roo.Toolbar.Button.superclass.render.call(this, td);
27323     },
27324     
27325     /**
27326      * Removes and destroys this button
27327      */
27328     destroy : function(){
27329         Roo.Toolbar.Button.superclass.destroy.call(this);
27330         this.td.parentNode.removeChild(this.td);
27331     },
27332     
27333     /**
27334      * Shows this button
27335      */
27336     show: function(){
27337         this.hidden = false;
27338         this.td.style.display = "";
27339     },
27340     
27341     /**
27342      * Hides this button
27343      */
27344     hide: function(){
27345         this.hidden = true;
27346         this.td.style.display = "none";
27347     },
27348
27349     /**
27350      * Disables this item
27351      */
27352     disable : function(){
27353         Roo.fly(this.td).addClass("x-item-disabled");
27354         this.disabled = true;
27355     },
27356
27357     /**
27358      * Enables this item
27359      */
27360     enable : function(){
27361         Roo.fly(this.td).removeClass("x-item-disabled");
27362         this.disabled = false;
27363     }
27364 });
27365 // backwards compat
27366 Roo.ToolbarButton = Roo.Toolbar.Button;
27367
27368 /**
27369  * @class Roo.Toolbar.SplitButton
27370  * @extends Roo.SplitButton
27371  * A menu button that renders into a toolbar.
27372  * @constructor
27373  * Creates a new SplitButton
27374  * @param {Object} config A standard {@link Roo.SplitButton} config object
27375  */
27376 Roo.Toolbar.SplitButton = function(config){
27377     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27378 };
27379 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27380     render : function(td){
27381         this.td = td;
27382         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27383     },
27384     
27385     /**
27386      * Removes and destroys this button
27387      */
27388     destroy : function(){
27389         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27390         this.td.parentNode.removeChild(this.td);
27391     },
27392     
27393     /**
27394      * Shows this button
27395      */
27396     show: function(){
27397         this.hidden = false;
27398         this.td.style.display = "";
27399     },
27400     
27401     /**
27402      * Hides this button
27403      */
27404     hide: function(){
27405         this.hidden = true;
27406         this.td.style.display = "none";
27407     }
27408 });
27409
27410 // backwards compat
27411 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27412  * Based on:
27413  * Ext JS Library 1.1.1
27414  * Copyright(c) 2006-2007, Ext JS, LLC.
27415  *
27416  * Originally Released Under LGPL - original licence link has changed is not relivant.
27417  *
27418  * Fork - LGPL
27419  * <script type="text/javascript">
27420  */
27421  
27422 /**
27423  * @class Roo.PagingToolbar
27424  * @extends Roo.Toolbar
27425  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27426  * @constructor
27427  * Create a new PagingToolbar
27428  * @param {Object} config The config object
27429  */
27430 Roo.PagingToolbar = function(el, ds, config)
27431 {
27432     // old args format still supported... - xtype is prefered..
27433     if (typeof(el) == 'object' && el.xtype) {
27434         // created from xtype...
27435         config = el;
27436         ds = el.dataSource;
27437         el = config.container;
27438     }
27439     var items = [];
27440     if (config.items) {
27441         items = config.items;
27442         config.items = [];
27443     }
27444     
27445     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27446     this.ds = ds;
27447     this.cursor = 0;
27448     this.renderButtons(this.el);
27449     this.bind(ds);
27450     
27451     // supprot items array.
27452    
27453     Roo.each(items, function(e) {
27454         this.add(Roo.factory(e));
27455     },this);
27456     
27457 };
27458
27459 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27460     /**
27461      * @cfg {Roo.data.Store} dataSource
27462      * The underlying data store providing the paged data
27463      */
27464     /**
27465      * @cfg {String/HTMLElement/Element} container
27466      * container The id or element that will contain the toolbar
27467      */
27468     /**
27469      * @cfg {Boolean} displayInfo
27470      * True to display the displayMsg (defaults to false)
27471      */
27472     /**
27473      * @cfg {Number} pageSize
27474      * The number of records to display per page (defaults to 20)
27475      */
27476     pageSize: 20,
27477     /**
27478      * @cfg {String} displayMsg
27479      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27480      */
27481     displayMsg : 'Displaying {0} - {1} of {2}',
27482     /**
27483      * @cfg {String} emptyMsg
27484      * The message to display when no records are found (defaults to "No data to display")
27485      */
27486     emptyMsg : 'No data to display',
27487     /**
27488      * Customizable piece of the default paging text (defaults to "Page")
27489      * @type String
27490      */
27491     beforePageText : "Page",
27492     /**
27493      * Customizable piece of the default paging text (defaults to "of %0")
27494      * @type String
27495      */
27496     afterPageText : "of {0}",
27497     /**
27498      * Customizable piece of the default paging text (defaults to "First Page")
27499      * @type String
27500      */
27501     firstText : "First Page",
27502     /**
27503      * Customizable piece of the default paging text (defaults to "Previous Page")
27504      * @type String
27505      */
27506     prevText : "Previous Page",
27507     /**
27508      * Customizable piece of the default paging text (defaults to "Next Page")
27509      * @type String
27510      */
27511     nextText : "Next Page",
27512     /**
27513      * Customizable piece of the default paging text (defaults to "Last Page")
27514      * @type String
27515      */
27516     lastText : "Last Page",
27517     /**
27518      * Customizable piece of the default paging text (defaults to "Refresh")
27519      * @type String
27520      */
27521     refreshText : "Refresh",
27522
27523     // private
27524     renderButtons : function(el){
27525         Roo.PagingToolbar.superclass.render.call(this, el);
27526         this.first = this.addButton({
27527             tooltip: this.firstText,
27528             cls: "x-btn-icon x-grid-page-first",
27529             disabled: true,
27530             handler: this.onClick.createDelegate(this, ["first"])
27531         });
27532         this.prev = this.addButton({
27533             tooltip: this.prevText,
27534             cls: "x-btn-icon x-grid-page-prev",
27535             disabled: true,
27536             handler: this.onClick.createDelegate(this, ["prev"])
27537         });
27538         //this.addSeparator();
27539         this.add(this.beforePageText);
27540         this.field = Roo.get(this.addDom({
27541            tag: "input",
27542            type: "text",
27543            size: "3",
27544            value: "1",
27545            cls: "x-grid-page-number"
27546         }).el);
27547         this.field.on("keydown", this.onPagingKeydown, this);
27548         this.field.on("focus", function(){this.dom.select();});
27549         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27550         this.field.setHeight(18);
27551         //this.addSeparator();
27552         this.next = this.addButton({
27553             tooltip: this.nextText,
27554             cls: "x-btn-icon x-grid-page-next",
27555             disabled: true,
27556             handler: this.onClick.createDelegate(this, ["next"])
27557         });
27558         this.last = this.addButton({
27559             tooltip: this.lastText,
27560             cls: "x-btn-icon x-grid-page-last",
27561             disabled: true,
27562             handler: this.onClick.createDelegate(this, ["last"])
27563         });
27564         //this.addSeparator();
27565         this.loading = this.addButton({
27566             tooltip: this.refreshText,
27567             cls: "x-btn-icon x-grid-loading",
27568             handler: this.onClick.createDelegate(this, ["refresh"])
27569         });
27570
27571         if(this.displayInfo){
27572             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27573         }
27574     },
27575
27576     // private
27577     updateInfo : function(){
27578         if(this.displayEl){
27579             var count = this.ds.getCount();
27580             var msg = count == 0 ?
27581                 this.emptyMsg :
27582                 String.format(
27583                     this.displayMsg,
27584                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27585                 );
27586             this.displayEl.update(msg);
27587         }
27588     },
27589
27590     // private
27591     onLoad : function(ds, r, o){
27592        this.cursor = o.params ? o.params.start : 0;
27593        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27594
27595        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27596        this.field.dom.value = ap;
27597        this.first.setDisabled(ap == 1);
27598        this.prev.setDisabled(ap == 1);
27599        this.next.setDisabled(ap == ps);
27600        this.last.setDisabled(ap == ps);
27601        this.loading.enable();
27602        this.updateInfo();
27603     },
27604
27605     // private
27606     getPageData : function(){
27607         var total = this.ds.getTotalCount();
27608         return {
27609             total : total,
27610             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27611             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27612         };
27613     },
27614
27615     // private
27616     onLoadError : function(){
27617         this.loading.enable();
27618     },
27619
27620     // private
27621     onPagingKeydown : function(e){
27622         var k = e.getKey();
27623         var d = this.getPageData();
27624         if(k == e.RETURN){
27625             var v = this.field.dom.value, pageNum;
27626             if(!v || isNaN(pageNum = parseInt(v, 10))){
27627                 this.field.dom.value = d.activePage;
27628                 return;
27629             }
27630             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27631             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27632             e.stopEvent();
27633         }
27634         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))
27635         {
27636           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27637           this.field.dom.value = pageNum;
27638           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27639           e.stopEvent();
27640         }
27641         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27642         {
27643           var v = this.field.dom.value, pageNum; 
27644           var increment = (e.shiftKey) ? 10 : 1;
27645           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27646             increment *= -1;
27647           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27648             this.field.dom.value = d.activePage;
27649             return;
27650           }
27651           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27652           {
27653             this.field.dom.value = parseInt(v, 10) + increment;
27654             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27655             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27656           }
27657           e.stopEvent();
27658         }
27659     },
27660
27661     // private
27662     beforeLoad : function(){
27663         if(this.loading){
27664             this.loading.disable();
27665         }
27666     },
27667
27668     // private
27669     onClick : function(which){
27670         var ds = this.ds;
27671         switch(which){
27672             case "first":
27673                 ds.load({params:{start: 0, limit: this.pageSize}});
27674             break;
27675             case "prev":
27676                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27677             break;
27678             case "next":
27679                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27680             break;
27681             case "last":
27682                 var total = ds.getTotalCount();
27683                 var extra = total % this.pageSize;
27684                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27685                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27686             break;
27687             case "refresh":
27688                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27689             break;
27690         }
27691     },
27692
27693     /**
27694      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27695      * @param {Roo.data.Store} store The data store to unbind
27696      */
27697     unbind : function(ds){
27698         ds.un("beforeload", this.beforeLoad, this);
27699         ds.un("load", this.onLoad, this);
27700         ds.un("loadexception", this.onLoadError, this);
27701         ds.un("remove", this.updateInfo, this);
27702         ds.un("add", this.updateInfo, this);
27703         this.ds = undefined;
27704     },
27705
27706     /**
27707      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27708      * @param {Roo.data.Store} store The data store to bind
27709      */
27710     bind : function(ds){
27711         ds.on("beforeload", this.beforeLoad, this);
27712         ds.on("load", this.onLoad, this);
27713         ds.on("loadexception", this.onLoadError, this);
27714         ds.on("remove", this.updateInfo, this);
27715         ds.on("add", this.updateInfo, this);
27716         this.ds = ds;
27717     }
27718 });/*
27719  * Based on:
27720  * Ext JS Library 1.1.1
27721  * Copyright(c) 2006-2007, Ext JS, LLC.
27722  *
27723  * Originally Released Under LGPL - original licence link has changed is not relivant.
27724  *
27725  * Fork - LGPL
27726  * <script type="text/javascript">
27727  */
27728
27729 /**
27730  * @class Roo.Resizable
27731  * @extends Roo.util.Observable
27732  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27733  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27734  * 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
27735  * the element will be wrapped for you automatically.</p>
27736  * <p>Here is the list of valid resize handles:</p>
27737  * <pre>
27738 Value   Description
27739 ------  -------------------
27740  'n'     north
27741  's'     south
27742  'e'     east
27743  'w'     west
27744  'nw'    northwest
27745  'sw'    southwest
27746  'se'    southeast
27747  'ne'    northeast
27748  'hd'    horizontal drag
27749  'all'   all
27750 </pre>
27751  * <p>Here's an example showing the creation of a typical Resizable:</p>
27752  * <pre><code>
27753 var resizer = new Roo.Resizable("element-id", {
27754     handles: 'all',
27755     minWidth: 200,
27756     minHeight: 100,
27757     maxWidth: 500,
27758     maxHeight: 400,
27759     pinned: true
27760 });
27761 resizer.on("resize", myHandler);
27762 </code></pre>
27763  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27764  * resizer.east.setDisplayed(false);</p>
27765  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27766  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27767  * resize operation's new size (defaults to [0, 0])
27768  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27769  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27770  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27771  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27772  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27773  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27774  * @cfg {Number} width The width of the element in pixels (defaults to null)
27775  * @cfg {Number} height The height of the element in pixels (defaults to null)
27776  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27777  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27778  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27779  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27780  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27781  * in favor of the handles config option (defaults to false)
27782  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27783  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27784  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27785  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27786  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27787  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27788  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27789  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27790  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27791  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27792  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27793  * @constructor
27794  * Create a new resizable component
27795  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27796  * @param {Object} config configuration options
27797   */
27798 Roo.Resizable = function(el, config)
27799 {
27800     this.el = Roo.get(el);
27801
27802     if(config && config.wrap){
27803         config.resizeChild = this.el;
27804         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27805         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27806         this.el.setStyle("overflow", "hidden");
27807         this.el.setPositioning(config.resizeChild.getPositioning());
27808         config.resizeChild.clearPositioning();
27809         if(!config.width || !config.height){
27810             var csize = config.resizeChild.getSize();
27811             this.el.setSize(csize.width, csize.height);
27812         }
27813         if(config.pinned && !config.adjustments){
27814             config.adjustments = "auto";
27815         }
27816     }
27817
27818     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27819     this.proxy.unselectable();
27820     this.proxy.enableDisplayMode('block');
27821
27822     Roo.apply(this, config);
27823
27824     if(this.pinned){
27825         this.disableTrackOver = true;
27826         this.el.addClass("x-resizable-pinned");
27827     }
27828     // if the element isn't positioned, make it relative
27829     var position = this.el.getStyle("position");
27830     if(position != "absolute" && position != "fixed"){
27831         this.el.setStyle("position", "relative");
27832     }
27833     if(!this.handles){ // no handles passed, must be legacy style
27834         this.handles = 's,e,se';
27835         if(this.multiDirectional){
27836             this.handles += ',n,w';
27837         }
27838     }
27839     if(this.handles == "all"){
27840         this.handles = "n s e w ne nw se sw";
27841     }
27842     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27843     var ps = Roo.Resizable.positions;
27844     for(var i = 0, len = hs.length; i < len; i++){
27845         if(hs[i] && ps[hs[i]]){
27846             var pos = ps[hs[i]];
27847             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27848         }
27849     }
27850     // legacy
27851     this.corner = this.southeast;
27852     
27853     // updateBox = the box can move..
27854     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27855         this.updateBox = true;
27856     }
27857
27858     this.activeHandle = null;
27859
27860     if(this.resizeChild){
27861         if(typeof this.resizeChild == "boolean"){
27862             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27863         }else{
27864             this.resizeChild = Roo.get(this.resizeChild, true);
27865         }
27866     }
27867     
27868     if(this.adjustments == "auto"){
27869         var rc = this.resizeChild;
27870         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27871         if(rc && (hw || hn)){
27872             rc.position("relative");
27873             rc.setLeft(hw ? hw.el.getWidth() : 0);
27874             rc.setTop(hn ? hn.el.getHeight() : 0);
27875         }
27876         this.adjustments = [
27877             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27878             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27879         ];
27880     }
27881
27882     if(this.draggable){
27883         this.dd = this.dynamic ?
27884             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27885         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27886     }
27887
27888     // public events
27889     this.addEvents({
27890         /**
27891          * @event beforeresize
27892          * Fired before resize is allowed. Set enabled to false to cancel resize.
27893          * @param {Roo.Resizable} this
27894          * @param {Roo.EventObject} e The mousedown event
27895          */
27896         "beforeresize" : true,
27897         /**
27898          * @event resize
27899          * Fired after a resize.
27900          * @param {Roo.Resizable} this
27901          * @param {Number} width The new width
27902          * @param {Number} height The new height
27903          * @param {Roo.EventObject} e The mouseup event
27904          */
27905         "resize" : true
27906     });
27907
27908     if(this.width !== null && this.height !== null){
27909         this.resizeTo(this.width, this.height);
27910     }else{
27911         this.updateChildSize();
27912     }
27913     if(Roo.isIE){
27914         this.el.dom.style.zoom = 1;
27915     }
27916     Roo.Resizable.superclass.constructor.call(this);
27917 };
27918
27919 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27920         resizeChild : false,
27921         adjustments : [0, 0],
27922         minWidth : 5,
27923         minHeight : 5,
27924         maxWidth : 10000,
27925         maxHeight : 10000,
27926         enabled : true,
27927         animate : false,
27928         duration : .35,
27929         dynamic : false,
27930         handles : false,
27931         multiDirectional : false,
27932         disableTrackOver : false,
27933         easing : 'easeOutStrong',
27934         widthIncrement : 0,
27935         heightIncrement : 0,
27936         pinned : false,
27937         width : null,
27938         height : null,
27939         preserveRatio : false,
27940         transparent: false,
27941         minX: 0,
27942         minY: 0,
27943         draggable: false,
27944
27945         /**
27946          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27947          */
27948         constrainTo: undefined,
27949         /**
27950          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27951          */
27952         resizeRegion: undefined,
27953
27954
27955     /**
27956      * Perform a manual resize
27957      * @param {Number} width
27958      * @param {Number} height
27959      */
27960     resizeTo : function(width, height){
27961         this.el.setSize(width, height);
27962         this.updateChildSize();
27963         this.fireEvent("resize", this, width, height, null);
27964     },
27965
27966     // private
27967     startSizing : function(e, handle){
27968         this.fireEvent("beforeresize", this, e);
27969         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27970
27971             if(!this.overlay){
27972                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27973                 this.overlay.unselectable();
27974                 this.overlay.enableDisplayMode("block");
27975                 this.overlay.on("mousemove", this.onMouseMove, this);
27976                 this.overlay.on("mouseup", this.onMouseUp, this);
27977             }
27978             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27979
27980             this.resizing = true;
27981             this.startBox = this.el.getBox();
27982             this.startPoint = e.getXY();
27983             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27984                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27985
27986             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27987             this.overlay.show();
27988
27989             if(this.constrainTo) {
27990                 var ct = Roo.get(this.constrainTo);
27991                 this.resizeRegion = ct.getRegion().adjust(
27992                     ct.getFrameWidth('t'),
27993                     ct.getFrameWidth('l'),
27994                     -ct.getFrameWidth('b'),
27995                     -ct.getFrameWidth('r')
27996                 );
27997             }
27998
27999             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28000             this.proxy.show();
28001             this.proxy.setBox(this.startBox);
28002             if(!this.dynamic){
28003                 this.proxy.setStyle('visibility', 'visible');
28004             }
28005         }
28006     },
28007
28008     // private
28009     onMouseDown : function(handle, e){
28010         if(this.enabled){
28011             e.stopEvent();
28012             this.activeHandle = handle;
28013             this.startSizing(e, handle);
28014         }
28015     },
28016
28017     // private
28018     onMouseUp : function(e){
28019         var size = this.resizeElement();
28020         this.resizing = false;
28021         this.handleOut();
28022         this.overlay.hide();
28023         this.proxy.hide();
28024         this.fireEvent("resize", this, size.width, size.height, e);
28025     },
28026
28027     // private
28028     updateChildSize : function(){
28029         if(this.resizeChild){
28030             var el = this.el;
28031             var child = this.resizeChild;
28032             var adj = this.adjustments;
28033             if(el.dom.offsetWidth){
28034                 var b = el.getSize(true);
28035                 child.setSize(b.width+adj[0], b.height+adj[1]);
28036             }
28037             // Second call here for IE
28038             // The first call enables instant resizing and
28039             // the second call corrects scroll bars if they
28040             // exist
28041             if(Roo.isIE){
28042                 setTimeout(function(){
28043                     if(el.dom.offsetWidth){
28044                         var b = el.getSize(true);
28045                         child.setSize(b.width+adj[0], b.height+adj[1]);
28046                     }
28047                 }, 10);
28048             }
28049         }
28050     },
28051
28052     // private
28053     snap : function(value, inc, min){
28054         if(!inc || !value) return value;
28055         var newValue = value;
28056         var m = value % inc;
28057         if(m > 0){
28058             if(m > (inc/2)){
28059                 newValue = value + (inc-m);
28060             }else{
28061                 newValue = value - m;
28062             }
28063         }
28064         return Math.max(min, newValue);
28065     },
28066
28067     // private
28068     resizeElement : function(){
28069         var box = this.proxy.getBox();
28070         if(this.updateBox){
28071             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28072         }else{
28073             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28074         }
28075         this.updateChildSize();
28076         if(!this.dynamic){
28077             this.proxy.hide();
28078         }
28079         return box;
28080     },
28081
28082     // private
28083     constrain : function(v, diff, m, mx){
28084         if(v - diff < m){
28085             diff = v - m;
28086         }else if(v - diff > mx){
28087             diff = mx - v;
28088         }
28089         return diff;
28090     },
28091
28092     // private
28093     onMouseMove : function(e){
28094         if(this.enabled){
28095             try{// try catch so if something goes wrong the user doesn't get hung
28096
28097             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28098                 return;
28099             }
28100
28101             //var curXY = this.startPoint;
28102             var curSize = this.curSize || this.startBox;
28103             var x = this.startBox.x, y = this.startBox.y;
28104             var ox = x, oy = y;
28105             var w = curSize.width, h = curSize.height;
28106             var ow = w, oh = h;
28107             var mw = this.minWidth, mh = this.minHeight;
28108             var mxw = this.maxWidth, mxh = this.maxHeight;
28109             var wi = this.widthIncrement;
28110             var hi = this.heightIncrement;
28111
28112             var eventXY = e.getXY();
28113             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28114             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28115
28116             var pos = this.activeHandle.position;
28117
28118             switch(pos){
28119                 case "east":
28120                     w += diffX;
28121                     w = Math.min(Math.max(mw, w), mxw);
28122                     break;
28123              
28124                 case "south":
28125                     h += diffY;
28126                     h = Math.min(Math.max(mh, h), mxh);
28127                     break;
28128                 case "southeast":
28129                     w += diffX;
28130                     h += diffY;
28131                     w = Math.min(Math.max(mw, w), mxw);
28132                     h = Math.min(Math.max(mh, h), mxh);
28133                     break;
28134                 case "north":
28135                     diffY = this.constrain(h, diffY, mh, mxh);
28136                     y += diffY;
28137                     h -= diffY;
28138                     break;
28139                 case "hdrag":
28140                     
28141                     if (wi) {
28142                         var adiffX = Math.abs(diffX);
28143                         var sub = (adiffX % wi); // how much 
28144                         if (sub > (wi/2)) { // far enough to snap
28145                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28146                         } else {
28147                             // remove difference.. 
28148                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28149                         }
28150                     }
28151                     x += diffX;
28152                     x = Math.max(this.minX, x);
28153                     break;
28154                 case "west":
28155                     diffX = this.constrain(w, diffX, mw, mxw);
28156                     x += diffX;
28157                     w -= diffX;
28158                     break;
28159                 case "northeast":
28160                     w += diffX;
28161                     w = Math.min(Math.max(mw, w), mxw);
28162                     diffY = this.constrain(h, diffY, mh, mxh);
28163                     y += diffY;
28164                     h -= diffY;
28165                     break;
28166                 case "northwest":
28167                     diffX = this.constrain(w, diffX, mw, mxw);
28168                     diffY = this.constrain(h, diffY, mh, mxh);
28169                     y += diffY;
28170                     h -= diffY;
28171                     x += diffX;
28172                     w -= diffX;
28173                     break;
28174                case "southwest":
28175                     diffX = this.constrain(w, diffX, mw, mxw);
28176                     h += diffY;
28177                     h = Math.min(Math.max(mh, h), mxh);
28178                     x += diffX;
28179                     w -= diffX;
28180                     break;
28181             }
28182
28183             var sw = this.snap(w, wi, mw);
28184             var sh = this.snap(h, hi, mh);
28185             if(sw != w || sh != h){
28186                 switch(pos){
28187                     case "northeast":
28188                         y -= sh - h;
28189                     break;
28190                     case "north":
28191                         y -= sh - h;
28192                         break;
28193                     case "southwest":
28194                         x -= sw - w;
28195                     break;
28196                     case "west":
28197                         x -= sw - w;
28198                         break;
28199                     case "northwest":
28200                         x -= sw - w;
28201                         y -= sh - h;
28202                     break;
28203                 }
28204                 w = sw;
28205                 h = sh;
28206             }
28207
28208             if(this.preserveRatio){
28209                 switch(pos){
28210                     case "southeast":
28211                     case "east":
28212                         h = oh * (w/ow);
28213                         h = Math.min(Math.max(mh, h), mxh);
28214                         w = ow * (h/oh);
28215                        break;
28216                     case "south":
28217                         w = ow * (h/oh);
28218                         w = Math.min(Math.max(mw, w), mxw);
28219                         h = oh * (w/ow);
28220                         break;
28221                     case "northeast":
28222                         w = ow * (h/oh);
28223                         w = Math.min(Math.max(mw, w), mxw);
28224                         h = oh * (w/ow);
28225                     break;
28226                     case "north":
28227                         var tw = w;
28228                         w = ow * (h/oh);
28229                         w = Math.min(Math.max(mw, w), mxw);
28230                         h = oh * (w/ow);
28231                         x += (tw - w) / 2;
28232                         break;
28233                     case "southwest":
28234                         h = oh * (w/ow);
28235                         h = Math.min(Math.max(mh, h), mxh);
28236                         var tw = w;
28237                         w = ow * (h/oh);
28238                         x += tw - w;
28239                         break;
28240                     case "west":
28241                         var th = h;
28242                         h = oh * (w/ow);
28243                         h = Math.min(Math.max(mh, h), mxh);
28244                         y += (th - h) / 2;
28245                         var tw = w;
28246                         w = ow * (h/oh);
28247                         x += tw - w;
28248                        break;
28249                     case "northwest":
28250                         var tw = w;
28251                         var th = h;
28252                         h = oh * (w/ow);
28253                         h = Math.min(Math.max(mh, h), mxh);
28254                         w = ow * (h/oh);
28255                         y += th - h;
28256                         x += tw - w;
28257                        break;
28258
28259                 }
28260             }
28261             if (pos == 'hdrag') {
28262                 w = ow;
28263             }
28264             this.proxy.setBounds(x, y, w, h);
28265             if(this.dynamic){
28266                 this.resizeElement();
28267             }
28268             }catch(e){}
28269         }
28270     },
28271
28272     // private
28273     handleOver : function(){
28274         if(this.enabled){
28275             this.el.addClass("x-resizable-over");
28276         }
28277     },
28278
28279     // private
28280     handleOut : function(){
28281         if(!this.resizing){
28282             this.el.removeClass("x-resizable-over");
28283         }
28284     },
28285
28286     /**
28287      * Returns the element this component is bound to.
28288      * @return {Roo.Element}
28289      */
28290     getEl : function(){
28291         return this.el;
28292     },
28293
28294     /**
28295      * Returns the resizeChild element (or null).
28296      * @return {Roo.Element}
28297      */
28298     getResizeChild : function(){
28299         return this.resizeChild;
28300     },
28301
28302     /**
28303      * Destroys this resizable. If the element was wrapped and
28304      * removeEl is not true then the element remains.
28305      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28306      */
28307     destroy : function(removeEl){
28308         this.proxy.remove();
28309         if(this.overlay){
28310             this.overlay.removeAllListeners();
28311             this.overlay.remove();
28312         }
28313         var ps = Roo.Resizable.positions;
28314         for(var k in ps){
28315             if(typeof ps[k] != "function" && this[ps[k]]){
28316                 var h = this[ps[k]];
28317                 h.el.removeAllListeners();
28318                 h.el.remove();
28319             }
28320         }
28321         if(removeEl){
28322             this.el.update("");
28323             this.el.remove();
28324         }
28325     }
28326 });
28327
28328 // private
28329 // hash to map config positions to true positions
28330 Roo.Resizable.positions = {
28331     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28332     hd: "hdrag"
28333 };
28334
28335 // private
28336 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28337     if(!this.tpl){
28338         // only initialize the template if resizable is used
28339         var tpl = Roo.DomHelper.createTemplate(
28340             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28341         );
28342         tpl.compile();
28343         Roo.Resizable.Handle.prototype.tpl = tpl;
28344     }
28345     this.position = pos;
28346     this.rz = rz;
28347     // show north drag fro topdra
28348     var handlepos = pos == 'hdrag' ? 'north' : pos;
28349     
28350     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28351     if (pos == 'hdrag') {
28352         this.el.setStyle('cursor', 'pointer');
28353     }
28354     this.el.unselectable();
28355     if(transparent){
28356         this.el.setOpacity(0);
28357     }
28358     this.el.on("mousedown", this.onMouseDown, this);
28359     if(!disableTrackOver){
28360         this.el.on("mouseover", this.onMouseOver, this);
28361         this.el.on("mouseout", this.onMouseOut, this);
28362     }
28363 };
28364
28365 // private
28366 Roo.Resizable.Handle.prototype = {
28367     afterResize : function(rz){
28368         // do nothing
28369     },
28370     // private
28371     onMouseDown : function(e){
28372         this.rz.onMouseDown(this, e);
28373     },
28374     // private
28375     onMouseOver : function(e){
28376         this.rz.handleOver(this, e);
28377     },
28378     // private
28379     onMouseOut : function(e){
28380         this.rz.handleOut(this, e);
28381     }
28382 };/*
28383  * Based on:
28384  * Ext JS Library 1.1.1
28385  * Copyright(c) 2006-2007, Ext JS, LLC.
28386  *
28387  * Originally Released Under LGPL - original licence link has changed is not relivant.
28388  *
28389  * Fork - LGPL
28390  * <script type="text/javascript">
28391  */
28392
28393 /**
28394  * @class Roo.Editor
28395  * @extends Roo.Component
28396  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28397  * @constructor
28398  * Create a new Editor
28399  * @param {Roo.form.Field} field The Field object (or descendant)
28400  * @param {Object} config The config object
28401  */
28402 Roo.Editor = function(field, config){
28403     Roo.Editor.superclass.constructor.call(this, config);
28404     this.field = field;
28405     this.addEvents({
28406         /**
28407              * @event beforestartedit
28408              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28409              * false from the handler of this event.
28410              * @param {Editor} this
28411              * @param {Roo.Element} boundEl The underlying element bound to this editor
28412              * @param {Mixed} value The field value being set
28413              */
28414         "beforestartedit" : true,
28415         /**
28416              * @event startedit
28417              * Fires when this editor is displayed
28418              * @param {Roo.Element} boundEl The underlying element bound to this editor
28419              * @param {Mixed} value The starting field value
28420              */
28421         "startedit" : true,
28422         /**
28423              * @event beforecomplete
28424              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28425              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28426              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28427              * event will not fire since no edit actually occurred.
28428              * @param {Editor} this
28429              * @param {Mixed} value The current field value
28430              * @param {Mixed} startValue The original field value
28431              */
28432         "beforecomplete" : true,
28433         /**
28434              * @event complete
28435              * Fires after editing is complete and any changed value has been written to the underlying field.
28436              * @param {Editor} this
28437              * @param {Mixed} value The current field value
28438              * @param {Mixed} startValue The original field value
28439              */
28440         "complete" : true,
28441         /**
28442          * @event specialkey
28443          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28444          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28445          * @param {Roo.form.Field} this
28446          * @param {Roo.EventObject} e The event object
28447          */
28448         "specialkey" : true
28449     });
28450 };
28451
28452 Roo.extend(Roo.Editor, Roo.Component, {
28453     /**
28454      * @cfg {Boolean/String} autosize
28455      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28456      * or "height" to adopt the height only (defaults to false)
28457      */
28458     /**
28459      * @cfg {Boolean} revertInvalid
28460      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28461      * validation fails (defaults to true)
28462      */
28463     /**
28464      * @cfg {Boolean} ignoreNoChange
28465      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28466      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28467      * will never be ignored.
28468      */
28469     /**
28470      * @cfg {Boolean} hideEl
28471      * False to keep the bound element visible while the editor is displayed (defaults to true)
28472      */
28473     /**
28474      * @cfg {Mixed} value
28475      * The data value of the underlying field (defaults to "")
28476      */
28477     value : "",
28478     /**
28479      * @cfg {String} alignment
28480      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28481      */
28482     alignment: "c-c?",
28483     /**
28484      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28485      * for bottom-right shadow (defaults to "frame")
28486      */
28487     shadow : "frame",
28488     /**
28489      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28490      */
28491     constrain : false,
28492     /**
28493      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28494      */
28495     completeOnEnter : false,
28496     /**
28497      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28498      */
28499     cancelOnEsc : false,
28500     /**
28501      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28502      */
28503     updateEl : false,
28504
28505     // private
28506     onRender : function(ct, position){
28507         this.el = new Roo.Layer({
28508             shadow: this.shadow,
28509             cls: "x-editor",
28510             parentEl : ct,
28511             shim : this.shim,
28512             shadowOffset:4,
28513             id: this.id,
28514             constrain: this.constrain
28515         });
28516         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28517         if(this.field.msgTarget != 'title'){
28518             this.field.msgTarget = 'qtip';
28519         }
28520         this.field.render(this.el);
28521         if(Roo.isGecko){
28522             this.field.el.dom.setAttribute('autocomplete', 'off');
28523         }
28524         this.field.on("specialkey", this.onSpecialKey, this);
28525         if(this.swallowKeys){
28526             this.field.el.swallowEvent(['keydown','keypress']);
28527         }
28528         this.field.show();
28529         this.field.on("blur", this.onBlur, this);
28530         if(this.field.grow){
28531             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28532         }
28533     },
28534
28535     onSpecialKey : function(field, e)
28536     {
28537         //Roo.log('editor onSpecialKey');
28538         if(this.completeOnEnter && e.getKey() == e.ENTER){
28539             e.stopEvent();
28540             this.completeEdit();
28541             return;
28542         }
28543         // do not fire special key otherwise it might hide close the editor...
28544         if(e.getKey() == e.ENTER){    
28545             return;
28546         }
28547         if(this.cancelOnEsc && e.getKey() == e.ESC){
28548             this.cancelEdit();
28549             return;
28550         } 
28551         this.fireEvent('specialkey', field, e);
28552     
28553     },
28554
28555     /**
28556      * Starts the editing process and shows the editor.
28557      * @param {String/HTMLElement/Element} el The element to edit
28558      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28559       * to the innerHTML of el.
28560      */
28561     startEdit : function(el, value){
28562         if(this.editing){
28563             this.completeEdit();
28564         }
28565         this.boundEl = Roo.get(el);
28566         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28567         if(!this.rendered){
28568             this.render(this.parentEl || document.body);
28569         }
28570         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28571             return;
28572         }
28573         this.startValue = v;
28574         this.field.setValue(v);
28575         if(this.autoSize){
28576             var sz = this.boundEl.getSize();
28577             switch(this.autoSize){
28578                 case "width":
28579                 this.setSize(sz.width,  "");
28580                 break;
28581                 case "height":
28582                 this.setSize("",  sz.height);
28583                 break;
28584                 default:
28585                 this.setSize(sz.width,  sz.height);
28586             }
28587         }
28588         this.el.alignTo(this.boundEl, this.alignment);
28589         this.editing = true;
28590         if(Roo.QuickTips){
28591             Roo.QuickTips.disable();
28592         }
28593         this.show();
28594     },
28595
28596     /**
28597      * Sets the height and width of this editor.
28598      * @param {Number} width The new width
28599      * @param {Number} height The new height
28600      */
28601     setSize : function(w, h){
28602         this.field.setSize(w, h);
28603         if(this.el){
28604             this.el.sync();
28605         }
28606     },
28607
28608     /**
28609      * Realigns the editor to the bound field based on the current alignment config value.
28610      */
28611     realign : function(){
28612         this.el.alignTo(this.boundEl, this.alignment);
28613     },
28614
28615     /**
28616      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28617      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28618      */
28619     completeEdit : function(remainVisible){
28620         if(!this.editing){
28621             return;
28622         }
28623         var v = this.getValue();
28624         if(this.revertInvalid !== false && !this.field.isValid()){
28625             v = this.startValue;
28626             this.cancelEdit(true);
28627         }
28628         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28629             this.editing = false;
28630             this.hide();
28631             return;
28632         }
28633         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28634             this.editing = false;
28635             if(this.updateEl && this.boundEl){
28636                 this.boundEl.update(v);
28637             }
28638             if(remainVisible !== true){
28639                 this.hide();
28640             }
28641             this.fireEvent("complete", this, v, this.startValue);
28642         }
28643     },
28644
28645     // private
28646     onShow : function(){
28647         this.el.show();
28648         if(this.hideEl !== false){
28649             this.boundEl.hide();
28650         }
28651         this.field.show();
28652         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28653             this.fixIEFocus = true;
28654             this.deferredFocus.defer(50, this);
28655         }else{
28656             this.field.focus();
28657         }
28658         this.fireEvent("startedit", this.boundEl, this.startValue);
28659     },
28660
28661     deferredFocus : function(){
28662         if(this.editing){
28663             this.field.focus();
28664         }
28665     },
28666
28667     /**
28668      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28669      * reverted to the original starting value.
28670      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28671      * cancel (defaults to false)
28672      */
28673     cancelEdit : function(remainVisible){
28674         if(this.editing){
28675             this.setValue(this.startValue);
28676             if(remainVisible !== true){
28677                 this.hide();
28678             }
28679         }
28680     },
28681
28682     // private
28683     onBlur : function(){
28684         if(this.allowBlur !== true && this.editing){
28685             this.completeEdit();
28686         }
28687     },
28688
28689     // private
28690     onHide : function(){
28691         if(this.editing){
28692             this.completeEdit();
28693             return;
28694         }
28695         this.field.blur();
28696         if(this.field.collapse){
28697             this.field.collapse();
28698         }
28699         this.el.hide();
28700         if(this.hideEl !== false){
28701             this.boundEl.show();
28702         }
28703         if(Roo.QuickTips){
28704             Roo.QuickTips.enable();
28705         }
28706     },
28707
28708     /**
28709      * Sets the data value of the editor
28710      * @param {Mixed} value Any valid value supported by the underlying field
28711      */
28712     setValue : function(v){
28713         this.field.setValue(v);
28714     },
28715
28716     /**
28717      * Gets the data value of the editor
28718      * @return {Mixed} The data value
28719      */
28720     getValue : function(){
28721         return this.field.getValue();
28722     }
28723 });/*
28724  * Based on:
28725  * Ext JS Library 1.1.1
28726  * Copyright(c) 2006-2007, Ext JS, LLC.
28727  *
28728  * Originally Released Under LGPL - original licence link has changed is not relivant.
28729  *
28730  * Fork - LGPL
28731  * <script type="text/javascript">
28732  */
28733  
28734 /**
28735  * @class Roo.BasicDialog
28736  * @extends Roo.util.Observable
28737  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28738  * <pre><code>
28739 var dlg = new Roo.BasicDialog("my-dlg", {
28740     height: 200,
28741     width: 300,
28742     minHeight: 100,
28743     minWidth: 150,
28744     modal: true,
28745     proxyDrag: true,
28746     shadow: true
28747 });
28748 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28749 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28750 dlg.addButton('Cancel', dlg.hide, dlg);
28751 dlg.show();
28752 </code></pre>
28753   <b>A Dialog should always be a direct child of the body element.</b>
28754  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28755  * @cfg {String} title Default text to display in the title bar (defaults to null)
28756  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28757  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28758  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28759  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28760  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28761  * (defaults to null with no animation)
28762  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28763  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28764  * property for valid values (defaults to 'all')
28765  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28766  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28767  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28768  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28769  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28770  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28771  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28772  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28773  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28774  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28775  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28776  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28777  * draggable = true (defaults to false)
28778  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28779  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28780  * shadow (defaults to false)
28781  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28782  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28783  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28784  * @cfg {Array} buttons Array of buttons
28785  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28786  * @constructor
28787  * Create a new BasicDialog.
28788  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28789  * @param {Object} config Configuration options
28790  */
28791 Roo.BasicDialog = function(el, config){
28792     this.el = Roo.get(el);
28793     var dh = Roo.DomHelper;
28794     if(!this.el && config && config.autoCreate){
28795         if(typeof config.autoCreate == "object"){
28796             if(!config.autoCreate.id){
28797                 config.autoCreate.id = el;
28798             }
28799             this.el = dh.append(document.body,
28800                         config.autoCreate, true);
28801         }else{
28802             this.el = dh.append(document.body,
28803                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28804         }
28805     }
28806     el = this.el;
28807     el.setDisplayed(true);
28808     el.hide = this.hideAction;
28809     this.id = el.id;
28810     el.addClass("x-dlg");
28811
28812     Roo.apply(this, config);
28813
28814     this.proxy = el.createProxy("x-dlg-proxy");
28815     this.proxy.hide = this.hideAction;
28816     this.proxy.setOpacity(.5);
28817     this.proxy.hide();
28818
28819     if(config.width){
28820         el.setWidth(config.width);
28821     }
28822     if(config.height){
28823         el.setHeight(config.height);
28824     }
28825     this.size = el.getSize();
28826     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28827         this.xy = [config.x,config.y];
28828     }else{
28829         this.xy = el.getCenterXY(true);
28830     }
28831     /** The header element @type Roo.Element */
28832     this.header = el.child("> .x-dlg-hd");
28833     /** The body element @type Roo.Element */
28834     this.body = el.child("> .x-dlg-bd");
28835     /** The footer element @type Roo.Element */
28836     this.footer = el.child("> .x-dlg-ft");
28837
28838     if(!this.header){
28839         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28840     }
28841     if(!this.body){
28842         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28843     }
28844
28845     this.header.unselectable();
28846     if(this.title){
28847         this.header.update(this.title);
28848     }
28849     // this element allows the dialog to be focused for keyboard event
28850     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28851     this.focusEl.swallowEvent("click", true);
28852
28853     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28854
28855     // wrap the body and footer for special rendering
28856     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28857     if(this.footer){
28858         this.bwrap.dom.appendChild(this.footer.dom);
28859     }
28860
28861     this.bg = this.el.createChild({
28862         tag: "div", cls:"x-dlg-bg",
28863         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28864     });
28865     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28866
28867
28868     if(this.autoScroll !== false && !this.autoTabs){
28869         this.body.setStyle("overflow", "auto");
28870     }
28871
28872     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28873
28874     if(this.closable !== false){
28875         this.el.addClass("x-dlg-closable");
28876         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28877         this.close.on("click", this.closeClick, this);
28878         this.close.addClassOnOver("x-dlg-close-over");
28879     }
28880     if(this.collapsible !== false){
28881         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28882         this.collapseBtn.on("click", this.collapseClick, this);
28883         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28884         this.header.on("dblclick", this.collapseClick, this);
28885     }
28886     if(this.resizable !== false){
28887         this.el.addClass("x-dlg-resizable");
28888         this.resizer = new Roo.Resizable(el, {
28889             minWidth: this.minWidth || 80,
28890             minHeight:this.minHeight || 80,
28891             handles: this.resizeHandles || "all",
28892             pinned: true
28893         });
28894         this.resizer.on("beforeresize", this.beforeResize, this);
28895         this.resizer.on("resize", this.onResize, this);
28896     }
28897     if(this.draggable !== false){
28898         el.addClass("x-dlg-draggable");
28899         if (!this.proxyDrag) {
28900             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28901         }
28902         else {
28903             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28904         }
28905         dd.setHandleElId(this.header.id);
28906         dd.endDrag = this.endMove.createDelegate(this);
28907         dd.startDrag = this.startMove.createDelegate(this);
28908         dd.onDrag = this.onDrag.createDelegate(this);
28909         dd.scroll = false;
28910         this.dd = dd;
28911     }
28912     if(this.modal){
28913         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28914         this.mask.enableDisplayMode("block");
28915         this.mask.hide();
28916         this.el.addClass("x-dlg-modal");
28917     }
28918     if(this.shadow){
28919         this.shadow = new Roo.Shadow({
28920             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28921             offset : this.shadowOffset
28922         });
28923     }else{
28924         this.shadowOffset = 0;
28925     }
28926     if(Roo.useShims && this.shim !== false){
28927         this.shim = this.el.createShim();
28928         this.shim.hide = this.hideAction;
28929         this.shim.hide();
28930     }else{
28931         this.shim = false;
28932     }
28933     if(this.autoTabs){
28934         this.initTabs();
28935     }
28936     if (this.buttons) { 
28937         var bts= this.buttons;
28938         this.buttons = [];
28939         Roo.each(bts, function(b) {
28940             this.addButton(b);
28941         }, this);
28942     }
28943     
28944     
28945     this.addEvents({
28946         /**
28947          * @event keydown
28948          * Fires when a key is pressed
28949          * @param {Roo.BasicDialog} this
28950          * @param {Roo.EventObject} e
28951          */
28952         "keydown" : true,
28953         /**
28954          * @event move
28955          * Fires when this dialog is moved by the user.
28956          * @param {Roo.BasicDialog} this
28957          * @param {Number} x The new page X
28958          * @param {Number} y The new page Y
28959          */
28960         "move" : true,
28961         /**
28962          * @event resize
28963          * Fires when this dialog is resized by the user.
28964          * @param {Roo.BasicDialog} this
28965          * @param {Number} width The new width
28966          * @param {Number} height The new height
28967          */
28968         "resize" : true,
28969         /**
28970          * @event beforehide
28971          * Fires before this dialog is hidden.
28972          * @param {Roo.BasicDialog} this
28973          */
28974         "beforehide" : true,
28975         /**
28976          * @event hide
28977          * Fires when this dialog is hidden.
28978          * @param {Roo.BasicDialog} this
28979          */
28980         "hide" : true,
28981         /**
28982          * @event beforeshow
28983          * Fires before this dialog is shown.
28984          * @param {Roo.BasicDialog} this
28985          */
28986         "beforeshow" : true,
28987         /**
28988          * @event show
28989          * Fires when this dialog is shown.
28990          * @param {Roo.BasicDialog} this
28991          */
28992         "show" : true
28993     });
28994     el.on("keydown", this.onKeyDown, this);
28995     el.on("mousedown", this.toFront, this);
28996     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28997     this.el.hide();
28998     Roo.DialogManager.register(this);
28999     Roo.BasicDialog.superclass.constructor.call(this);
29000 };
29001
29002 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29003     shadowOffset: Roo.isIE ? 6 : 5,
29004     minHeight: 80,
29005     minWidth: 200,
29006     minButtonWidth: 75,
29007     defaultButton: null,
29008     buttonAlign: "right",
29009     tabTag: 'div',
29010     firstShow: true,
29011
29012     /**
29013      * Sets the dialog title text
29014      * @param {String} text The title text to display
29015      * @return {Roo.BasicDialog} this
29016      */
29017     setTitle : function(text){
29018         this.header.update(text);
29019         return this;
29020     },
29021
29022     // private
29023     closeClick : function(){
29024         this.hide();
29025     },
29026
29027     // private
29028     collapseClick : function(){
29029         this[this.collapsed ? "expand" : "collapse"]();
29030     },
29031
29032     /**
29033      * Collapses the dialog to its minimized state (only the title bar is visible).
29034      * Equivalent to the user clicking the collapse dialog button.
29035      */
29036     collapse : function(){
29037         if(!this.collapsed){
29038             this.collapsed = true;
29039             this.el.addClass("x-dlg-collapsed");
29040             this.restoreHeight = this.el.getHeight();
29041             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29042         }
29043     },
29044
29045     /**
29046      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29047      * clicking the expand dialog button.
29048      */
29049     expand : function(){
29050         if(this.collapsed){
29051             this.collapsed = false;
29052             this.el.removeClass("x-dlg-collapsed");
29053             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29054         }
29055     },
29056
29057     /**
29058      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29059      * @return {Roo.TabPanel} The tabs component
29060      */
29061     initTabs : function(){
29062         var tabs = this.getTabs();
29063         while(tabs.getTab(0)){
29064             tabs.removeTab(0);
29065         }
29066         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29067             var dom = el.dom;
29068             tabs.addTab(Roo.id(dom), dom.title);
29069             dom.title = "";
29070         });
29071         tabs.activate(0);
29072         return tabs;
29073     },
29074
29075     // private
29076     beforeResize : function(){
29077         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29078     },
29079
29080     // private
29081     onResize : function(){
29082         this.refreshSize();
29083         this.syncBodyHeight();
29084         this.adjustAssets();
29085         this.focus();
29086         this.fireEvent("resize", this, this.size.width, this.size.height);
29087     },
29088
29089     // private
29090     onKeyDown : function(e){
29091         if(this.isVisible()){
29092             this.fireEvent("keydown", this, e);
29093         }
29094     },
29095
29096     /**
29097      * Resizes the dialog.
29098      * @param {Number} width
29099      * @param {Number} height
29100      * @return {Roo.BasicDialog} this
29101      */
29102     resizeTo : function(width, height){
29103         this.el.setSize(width, height);
29104         this.size = {width: width, height: height};
29105         this.syncBodyHeight();
29106         if(this.fixedcenter){
29107             this.center();
29108         }
29109         if(this.isVisible()){
29110             this.constrainXY();
29111             this.adjustAssets();
29112         }
29113         this.fireEvent("resize", this, width, height);
29114         return this;
29115     },
29116
29117
29118     /**
29119      * Resizes the dialog to fit the specified content size.
29120      * @param {Number} width
29121      * @param {Number} height
29122      * @return {Roo.BasicDialog} this
29123      */
29124     setContentSize : function(w, h){
29125         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29126         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29127         //if(!this.el.isBorderBox()){
29128             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29129             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29130         //}
29131         if(this.tabs){
29132             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29133             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29134         }
29135         this.resizeTo(w, h);
29136         return this;
29137     },
29138
29139     /**
29140      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29141      * executed in response to a particular key being pressed while the dialog is active.
29142      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29143      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29144      * @param {Function} fn The function to call
29145      * @param {Object} scope (optional) The scope of the function
29146      * @return {Roo.BasicDialog} this
29147      */
29148     addKeyListener : function(key, fn, scope){
29149         var keyCode, shift, ctrl, alt;
29150         if(typeof key == "object" && !(key instanceof Array)){
29151             keyCode = key["key"];
29152             shift = key["shift"];
29153             ctrl = key["ctrl"];
29154             alt = key["alt"];
29155         }else{
29156             keyCode = key;
29157         }
29158         var handler = function(dlg, e){
29159             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29160                 var k = e.getKey();
29161                 if(keyCode instanceof Array){
29162                     for(var i = 0, len = keyCode.length; i < len; i++){
29163                         if(keyCode[i] == k){
29164                           fn.call(scope || window, dlg, k, e);
29165                           return;
29166                         }
29167                     }
29168                 }else{
29169                     if(k == keyCode){
29170                         fn.call(scope || window, dlg, k, e);
29171                     }
29172                 }
29173             }
29174         };
29175         this.on("keydown", handler);
29176         return this;
29177     },
29178
29179     /**
29180      * Returns the TabPanel component (creates it if it doesn't exist).
29181      * Note: If you wish to simply check for the existence of tabs without creating them,
29182      * check for a null 'tabs' property.
29183      * @return {Roo.TabPanel} The tabs component
29184      */
29185     getTabs : function(){
29186         if(!this.tabs){
29187             this.el.addClass("x-dlg-auto-tabs");
29188             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29189             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29190         }
29191         return this.tabs;
29192     },
29193
29194     /**
29195      * Adds a button to the footer section of the dialog.
29196      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29197      * object or a valid Roo.DomHelper element config
29198      * @param {Function} handler The function called when the button is clicked
29199      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29200      * @return {Roo.Button} The new button
29201      */
29202     addButton : function(config, handler, scope){
29203         var dh = Roo.DomHelper;
29204         if(!this.footer){
29205             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29206         }
29207         if(!this.btnContainer){
29208             var tb = this.footer.createChild({
29209
29210                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29211                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29212             }, null, true);
29213             this.btnContainer = tb.firstChild.firstChild.firstChild;
29214         }
29215         var bconfig = {
29216             handler: handler,
29217             scope: scope,
29218             minWidth: this.minButtonWidth,
29219             hideParent:true
29220         };
29221         if(typeof config == "string"){
29222             bconfig.text = config;
29223         }else{
29224             if(config.tag){
29225                 bconfig.dhconfig = config;
29226             }else{
29227                 Roo.apply(bconfig, config);
29228             }
29229         }
29230         var fc = false;
29231         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29232             bconfig.position = Math.max(0, bconfig.position);
29233             fc = this.btnContainer.childNodes[bconfig.position];
29234         }
29235          
29236         var btn = new Roo.Button(
29237             fc ? 
29238                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29239                 : this.btnContainer.appendChild(document.createElement("td")),
29240             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29241             bconfig
29242         );
29243         this.syncBodyHeight();
29244         if(!this.buttons){
29245             /**
29246              * Array of all the buttons that have been added to this dialog via addButton
29247              * @type Array
29248              */
29249             this.buttons = [];
29250         }
29251         this.buttons.push(btn);
29252         return btn;
29253     },
29254
29255     /**
29256      * Sets the default button to be focused when the dialog is displayed.
29257      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29258      * @return {Roo.BasicDialog} this
29259      */
29260     setDefaultButton : function(btn){
29261         this.defaultButton = btn;
29262         return this;
29263     },
29264
29265     // private
29266     getHeaderFooterHeight : function(safe){
29267         var height = 0;
29268         if(this.header){
29269            height += this.header.getHeight();
29270         }
29271         if(this.footer){
29272            var fm = this.footer.getMargins();
29273             height += (this.footer.getHeight()+fm.top+fm.bottom);
29274         }
29275         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29276         height += this.centerBg.getPadding("tb");
29277         return height;
29278     },
29279
29280     // private
29281     syncBodyHeight : function(){
29282         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29283         var height = this.size.height - this.getHeaderFooterHeight(false);
29284         bd.setHeight(height-bd.getMargins("tb"));
29285         var hh = this.header.getHeight();
29286         var h = this.size.height-hh;
29287         cb.setHeight(h);
29288         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29289         bw.setHeight(h-cb.getPadding("tb"));
29290         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29291         bd.setWidth(bw.getWidth(true));
29292         if(this.tabs){
29293             this.tabs.syncHeight();
29294             if(Roo.isIE){
29295                 this.tabs.el.repaint();
29296             }
29297         }
29298     },
29299
29300     /**
29301      * Restores the previous state of the dialog if Roo.state is configured.
29302      * @return {Roo.BasicDialog} this
29303      */
29304     restoreState : function(){
29305         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29306         if(box && box.width){
29307             this.xy = [box.x, box.y];
29308             this.resizeTo(box.width, box.height);
29309         }
29310         return this;
29311     },
29312
29313     // private
29314     beforeShow : function(){
29315         this.expand();
29316         if(this.fixedcenter){
29317             this.xy = this.el.getCenterXY(true);
29318         }
29319         if(this.modal){
29320             Roo.get(document.body).addClass("x-body-masked");
29321             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29322             this.mask.show();
29323         }
29324         this.constrainXY();
29325     },
29326
29327     // private
29328     animShow : function(){
29329         var b = Roo.get(this.animateTarget).getBox();
29330         this.proxy.setSize(b.width, b.height);
29331         this.proxy.setLocation(b.x, b.y);
29332         this.proxy.show();
29333         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29334                     true, .35, this.showEl.createDelegate(this));
29335     },
29336
29337     /**
29338      * Shows the dialog.
29339      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29340      * @return {Roo.BasicDialog} this
29341      */
29342     show : function(animateTarget){
29343         if (this.fireEvent("beforeshow", this) === false){
29344             return;
29345         }
29346         if(this.syncHeightBeforeShow){
29347             this.syncBodyHeight();
29348         }else if(this.firstShow){
29349             this.firstShow = false;
29350             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29351         }
29352         this.animateTarget = animateTarget || this.animateTarget;
29353         if(!this.el.isVisible()){
29354             this.beforeShow();
29355             if(this.animateTarget && Roo.get(this.animateTarget)){
29356                 this.animShow();
29357             }else{
29358                 this.showEl();
29359             }
29360         }
29361         return this;
29362     },
29363
29364     // private
29365     showEl : function(){
29366         this.proxy.hide();
29367         this.el.setXY(this.xy);
29368         this.el.show();
29369         this.adjustAssets(true);
29370         this.toFront();
29371         this.focus();
29372         // IE peekaboo bug - fix found by Dave Fenwick
29373         if(Roo.isIE){
29374             this.el.repaint();
29375         }
29376         this.fireEvent("show", this);
29377     },
29378
29379     /**
29380      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29381      * dialog itself will receive focus.
29382      */
29383     focus : function(){
29384         if(this.defaultButton){
29385             this.defaultButton.focus();
29386         }else{
29387             this.focusEl.focus();
29388         }
29389     },
29390
29391     // private
29392     constrainXY : function(){
29393         if(this.constraintoviewport !== false){
29394             if(!this.viewSize){
29395                 if(this.container){
29396                     var s = this.container.getSize();
29397                     this.viewSize = [s.width, s.height];
29398                 }else{
29399                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29400                 }
29401             }
29402             var s = Roo.get(this.container||document).getScroll();
29403
29404             var x = this.xy[0], y = this.xy[1];
29405             var w = this.size.width, h = this.size.height;
29406             var vw = this.viewSize[0], vh = this.viewSize[1];
29407             // only move it if it needs it
29408             var moved = false;
29409             // first validate right/bottom
29410             if(x + w > vw+s.left){
29411                 x = vw - w;
29412                 moved = true;
29413             }
29414             if(y + h > vh+s.top){
29415                 y = vh - h;
29416                 moved = true;
29417             }
29418             // then make sure top/left isn't negative
29419             if(x < s.left){
29420                 x = s.left;
29421                 moved = true;
29422             }
29423             if(y < s.top){
29424                 y = s.top;
29425                 moved = true;
29426             }
29427             if(moved){
29428                 // cache xy
29429                 this.xy = [x, y];
29430                 if(this.isVisible()){
29431                     this.el.setLocation(x, y);
29432                     this.adjustAssets();
29433                 }
29434             }
29435         }
29436     },
29437
29438     // private
29439     onDrag : function(){
29440         if(!this.proxyDrag){
29441             this.xy = this.el.getXY();
29442             this.adjustAssets();
29443         }
29444     },
29445
29446     // private
29447     adjustAssets : function(doShow){
29448         var x = this.xy[0], y = this.xy[1];
29449         var w = this.size.width, h = this.size.height;
29450         if(doShow === true){
29451             if(this.shadow){
29452                 this.shadow.show(this.el);
29453             }
29454             if(this.shim){
29455                 this.shim.show();
29456             }
29457         }
29458         if(this.shadow && this.shadow.isVisible()){
29459             this.shadow.show(this.el);
29460         }
29461         if(this.shim && this.shim.isVisible()){
29462             this.shim.setBounds(x, y, w, h);
29463         }
29464     },
29465
29466     // private
29467     adjustViewport : function(w, h){
29468         if(!w || !h){
29469             w = Roo.lib.Dom.getViewWidth();
29470             h = Roo.lib.Dom.getViewHeight();
29471         }
29472         // cache the size
29473         this.viewSize = [w, h];
29474         if(this.modal && this.mask.isVisible()){
29475             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29476             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29477         }
29478         if(this.isVisible()){
29479             this.constrainXY();
29480         }
29481     },
29482
29483     /**
29484      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29485      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29486      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29487      */
29488     destroy : function(removeEl){
29489         if(this.isVisible()){
29490             this.animateTarget = null;
29491             this.hide();
29492         }
29493         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29494         if(this.tabs){
29495             this.tabs.destroy(removeEl);
29496         }
29497         Roo.destroy(
29498              this.shim,
29499              this.proxy,
29500              this.resizer,
29501              this.close,
29502              this.mask
29503         );
29504         if(this.dd){
29505             this.dd.unreg();
29506         }
29507         if(this.buttons){
29508            for(var i = 0, len = this.buttons.length; i < len; i++){
29509                this.buttons[i].destroy();
29510            }
29511         }
29512         this.el.removeAllListeners();
29513         if(removeEl === true){
29514             this.el.update("");
29515             this.el.remove();
29516         }
29517         Roo.DialogManager.unregister(this);
29518     },
29519
29520     // private
29521     startMove : function(){
29522         if(this.proxyDrag){
29523             this.proxy.show();
29524         }
29525         if(this.constraintoviewport !== false){
29526             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29527         }
29528     },
29529
29530     // private
29531     endMove : function(){
29532         if(!this.proxyDrag){
29533             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29534         }else{
29535             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29536             this.proxy.hide();
29537         }
29538         this.refreshSize();
29539         this.adjustAssets();
29540         this.focus();
29541         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29542     },
29543
29544     /**
29545      * Brings this dialog to the front of any other visible dialogs
29546      * @return {Roo.BasicDialog} this
29547      */
29548     toFront : function(){
29549         Roo.DialogManager.bringToFront(this);
29550         return this;
29551     },
29552
29553     /**
29554      * Sends this dialog to the back (under) of any other visible dialogs
29555      * @return {Roo.BasicDialog} this
29556      */
29557     toBack : function(){
29558         Roo.DialogManager.sendToBack(this);
29559         return this;
29560     },
29561
29562     /**
29563      * Centers this dialog in the viewport
29564      * @return {Roo.BasicDialog} this
29565      */
29566     center : function(){
29567         var xy = this.el.getCenterXY(true);
29568         this.moveTo(xy[0], xy[1]);
29569         return this;
29570     },
29571
29572     /**
29573      * Moves the dialog's top-left corner to the specified point
29574      * @param {Number} x
29575      * @param {Number} y
29576      * @return {Roo.BasicDialog} this
29577      */
29578     moveTo : function(x, y){
29579         this.xy = [x,y];
29580         if(this.isVisible()){
29581             this.el.setXY(this.xy);
29582             this.adjustAssets();
29583         }
29584         return this;
29585     },
29586
29587     /**
29588      * Aligns the dialog to the specified element
29589      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29590      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29591      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29592      * @return {Roo.BasicDialog} this
29593      */
29594     alignTo : function(element, position, offsets){
29595         this.xy = this.el.getAlignToXY(element, position, offsets);
29596         if(this.isVisible()){
29597             this.el.setXY(this.xy);
29598             this.adjustAssets();
29599         }
29600         return this;
29601     },
29602
29603     /**
29604      * Anchors an element to another element and realigns it when the window is resized.
29605      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29606      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29607      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29608      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29609      * is a number, it is used as the buffer delay (defaults to 50ms).
29610      * @return {Roo.BasicDialog} this
29611      */
29612     anchorTo : function(el, alignment, offsets, monitorScroll){
29613         var action = function(){
29614             this.alignTo(el, alignment, offsets);
29615         };
29616         Roo.EventManager.onWindowResize(action, this);
29617         var tm = typeof monitorScroll;
29618         if(tm != 'undefined'){
29619             Roo.EventManager.on(window, 'scroll', action, this,
29620                 {buffer: tm == 'number' ? monitorScroll : 50});
29621         }
29622         action.call(this);
29623         return this;
29624     },
29625
29626     /**
29627      * Returns true if the dialog is visible
29628      * @return {Boolean}
29629      */
29630     isVisible : function(){
29631         return this.el.isVisible();
29632     },
29633
29634     // private
29635     animHide : function(callback){
29636         var b = Roo.get(this.animateTarget).getBox();
29637         this.proxy.show();
29638         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29639         this.el.hide();
29640         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29641                     this.hideEl.createDelegate(this, [callback]));
29642     },
29643
29644     /**
29645      * Hides the dialog.
29646      * @param {Function} callback (optional) Function to call when the dialog is hidden
29647      * @return {Roo.BasicDialog} this
29648      */
29649     hide : function(callback){
29650         if (this.fireEvent("beforehide", this) === false){
29651             return;
29652         }
29653         if(this.shadow){
29654             this.shadow.hide();
29655         }
29656         if(this.shim) {
29657           this.shim.hide();
29658         }
29659         // sometimes animateTarget seems to get set.. causing problems...
29660         // this just double checks..
29661         if(this.animateTarget && Roo.get(this.animateTarget)) {
29662            this.animHide(callback);
29663         }else{
29664             this.el.hide();
29665             this.hideEl(callback);
29666         }
29667         return this;
29668     },
29669
29670     // private
29671     hideEl : function(callback){
29672         this.proxy.hide();
29673         if(this.modal){
29674             this.mask.hide();
29675             Roo.get(document.body).removeClass("x-body-masked");
29676         }
29677         this.fireEvent("hide", this);
29678         if(typeof callback == "function"){
29679             callback();
29680         }
29681     },
29682
29683     // private
29684     hideAction : function(){
29685         this.setLeft("-10000px");
29686         this.setTop("-10000px");
29687         this.setStyle("visibility", "hidden");
29688     },
29689
29690     // private
29691     refreshSize : function(){
29692         this.size = this.el.getSize();
29693         this.xy = this.el.getXY();
29694         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29695     },
29696
29697     // private
29698     // z-index is managed by the DialogManager and may be overwritten at any time
29699     setZIndex : function(index){
29700         if(this.modal){
29701             this.mask.setStyle("z-index", index);
29702         }
29703         if(this.shim){
29704             this.shim.setStyle("z-index", ++index);
29705         }
29706         if(this.shadow){
29707             this.shadow.setZIndex(++index);
29708         }
29709         this.el.setStyle("z-index", ++index);
29710         if(this.proxy){
29711             this.proxy.setStyle("z-index", ++index);
29712         }
29713         if(this.resizer){
29714             this.resizer.proxy.setStyle("z-index", ++index);
29715         }
29716
29717         this.lastZIndex = index;
29718     },
29719
29720     /**
29721      * Returns the element for this dialog
29722      * @return {Roo.Element} The underlying dialog Element
29723      */
29724     getEl : function(){
29725         return this.el;
29726     }
29727 });
29728
29729 /**
29730  * @class Roo.DialogManager
29731  * Provides global access to BasicDialogs that have been created and
29732  * support for z-indexing (layering) multiple open dialogs.
29733  */
29734 Roo.DialogManager = function(){
29735     var list = {};
29736     var accessList = [];
29737     var front = null;
29738
29739     // private
29740     var sortDialogs = function(d1, d2){
29741         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29742     };
29743
29744     // private
29745     var orderDialogs = function(){
29746         accessList.sort(sortDialogs);
29747         var seed = Roo.DialogManager.zseed;
29748         for(var i = 0, len = accessList.length; i < len; i++){
29749             var dlg = accessList[i];
29750             if(dlg){
29751                 dlg.setZIndex(seed + (i*10));
29752             }
29753         }
29754     };
29755
29756     return {
29757         /**
29758          * The starting z-index for BasicDialogs (defaults to 9000)
29759          * @type Number The z-index value
29760          */
29761         zseed : 9000,
29762
29763         // private
29764         register : function(dlg){
29765             list[dlg.id] = dlg;
29766             accessList.push(dlg);
29767         },
29768
29769         // private
29770         unregister : function(dlg){
29771             delete list[dlg.id];
29772             var i=0;
29773             var len=0;
29774             if(!accessList.indexOf){
29775                 for(  i = 0, len = accessList.length; i < len; i++){
29776                     if(accessList[i] == dlg){
29777                         accessList.splice(i, 1);
29778                         return;
29779                     }
29780                 }
29781             }else{
29782                  i = accessList.indexOf(dlg);
29783                 if(i != -1){
29784                     accessList.splice(i, 1);
29785                 }
29786             }
29787         },
29788
29789         /**
29790          * Gets a registered dialog by id
29791          * @param {String/Object} id The id of the dialog or a dialog
29792          * @return {Roo.BasicDialog} this
29793          */
29794         get : function(id){
29795             return typeof id == "object" ? id : list[id];
29796         },
29797
29798         /**
29799          * Brings the specified dialog to the front
29800          * @param {String/Object} dlg The id of the dialog or a dialog
29801          * @return {Roo.BasicDialog} this
29802          */
29803         bringToFront : function(dlg){
29804             dlg = this.get(dlg);
29805             if(dlg != front){
29806                 front = dlg;
29807                 dlg._lastAccess = new Date().getTime();
29808                 orderDialogs();
29809             }
29810             return dlg;
29811         },
29812
29813         /**
29814          * Sends the specified dialog to the back
29815          * @param {String/Object} dlg The id of the dialog or a dialog
29816          * @return {Roo.BasicDialog} this
29817          */
29818         sendToBack : function(dlg){
29819             dlg = this.get(dlg);
29820             dlg._lastAccess = -(new Date().getTime());
29821             orderDialogs();
29822             return dlg;
29823         },
29824
29825         /**
29826          * Hides all dialogs
29827          */
29828         hideAll : function(){
29829             for(var id in list){
29830                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29831                     list[id].hide();
29832                 }
29833             }
29834         }
29835     };
29836 }();
29837
29838 /**
29839  * @class Roo.LayoutDialog
29840  * @extends Roo.BasicDialog
29841  * Dialog which provides adjustments for working with a layout in a Dialog.
29842  * Add your necessary layout config options to the dialog's config.<br>
29843  * Example usage (including a nested layout):
29844  * <pre><code>
29845 if(!dialog){
29846     dialog = new Roo.LayoutDialog("download-dlg", {
29847         modal: true,
29848         width:600,
29849         height:450,
29850         shadow:true,
29851         minWidth:500,
29852         minHeight:350,
29853         autoTabs:true,
29854         proxyDrag:true,
29855         // layout config merges with the dialog config
29856         center:{
29857             tabPosition: "top",
29858             alwaysShowTabs: true
29859         }
29860     });
29861     dialog.addKeyListener(27, dialog.hide, dialog);
29862     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29863     dialog.addButton("Build It!", this.getDownload, this);
29864
29865     // we can even add nested layouts
29866     var innerLayout = new Roo.BorderLayout("dl-inner", {
29867         east: {
29868             initialSize: 200,
29869             autoScroll:true,
29870             split:true
29871         },
29872         center: {
29873             autoScroll:true
29874         }
29875     });
29876     innerLayout.beginUpdate();
29877     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29878     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29879     innerLayout.endUpdate(true);
29880
29881     var layout = dialog.getLayout();
29882     layout.beginUpdate();
29883     layout.add("center", new Roo.ContentPanel("standard-panel",
29884                         {title: "Download the Source", fitToFrame:true}));
29885     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29886                {title: "Build your own roo.js"}));
29887     layout.getRegion("center").showPanel(sp);
29888     layout.endUpdate();
29889 }
29890 </code></pre>
29891     * @constructor
29892     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29893     * @param {Object} config configuration options
29894   */
29895 Roo.LayoutDialog = function(el, cfg){
29896     
29897     var config=  cfg;
29898     if (typeof(cfg) == 'undefined') {
29899         config = Roo.apply({}, el);
29900         // not sure why we use documentElement here.. - it should always be body.
29901         // IE7 borks horribly if we use documentElement.
29902         // webkit also does not like documentElement - it creates a body element...
29903         el = Roo.get( document.body || document.documentElement ).createChild();
29904         //config.autoCreate = true;
29905     }
29906     
29907     
29908     config.autoTabs = false;
29909     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29910     this.body.setStyle({overflow:"hidden", position:"relative"});
29911     this.layout = new Roo.BorderLayout(this.body.dom, config);
29912     this.layout.monitorWindowResize = false;
29913     this.el.addClass("x-dlg-auto-layout");
29914     // fix case when center region overwrites center function
29915     this.center = Roo.BasicDialog.prototype.center;
29916     this.on("show", this.layout.layout, this.layout, true);
29917     if (config.items) {
29918         var xitems = config.items;
29919         delete config.items;
29920         Roo.each(xitems, this.addxtype, this);
29921     }
29922     
29923     
29924 };
29925 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29926     /**
29927      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29928      * @deprecated
29929      */
29930     endUpdate : function(){
29931         this.layout.endUpdate();
29932     },
29933
29934     /**
29935      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29936      *  @deprecated
29937      */
29938     beginUpdate : function(){
29939         this.layout.beginUpdate();
29940     },
29941
29942     /**
29943      * Get the BorderLayout for this dialog
29944      * @return {Roo.BorderLayout}
29945      */
29946     getLayout : function(){
29947         return this.layout;
29948     },
29949
29950     showEl : function(){
29951         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29952         if(Roo.isIE7){
29953             this.layout.layout();
29954         }
29955     },
29956
29957     // private
29958     // Use the syncHeightBeforeShow config option to control this automatically
29959     syncBodyHeight : function(){
29960         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29961         if(this.layout){this.layout.layout();}
29962     },
29963     
29964       /**
29965      * Add an xtype element (actually adds to the layout.)
29966      * @return {Object} xdata xtype object data.
29967      */
29968     
29969     addxtype : function(c) {
29970         return this.layout.addxtype(c);
29971     }
29972 });/*
29973  * Based on:
29974  * Ext JS Library 1.1.1
29975  * Copyright(c) 2006-2007, Ext JS, LLC.
29976  *
29977  * Originally Released Under LGPL - original licence link has changed is not relivant.
29978  *
29979  * Fork - LGPL
29980  * <script type="text/javascript">
29981  */
29982  
29983 /**
29984  * @class Roo.MessageBox
29985  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29986  * Example usage:
29987  *<pre><code>
29988 // Basic alert:
29989 Roo.Msg.alert('Status', 'Changes saved successfully.');
29990
29991 // Prompt for user data:
29992 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29993     if (btn == 'ok'){
29994         // process text value...
29995     }
29996 });
29997
29998 // Show a dialog using config options:
29999 Roo.Msg.show({
30000    title:'Save Changes?',
30001    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30002    buttons: Roo.Msg.YESNOCANCEL,
30003    fn: processResult,
30004    animEl: 'elId'
30005 });
30006 </code></pre>
30007  * @singleton
30008  */
30009 Roo.MessageBox = function(){
30010     var dlg, opt, mask, waitTimer;
30011     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30012     var buttons, activeTextEl, bwidth;
30013
30014     // private
30015     var handleButton = function(button){
30016         dlg.hide();
30017         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30018     };
30019
30020     // private
30021     var handleHide = function(){
30022         if(opt && opt.cls){
30023             dlg.el.removeClass(opt.cls);
30024         }
30025         if(waitTimer){
30026             Roo.TaskMgr.stop(waitTimer);
30027             waitTimer = null;
30028         }
30029     };
30030
30031     // private
30032     var updateButtons = function(b){
30033         var width = 0;
30034         if(!b){
30035             buttons["ok"].hide();
30036             buttons["cancel"].hide();
30037             buttons["yes"].hide();
30038             buttons["no"].hide();
30039             dlg.footer.dom.style.display = 'none';
30040             return width;
30041         }
30042         dlg.footer.dom.style.display = '';
30043         for(var k in buttons){
30044             if(typeof buttons[k] != "function"){
30045                 if(b[k]){
30046                     buttons[k].show();
30047                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30048                     width += buttons[k].el.getWidth()+15;
30049                 }else{
30050                     buttons[k].hide();
30051                 }
30052             }
30053         }
30054         return width;
30055     };
30056
30057     // private
30058     var handleEsc = function(d, k, e){
30059         if(opt && opt.closable !== false){
30060             dlg.hide();
30061         }
30062         if(e){
30063             e.stopEvent();
30064         }
30065     };
30066
30067     return {
30068         /**
30069          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30070          * @return {Roo.BasicDialog} The BasicDialog element
30071          */
30072         getDialog : function(){
30073            if(!dlg){
30074                 dlg = new Roo.BasicDialog("x-msg-box", {
30075                     autoCreate : true,
30076                     shadow: true,
30077                     draggable: true,
30078                     resizable:false,
30079                     constraintoviewport:false,
30080                     fixedcenter:true,
30081                     collapsible : false,
30082                     shim:true,
30083                     modal: true,
30084                     width:400, height:100,
30085                     buttonAlign:"center",
30086                     closeClick : function(){
30087                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30088                             handleButton("no");
30089                         }else{
30090                             handleButton("cancel");
30091                         }
30092                     }
30093                 });
30094                 dlg.on("hide", handleHide);
30095                 mask = dlg.mask;
30096                 dlg.addKeyListener(27, handleEsc);
30097                 buttons = {};
30098                 var bt = this.buttonText;
30099                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30100                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30101                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30102                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30103                 bodyEl = dlg.body.createChild({
30104
30105                     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>'
30106                 });
30107                 msgEl = bodyEl.dom.firstChild;
30108                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30109                 textboxEl.enableDisplayMode();
30110                 textboxEl.addKeyListener([10,13], function(){
30111                     if(dlg.isVisible() && opt && opt.buttons){
30112                         if(opt.buttons.ok){
30113                             handleButton("ok");
30114                         }else if(opt.buttons.yes){
30115                             handleButton("yes");
30116                         }
30117                     }
30118                 });
30119                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30120                 textareaEl.enableDisplayMode();
30121                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30122                 progressEl.enableDisplayMode();
30123                 var pf = progressEl.dom.firstChild;
30124                 if (pf) {
30125                     pp = Roo.get(pf.firstChild);
30126                     pp.setHeight(pf.offsetHeight);
30127                 }
30128                 
30129             }
30130             return dlg;
30131         },
30132
30133         /**
30134          * Updates the message box body text
30135          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30136          * the XHTML-compliant non-breaking space character '&amp;#160;')
30137          * @return {Roo.MessageBox} This message box
30138          */
30139         updateText : function(text){
30140             if(!dlg.isVisible() && !opt.width){
30141                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30142             }
30143             msgEl.innerHTML = text || '&#160;';
30144             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30145                         Math.max(opt.minWidth || this.minWidth, bwidth));
30146             if(opt.prompt){
30147                 activeTextEl.setWidth(w);
30148             }
30149             if(dlg.isVisible()){
30150                 dlg.fixedcenter = false;
30151             }
30152             dlg.setContentSize(w, bodyEl.getHeight());
30153             if(dlg.isVisible()){
30154                 dlg.fixedcenter = true;
30155             }
30156             return this;
30157         },
30158
30159         /**
30160          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30161          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30162          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30163          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30164          * @return {Roo.MessageBox} This message box
30165          */
30166         updateProgress : function(value, text){
30167             if(text){
30168                 this.updateText(text);
30169             }
30170             if (pp) { // weird bug on my firefox - for some reason this is not defined
30171                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30172             }
30173             return this;
30174         },        
30175
30176         /**
30177          * Returns true if the message box is currently displayed
30178          * @return {Boolean} True if the message box is visible, else false
30179          */
30180         isVisible : function(){
30181             return dlg && dlg.isVisible();  
30182         },
30183
30184         /**
30185          * Hides the message box if it is displayed
30186          */
30187         hide : function(){
30188             if(this.isVisible()){
30189                 dlg.hide();
30190             }  
30191         },
30192
30193         /**
30194          * Displays a new message box, or reinitializes an existing message box, based on the config options
30195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30196          * The following config object properties are supported:
30197          * <pre>
30198 Property    Type             Description
30199 ----------  ---------------  ------------------------------------------------------------------------------------
30200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30201                                    closes (defaults to undefined)
30202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30205                                    progress and wait dialogs will ignore this property and always hide the
30206                                    close button as they can only be closed programmatically.
30207 cls               String           A custom CSS class to apply to the message box element
30208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30209                                    displayed (defaults to 75)
30210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30211                                    function will be btn (the name of the button that was clicked, if applicable,
30212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30213                                    Progress and wait dialogs will ignore this option since they do not respond to
30214                                    user actions and can only be closed programmatically, so any required function
30215                                    should be called by the same code after it closes the dialog.
30216 icon              String           A CSS class that provides a background image to be used as an icon for
30217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30220 modal             Boolean          False to allow user interaction with the page while the message box is
30221                                    displayed (defaults to true)
30222 msg               String           A string that will replace the existing message box body text (defaults
30223                                    to the XHTML-compliant non-breaking space character '&#160;')
30224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30225 progress          Boolean          True to display a progress bar (defaults to false)
30226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30229 title             String           The title text
30230 value             String           The string value to set into the active textbox element if displayed
30231 wait              Boolean          True to display a progress bar (defaults to false)
30232 width             Number           The width of the dialog in pixels
30233 </pre>
30234          *
30235          * Example usage:
30236          * <pre><code>
30237 Roo.Msg.show({
30238    title: 'Address',
30239    msg: 'Please enter your address:',
30240    width: 300,
30241    buttons: Roo.MessageBox.OKCANCEL,
30242    multiline: true,
30243    fn: saveAddress,
30244    animEl: 'addAddressBtn'
30245 });
30246 </code></pre>
30247          * @param {Object} config Configuration options
30248          * @return {Roo.MessageBox} This message box
30249          */
30250         show : function(options)
30251         {
30252             
30253             // this causes nightmares if you show one dialog after another
30254             // especially on callbacks..
30255              
30256             if(this.isVisible()){
30257                 
30258                 this.hide();
30259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30260                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30261                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30262                 
30263             }
30264             var d = this.getDialog();
30265             opt = options;
30266             d.setTitle(opt.title || "&#160;");
30267             d.close.setDisplayed(opt.closable !== false);
30268             activeTextEl = textboxEl;
30269             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30270             if(opt.prompt){
30271                 if(opt.multiline){
30272                     textboxEl.hide();
30273                     textareaEl.show();
30274                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30275                         opt.multiline : this.defaultTextHeight);
30276                     activeTextEl = textareaEl;
30277                 }else{
30278                     textboxEl.show();
30279                     textareaEl.hide();
30280                 }
30281             }else{
30282                 textboxEl.hide();
30283                 textareaEl.hide();
30284             }
30285             progressEl.setDisplayed(opt.progress === true);
30286             this.updateProgress(0);
30287             activeTextEl.dom.value = opt.value || "";
30288             if(opt.prompt){
30289                 dlg.setDefaultButton(activeTextEl);
30290             }else{
30291                 var bs = opt.buttons;
30292                 var db = null;
30293                 if(bs && bs.ok){
30294                     db = buttons["ok"];
30295                 }else if(bs && bs.yes){
30296                     db = buttons["yes"];
30297                 }
30298                 dlg.setDefaultButton(db);
30299             }
30300             bwidth = updateButtons(opt.buttons);
30301             this.updateText(opt.msg);
30302             if(opt.cls){
30303                 d.el.addClass(opt.cls);
30304             }
30305             d.proxyDrag = opt.proxyDrag === true;
30306             d.modal = opt.modal !== false;
30307             d.mask = opt.modal !== false ? mask : false;
30308             if(!d.isVisible()){
30309                 // force it to the end of the z-index stack so it gets a cursor in FF
30310                 document.body.appendChild(dlg.el.dom);
30311                 d.animateTarget = null;
30312                 d.show(options.animEl);
30313             }
30314             return this;
30315         },
30316
30317         /**
30318          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30319          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30320          * and closing the message box when the process is complete.
30321          * @param {String} title The title bar text
30322          * @param {String} msg The message box body text
30323          * @return {Roo.MessageBox} This message box
30324          */
30325         progress : function(title, msg){
30326             this.show({
30327                 title : title,
30328                 msg : msg,
30329                 buttons: false,
30330                 progress:true,
30331                 closable:false,
30332                 minWidth: this.minProgressWidth,
30333                 modal : true
30334             });
30335             return this;
30336         },
30337
30338         /**
30339          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30340          * If a callback function is passed it will be called after the user clicks the button, and the
30341          * id of the button that was clicked will be passed as the only parameter to the callback
30342          * (could also be the top-right close button).
30343          * @param {String} title The title bar text
30344          * @param {String} msg The message box body text
30345          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30346          * @param {Object} scope (optional) The scope of the callback function
30347          * @return {Roo.MessageBox} This message box
30348          */
30349         alert : function(title, msg, fn, scope){
30350             this.show({
30351                 title : title,
30352                 msg : msg,
30353                 buttons: this.OK,
30354                 fn: fn,
30355                 scope : scope,
30356                 modal : true
30357             });
30358             return this;
30359         },
30360
30361         /**
30362          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30363          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30364          * You are responsible for closing the message box when the process is complete.
30365          * @param {String} msg The message box body text
30366          * @param {String} title (optional) The title bar text
30367          * @return {Roo.MessageBox} This message box
30368          */
30369         wait : function(msg, title){
30370             this.show({
30371                 title : title,
30372                 msg : msg,
30373                 buttons: false,
30374                 closable:false,
30375                 progress:true,
30376                 modal:true,
30377                 width:300,
30378                 wait:true
30379             });
30380             waitTimer = Roo.TaskMgr.start({
30381                 run: function(i){
30382                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30383                 },
30384                 interval: 1000
30385             });
30386             return this;
30387         },
30388
30389         /**
30390          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30391          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30392          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30393          * @param {String} title The title bar text
30394          * @param {String} msg The message box body text
30395          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30396          * @param {Object} scope (optional) The scope of the callback function
30397          * @return {Roo.MessageBox} This message box
30398          */
30399         confirm : function(title, msg, fn, scope){
30400             this.show({
30401                 title : title,
30402                 msg : msg,
30403                 buttons: this.YESNO,
30404                 fn: fn,
30405                 scope : scope,
30406                 modal : true
30407             });
30408             return this;
30409         },
30410
30411         /**
30412          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30413          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30414          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30415          * (could also be the top-right close button) and the text that was entered will be passed as the two
30416          * parameters to the callback.
30417          * @param {String} title The title bar text
30418          * @param {String} msg The message box body text
30419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30420          * @param {Object} scope (optional) The scope of the callback function
30421          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30422          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30423          * @return {Roo.MessageBox} This message box
30424          */
30425         prompt : function(title, msg, fn, scope, multiline){
30426             this.show({
30427                 title : title,
30428                 msg : msg,
30429                 buttons: this.OKCANCEL,
30430                 fn: fn,
30431                 minWidth:250,
30432                 scope : scope,
30433                 prompt:true,
30434                 multiline: multiline,
30435                 modal : true
30436             });
30437             return this;
30438         },
30439
30440         /**
30441          * Button config that displays a single OK button
30442          * @type Object
30443          */
30444         OK : {ok:true},
30445         /**
30446          * Button config that displays Yes and No buttons
30447          * @type Object
30448          */
30449         YESNO : {yes:true, no:true},
30450         /**
30451          * Button config that displays OK and Cancel buttons
30452          * @type Object
30453          */
30454         OKCANCEL : {ok:true, cancel:true},
30455         /**
30456          * Button config that displays Yes, No and Cancel buttons
30457          * @type Object
30458          */
30459         YESNOCANCEL : {yes:true, no:true, cancel:true},
30460
30461         /**
30462          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30463          * @type Number
30464          */
30465         defaultTextHeight : 75,
30466         /**
30467          * The maximum width in pixels of the message box (defaults to 600)
30468          * @type Number
30469          */
30470         maxWidth : 600,
30471         /**
30472          * The minimum width in pixels of the message box (defaults to 100)
30473          * @type Number
30474          */
30475         minWidth : 100,
30476         /**
30477          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30478          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30479          * @type Number
30480          */
30481         minProgressWidth : 250,
30482         /**
30483          * An object containing the default button text strings that can be overriden for localized language support.
30484          * Supported properties are: ok, cancel, yes and no.
30485          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30486          * @type Object
30487          */
30488         buttonText : {
30489             ok : "OK",
30490             cancel : "Cancel",
30491             yes : "Yes",
30492             no : "No"
30493         }
30494     };
30495 }();
30496
30497 /**
30498  * Shorthand for {@link Roo.MessageBox}
30499  */
30500 Roo.Msg = Roo.MessageBox;/*
30501  * Based on:
30502  * Ext JS Library 1.1.1
30503  * Copyright(c) 2006-2007, Ext JS, LLC.
30504  *
30505  * Originally Released Under LGPL - original licence link has changed is not relivant.
30506  *
30507  * Fork - LGPL
30508  * <script type="text/javascript">
30509  */
30510 /**
30511  * @class Roo.QuickTips
30512  * Provides attractive and customizable tooltips for any element.
30513  * @singleton
30514  */
30515 Roo.QuickTips = function(){
30516     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30517     var ce, bd, xy, dd;
30518     var visible = false, disabled = true, inited = false;
30519     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30520     
30521     var onOver = function(e){
30522         if(disabled){
30523             return;
30524         }
30525         var t = e.getTarget();
30526         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30527             return;
30528         }
30529         if(ce && t == ce.el){
30530             clearTimeout(hideProc);
30531             return;
30532         }
30533         if(t && tagEls[t.id]){
30534             tagEls[t.id].el = t;
30535             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30536             return;
30537         }
30538         var ttp, et = Roo.fly(t);
30539         var ns = cfg.namespace;
30540         if(tm.interceptTitles && t.title){
30541             ttp = t.title;
30542             t.qtip = ttp;
30543             t.removeAttribute("title");
30544             e.preventDefault();
30545         }else{
30546             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30547         }
30548         if(ttp){
30549             showProc = show.defer(tm.showDelay, tm, [{
30550                 el: t, 
30551                 text: ttp, 
30552                 width: et.getAttributeNS(ns, cfg.width),
30553                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30554                 title: et.getAttributeNS(ns, cfg.title),
30555                     cls: et.getAttributeNS(ns, cfg.cls)
30556             }]);
30557         }
30558     };
30559     
30560     var onOut = function(e){
30561         clearTimeout(showProc);
30562         var t = e.getTarget();
30563         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30564             hideProc = setTimeout(hide, tm.hideDelay);
30565         }
30566     };
30567     
30568     var onMove = function(e){
30569         if(disabled){
30570             return;
30571         }
30572         xy = e.getXY();
30573         xy[1] += 18;
30574         if(tm.trackMouse && ce){
30575             el.setXY(xy);
30576         }
30577     };
30578     
30579     var onDown = function(e){
30580         clearTimeout(showProc);
30581         clearTimeout(hideProc);
30582         if(!e.within(el)){
30583             if(tm.hideOnClick){
30584                 hide();
30585                 tm.disable();
30586                 tm.enable.defer(100, tm);
30587             }
30588         }
30589     };
30590     
30591     var getPad = function(){
30592         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30593     };
30594
30595     var show = function(o){
30596         if(disabled){
30597             return;
30598         }
30599         clearTimeout(dismissProc);
30600         ce = o;
30601         if(removeCls){ // in case manually hidden
30602             el.removeClass(removeCls);
30603             removeCls = null;
30604         }
30605         if(ce.cls){
30606             el.addClass(ce.cls);
30607             removeCls = ce.cls;
30608         }
30609         if(ce.title){
30610             tipTitle.update(ce.title);
30611             tipTitle.show();
30612         }else{
30613             tipTitle.update('');
30614             tipTitle.hide();
30615         }
30616         el.dom.style.width  = tm.maxWidth+'px';
30617         //tipBody.dom.style.width = '';
30618         tipBodyText.update(o.text);
30619         var p = getPad(), w = ce.width;
30620         if(!w){
30621             var td = tipBodyText.dom;
30622             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30623             if(aw > tm.maxWidth){
30624                 w = tm.maxWidth;
30625             }else if(aw < tm.minWidth){
30626                 w = tm.minWidth;
30627             }else{
30628                 w = aw;
30629             }
30630         }
30631         //tipBody.setWidth(w);
30632         el.setWidth(parseInt(w, 10) + p);
30633         if(ce.autoHide === false){
30634             close.setDisplayed(true);
30635             if(dd){
30636                 dd.unlock();
30637             }
30638         }else{
30639             close.setDisplayed(false);
30640             if(dd){
30641                 dd.lock();
30642             }
30643         }
30644         if(xy){
30645             el.avoidY = xy[1]-18;
30646             el.setXY(xy);
30647         }
30648         if(tm.animate){
30649             el.setOpacity(.1);
30650             el.setStyle("visibility", "visible");
30651             el.fadeIn({callback: afterShow});
30652         }else{
30653             afterShow();
30654         }
30655     };
30656     
30657     var afterShow = function(){
30658         if(ce){
30659             el.show();
30660             esc.enable();
30661             if(tm.autoDismiss && ce.autoHide !== false){
30662                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30663             }
30664         }
30665     };
30666     
30667     var hide = function(noanim){
30668         clearTimeout(dismissProc);
30669         clearTimeout(hideProc);
30670         ce = null;
30671         if(el.isVisible()){
30672             esc.disable();
30673             if(noanim !== true && tm.animate){
30674                 el.fadeOut({callback: afterHide});
30675             }else{
30676                 afterHide();
30677             } 
30678         }
30679     };
30680     
30681     var afterHide = function(){
30682         el.hide();
30683         if(removeCls){
30684             el.removeClass(removeCls);
30685             removeCls = null;
30686         }
30687     };
30688     
30689     return {
30690         /**
30691         * @cfg {Number} minWidth
30692         * The minimum width of the quick tip (defaults to 40)
30693         */
30694        minWidth : 40,
30695         /**
30696         * @cfg {Number} maxWidth
30697         * The maximum width of the quick tip (defaults to 300)
30698         */
30699        maxWidth : 300,
30700         /**
30701         * @cfg {Boolean} interceptTitles
30702         * True to automatically use the element's DOM title value if available (defaults to false)
30703         */
30704        interceptTitles : false,
30705         /**
30706         * @cfg {Boolean} trackMouse
30707         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30708         */
30709        trackMouse : false,
30710         /**
30711         * @cfg {Boolean} hideOnClick
30712         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30713         */
30714        hideOnClick : true,
30715         /**
30716         * @cfg {Number} showDelay
30717         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30718         */
30719        showDelay : 500,
30720         /**
30721         * @cfg {Number} hideDelay
30722         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30723         */
30724        hideDelay : 200,
30725         /**
30726         * @cfg {Boolean} autoHide
30727         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30728         * Used in conjunction with hideDelay.
30729         */
30730        autoHide : true,
30731         /**
30732         * @cfg {Boolean}
30733         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30734         * (defaults to true).  Used in conjunction with autoDismissDelay.
30735         */
30736        autoDismiss : true,
30737         /**
30738         * @cfg {Number}
30739         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30740         */
30741        autoDismissDelay : 5000,
30742        /**
30743         * @cfg {Boolean} animate
30744         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30745         */
30746        animate : false,
30747
30748        /**
30749         * @cfg {String} title
30750         * Title text to display (defaults to '').  This can be any valid HTML markup.
30751         */
30752         title: '',
30753        /**
30754         * @cfg {String} text
30755         * Body text to display (defaults to '').  This can be any valid HTML markup.
30756         */
30757         text : '',
30758        /**
30759         * @cfg {String} cls
30760         * A CSS class to apply to the base quick tip element (defaults to '').
30761         */
30762         cls : '',
30763        /**
30764         * @cfg {Number} width
30765         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30766         * minWidth or maxWidth.
30767         */
30768         width : null,
30769
30770     /**
30771      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30772      * or display QuickTips in a page.
30773      */
30774        init : function(){
30775           tm = Roo.QuickTips;
30776           cfg = tm.tagConfig;
30777           if(!inited){
30778               if(!Roo.isReady){ // allow calling of init() before onReady
30779                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30780                   return;
30781               }
30782               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30783               el.fxDefaults = {stopFx: true};
30784               // maximum custom styling
30785               //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>');
30786               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>');              
30787               tipTitle = el.child('h3');
30788               tipTitle.enableDisplayMode("block");
30789               tipBody = el.child('div.x-tip-bd');
30790               tipBodyText = el.child('div.x-tip-bd-inner');
30791               //bdLeft = el.child('div.x-tip-bd-left');
30792               //bdRight = el.child('div.x-tip-bd-right');
30793               close = el.child('div.x-tip-close');
30794               close.enableDisplayMode("block");
30795               close.on("click", hide);
30796               var d = Roo.get(document);
30797               d.on("mousedown", onDown);
30798               d.on("mouseover", onOver);
30799               d.on("mouseout", onOut);
30800               d.on("mousemove", onMove);
30801               esc = d.addKeyListener(27, hide);
30802               esc.disable();
30803               if(Roo.dd.DD){
30804                   dd = el.initDD("default", null, {
30805                       onDrag : function(){
30806                           el.sync();  
30807                       }
30808                   });
30809                   dd.setHandleElId(tipTitle.id);
30810                   dd.lock();
30811               }
30812               inited = true;
30813           }
30814           this.enable(); 
30815        },
30816
30817     /**
30818      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30819      * are supported:
30820      * <pre>
30821 Property    Type                   Description
30822 ----------  ---------------------  ------------------------------------------------------------------------
30823 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30824      * </ul>
30825      * @param {Object} config The config object
30826      */
30827        register : function(config){
30828            var cs = config instanceof Array ? config : arguments;
30829            for(var i = 0, len = cs.length; i < len; i++) {
30830                var c = cs[i];
30831                var target = c.target;
30832                if(target){
30833                    if(target instanceof Array){
30834                        for(var j = 0, jlen = target.length; j < jlen; j++){
30835                            tagEls[target[j]] = c;
30836                        }
30837                    }else{
30838                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30839                    }
30840                }
30841            }
30842        },
30843
30844     /**
30845      * Removes this quick tip from its element and destroys it.
30846      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30847      */
30848        unregister : function(el){
30849            delete tagEls[Roo.id(el)];
30850        },
30851
30852     /**
30853      * Enable this quick tip.
30854      */
30855        enable : function(){
30856            if(inited && disabled){
30857                locks.pop();
30858                if(locks.length < 1){
30859                    disabled = false;
30860                }
30861            }
30862        },
30863
30864     /**
30865      * Disable this quick tip.
30866      */
30867        disable : function(){
30868           disabled = true;
30869           clearTimeout(showProc);
30870           clearTimeout(hideProc);
30871           clearTimeout(dismissProc);
30872           if(ce){
30873               hide(true);
30874           }
30875           locks.push(1);
30876        },
30877
30878     /**
30879      * Returns true if the quick tip is enabled, else false.
30880      */
30881        isEnabled : function(){
30882             return !disabled;
30883        },
30884
30885         // private
30886        tagConfig : {
30887            namespace : "ext",
30888            attribute : "qtip",
30889            width : "width",
30890            target : "target",
30891            title : "qtitle",
30892            hide : "hide",
30893            cls : "qclass"
30894        }
30895    };
30896 }();
30897
30898 // backwards compat
30899 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30900  * Based on:
30901  * Ext JS Library 1.1.1
30902  * Copyright(c) 2006-2007, Ext JS, LLC.
30903  *
30904  * Originally Released Under LGPL - original licence link has changed is not relivant.
30905  *
30906  * Fork - LGPL
30907  * <script type="text/javascript">
30908  */
30909  
30910
30911 /**
30912  * @class Roo.tree.TreePanel
30913  * @extends Roo.data.Tree
30914
30915  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30916  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30917  * @cfg {Boolean} enableDD true to enable drag and drop
30918  * @cfg {Boolean} enableDrag true to enable just drag
30919  * @cfg {Boolean} enableDrop true to enable just drop
30920  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30921  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30922  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30923  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30924  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30925  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30926  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30927  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30928  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30929  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30930  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30931  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30932  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30933  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30934  * @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>
30935  * @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>
30936  * 
30937  * @constructor
30938  * @param {String/HTMLElement/Element} el The container element
30939  * @param {Object} config
30940  */
30941 Roo.tree.TreePanel = function(el, config){
30942     var root = false;
30943     var loader = false;
30944     if (config.root) {
30945         root = config.root;
30946         delete config.root;
30947     }
30948     if (config.loader) {
30949         loader = config.loader;
30950         delete config.loader;
30951     }
30952     
30953     Roo.apply(this, config);
30954     Roo.tree.TreePanel.superclass.constructor.call(this);
30955     this.el = Roo.get(el);
30956     this.el.addClass('x-tree');
30957     //console.log(root);
30958     if (root) {
30959         this.setRootNode( Roo.factory(root, Roo.tree));
30960     }
30961     if (loader) {
30962         this.loader = Roo.factory(loader, Roo.tree);
30963     }
30964    /**
30965     * Read-only. The id of the container element becomes this TreePanel's id.
30966     */
30967     this.id = this.el.id;
30968     this.addEvents({
30969         /**
30970         * @event beforeload
30971         * Fires before a node is loaded, return false to cancel
30972         * @param {Node} node The node being loaded
30973         */
30974         "beforeload" : true,
30975         /**
30976         * @event load
30977         * Fires when a node is loaded
30978         * @param {Node} node The node that was loaded
30979         */
30980         "load" : true,
30981         /**
30982         * @event textchange
30983         * Fires when the text for a node is changed
30984         * @param {Node} node The node
30985         * @param {String} text The new text
30986         * @param {String} oldText The old text
30987         */
30988         "textchange" : true,
30989         /**
30990         * @event beforeexpand
30991         * Fires before a node is expanded, return false to cancel.
30992         * @param {Node} node The node
30993         * @param {Boolean} deep
30994         * @param {Boolean} anim
30995         */
30996         "beforeexpand" : true,
30997         /**
30998         * @event beforecollapse
30999         * Fires before a node is collapsed, return false to cancel.
31000         * @param {Node} node The node
31001         * @param {Boolean} deep
31002         * @param {Boolean} anim
31003         */
31004         "beforecollapse" : true,
31005         /**
31006         * @event expand
31007         * Fires when a node is expanded
31008         * @param {Node} node The node
31009         */
31010         "expand" : true,
31011         /**
31012         * @event disabledchange
31013         * Fires when the disabled status of a node changes
31014         * @param {Node} node The node
31015         * @param {Boolean} disabled
31016         */
31017         "disabledchange" : true,
31018         /**
31019         * @event collapse
31020         * Fires when a node is collapsed
31021         * @param {Node} node The node
31022         */
31023         "collapse" : true,
31024         /**
31025         * @event beforeclick
31026         * Fires before click processing on a node. Return false to cancel the default action.
31027         * @param {Node} node The node
31028         * @param {Roo.EventObject} e The event object
31029         */
31030         "beforeclick":true,
31031         /**
31032         * @event checkchange
31033         * Fires when a node with a checkbox's checked property changes
31034         * @param {Node} this This node
31035         * @param {Boolean} checked
31036         */
31037         "checkchange":true,
31038         /**
31039         * @event click
31040         * Fires when a node is clicked
31041         * @param {Node} node The node
31042         * @param {Roo.EventObject} e The event object
31043         */
31044         "click":true,
31045         /**
31046         * @event dblclick
31047         * Fires when a node is double clicked
31048         * @param {Node} node The node
31049         * @param {Roo.EventObject} e The event object
31050         */
31051         "dblclick":true,
31052         /**
31053         * @event contextmenu
31054         * Fires when a node is right clicked
31055         * @param {Node} node The node
31056         * @param {Roo.EventObject} e The event object
31057         */
31058         "contextmenu":true,
31059         /**
31060         * @event beforechildrenrendered
31061         * Fires right before the child nodes for a node are rendered
31062         * @param {Node} node The node
31063         */
31064         "beforechildrenrendered":true,
31065         /**
31066         * @event startdrag
31067         * Fires when a node starts being dragged
31068         * @param {Roo.tree.TreePanel} this
31069         * @param {Roo.tree.TreeNode} node
31070         * @param {event} e The raw browser event
31071         */ 
31072        "startdrag" : true,
31073        /**
31074         * @event enddrag
31075         * Fires when a drag operation is complete
31076         * @param {Roo.tree.TreePanel} this
31077         * @param {Roo.tree.TreeNode} node
31078         * @param {event} e The raw browser event
31079         */
31080        "enddrag" : true,
31081        /**
31082         * @event dragdrop
31083         * Fires when a dragged node is dropped on a valid DD target
31084         * @param {Roo.tree.TreePanel} this
31085         * @param {Roo.tree.TreeNode} node
31086         * @param {DD} dd The dd it was dropped on
31087         * @param {event} e The raw browser event
31088         */
31089        "dragdrop" : true,
31090        /**
31091         * @event beforenodedrop
31092         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31093         * passed to handlers has the following properties:<br />
31094         * <ul style="padding:5px;padding-left:16px;">
31095         * <li>tree - The TreePanel</li>
31096         * <li>target - The node being targeted for the drop</li>
31097         * <li>data - The drag data from the drag source</li>
31098         * <li>point - The point of the drop - append, above or below</li>
31099         * <li>source - The drag source</li>
31100         * <li>rawEvent - Raw mouse event</li>
31101         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31102         * to be inserted by setting them on this object.</li>
31103         * <li>cancel - Set this to true to cancel the drop.</li>
31104         * </ul>
31105         * @param {Object} dropEvent
31106         */
31107        "beforenodedrop" : true,
31108        /**
31109         * @event nodedrop
31110         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31111         * passed to handlers has the following properties:<br />
31112         * <ul style="padding:5px;padding-left:16px;">
31113         * <li>tree - The TreePanel</li>
31114         * <li>target - The node being targeted for the drop</li>
31115         * <li>data - The drag data from the drag source</li>
31116         * <li>point - The point of the drop - append, above or below</li>
31117         * <li>source - The drag source</li>
31118         * <li>rawEvent - Raw mouse event</li>
31119         * <li>dropNode - Dropped node(s).</li>
31120         * </ul>
31121         * @param {Object} dropEvent
31122         */
31123        "nodedrop" : true,
31124         /**
31125         * @event nodedragover
31126         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31127         * passed to handlers has the following properties:<br />
31128         * <ul style="padding:5px;padding-left:16px;">
31129         * <li>tree - The TreePanel</li>
31130         * <li>target - The node being targeted for the drop</li>
31131         * <li>data - The drag data from the drag source</li>
31132         * <li>point - The point of the drop - append, above or below</li>
31133         * <li>source - The drag source</li>
31134         * <li>rawEvent - Raw mouse event</li>
31135         * <li>dropNode - Drop node(s) provided by the source.</li>
31136         * <li>cancel - Set this to true to signal drop not allowed.</li>
31137         * </ul>
31138         * @param {Object} dragOverEvent
31139         */
31140        "nodedragover" : true
31141         
31142     });
31143     if(this.singleExpand){
31144        this.on("beforeexpand", this.restrictExpand, this);
31145     }
31146     if (this.editor) {
31147         this.editor.tree = this;
31148         this.editor = Roo.factory(this.editor, Roo.tree);
31149     }
31150     
31151     if (this.selModel) {
31152         this.selModel = Roo.factory(this.selModel, Roo.tree);
31153     }
31154    
31155 };
31156 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31157     rootVisible : true,
31158     animate: Roo.enableFx,
31159     lines : true,
31160     enableDD : false,
31161     hlDrop : Roo.enableFx,
31162   
31163     renderer: false,
31164     
31165     rendererTip: false,
31166     // private
31167     restrictExpand : function(node){
31168         var p = node.parentNode;
31169         if(p){
31170             if(p.expandedChild && p.expandedChild.parentNode == p){
31171                 p.expandedChild.collapse();
31172             }
31173             p.expandedChild = node;
31174         }
31175     },
31176
31177     // private override
31178     setRootNode : function(node){
31179         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31180         if(!this.rootVisible){
31181             node.ui = new Roo.tree.RootTreeNodeUI(node);
31182         }
31183         return node;
31184     },
31185
31186     /**
31187      * Returns the container element for this TreePanel
31188      */
31189     getEl : function(){
31190         return this.el;
31191     },
31192
31193     /**
31194      * Returns the default TreeLoader for this TreePanel
31195      */
31196     getLoader : function(){
31197         return this.loader;
31198     },
31199
31200     /**
31201      * Expand all nodes
31202      */
31203     expandAll : function(){
31204         this.root.expand(true);
31205     },
31206
31207     /**
31208      * Collapse all nodes
31209      */
31210     collapseAll : function(){
31211         this.root.collapse(true);
31212     },
31213
31214     /**
31215      * Returns the selection model used by this TreePanel
31216      */
31217     getSelectionModel : function(){
31218         if(!this.selModel){
31219             this.selModel = new Roo.tree.DefaultSelectionModel();
31220         }
31221         return this.selModel;
31222     },
31223
31224     /**
31225      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31226      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31227      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31228      * @return {Array}
31229      */
31230     getChecked : function(a, startNode){
31231         startNode = startNode || this.root;
31232         var r = [];
31233         var f = function(){
31234             if(this.attributes.checked){
31235                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31236             }
31237         }
31238         startNode.cascade(f);
31239         return r;
31240     },
31241
31242     /**
31243      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31244      * @param {String} path
31245      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31246      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31247      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31248      */
31249     expandPath : function(path, attr, callback){
31250         attr = attr || "id";
31251         var keys = path.split(this.pathSeparator);
31252         var curNode = this.root;
31253         if(curNode.attributes[attr] != keys[1]){ // invalid root
31254             if(callback){
31255                 callback(false, null);
31256             }
31257             return;
31258         }
31259         var index = 1;
31260         var f = function(){
31261             if(++index == keys.length){
31262                 if(callback){
31263                     callback(true, curNode);
31264                 }
31265                 return;
31266             }
31267             var c = curNode.findChild(attr, keys[index]);
31268             if(!c){
31269                 if(callback){
31270                     callback(false, curNode);
31271                 }
31272                 return;
31273             }
31274             curNode = c;
31275             c.expand(false, false, f);
31276         };
31277         curNode.expand(false, false, f);
31278     },
31279
31280     /**
31281      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31282      * @param {String} path
31283      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31284      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31285      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31286      */
31287     selectPath : function(path, attr, callback){
31288         attr = attr || "id";
31289         var keys = path.split(this.pathSeparator);
31290         var v = keys.pop();
31291         if(keys.length > 0){
31292             var f = function(success, node){
31293                 if(success && node){
31294                     var n = node.findChild(attr, v);
31295                     if(n){
31296                         n.select();
31297                         if(callback){
31298                             callback(true, n);
31299                         }
31300                     }else if(callback){
31301                         callback(false, n);
31302                     }
31303                 }else{
31304                     if(callback){
31305                         callback(false, n);
31306                     }
31307                 }
31308             };
31309             this.expandPath(keys.join(this.pathSeparator), attr, f);
31310         }else{
31311             this.root.select();
31312             if(callback){
31313                 callback(true, this.root);
31314             }
31315         }
31316     },
31317
31318     getTreeEl : function(){
31319         return this.el;
31320     },
31321
31322     /**
31323      * Trigger rendering of this TreePanel
31324      */
31325     render : function(){
31326         if (this.innerCt) {
31327             return this; // stop it rendering more than once!!
31328         }
31329         
31330         this.innerCt = this.el.createChild({tag:"ul",
31331                cls:"x-tree-root-ct " +
31332                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31333
31334         if(this.containerScroll){
31335             Roo.dd.ScrollManager.register(this.el);
31336         }
31337         if((this.enableDD || this.enableDrop) && !this.dropZone){
31338            /**
31339             * The dropZone used by this tree if drop is enabled
31340             * @type Roo.tree.TreeDropZone
31341             */
31342              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31343                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31344            });
31345         }
31346         if((this.enableDD || this.enableDrag) && !this.dragZone){
31347            /**
31348             * The dragZone used by this tree if drag is enabled
31349             * @type Roo.tree.TreeDragZone
31350             */
31351             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31352                ddGroup: this.ddGroup || "TreeDD",
31353                scroll: this.ddScroll
31354            });
31355         }
31356         this.getSelectionModel().init(this);
31357         if (!this.root) {
31358             console.log("ROOT not set in tree");
31359             return;
31360         }
31361         this.root.render();
31362         if(!this.rootVisible){
31363             this.root.renderChildren();
31364         }
31365         return this;
31366     }
31367 });/*
31368  * Based on:
31369  * Ext JS Library 1.1.1
31370  * Copyright(c) 2006-2007, Ext JS, LLC.
31371  *
31372  * Originally Released Under LGPL - original licence link has changed is not relivant.
31373  *
31374  * Fork - LGPL
31375  * <script type="text/javascript">
31376  */
31377  
31378
31379 /**
31380  * @class Roo.tree.DefaultSelectionModel
31381  * @extends Roo.util.Observable
31382  * The default single selection for a TreePanel.
31383  * @param {Object} cfg Configuration
31384  */
31385 Roo.tree.DefaultSelectionModel = function(cfg){
31386    this.selNode = null;
31387    
31388    
31389    
31390    this.addEvents({
31391        /**
31392         * @event selectionchange
31393         * Fires when the selected node changes
31394         * @param {DefaultSelectionModel} this
31395         * @param {TreeNode} node the new selection
31396         */
31397        "selectionchange" : true,
31398
31399        /**
31400         * @event beforeselect
31401         * Fires before the selected node changes, return false to cancel the change
31402         * @param {DefaultSelectionModel} this
31403         * @param {TreeNode} node the new selection
31404         * @param {TreeNode} node the old selection
31405         */
31406        "beforeselect" : true
31407    });
31408    
31409     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31410 };
31411
31412 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31413     init : function(tree){
31414         this.tree = tree;
31415         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31416         tree.on("click", this.onNodeClick, this);
31417     },
31418     
31419     onNodeClick : function(node, e){
31420         if (e.ctrlKey && this.selNode == node)  {
31421             this.unselect(node);
31422             return;
31423         }
31424         this.select(node);
31425     },
31426     
31427     /**
31428      * Select a node.
31429      * @param {TreeNode} node The node to select
31430      * @return {TreeNode} The selected node
31431      */
31432     select : function(node){
31433         var last = this.selNode;
31434         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31435             if(last){
31436                 last.ui.onSelectedChange(false);
31437             }
31438             this.selNode = node;
31439             node.ui.onSelectedChange(true);
31440             this.fireEvent("selectionchange", this, node, last);
31441         }
31442         return node;
31443     },
31444     
31445     /**
31446      * Deselect a node.
31447      * @param {TreeNode} node The node to unselect
31448      */
31449     unselect : function(node){
31450         if(this.selNode == node){
31451             this.clearSelections();
31452         }    
31453     },
31454     
31455     /**
31456      * Clear all selections
31457      */
31458     clearSelections : function(){
31459         var n = this.selNode;
31460         if(n){
31461             n.ui.onSelectedChange(false);
31462             this.selNode = null;
31463             this.fireEvent("selectionchange", this, null);
31464         }
31465         return n;
31466     },
31467     
31468     /**
31469      * Get the selected node
31470      * @return {TreeNode} The selected node
31471      */
31472     getSelectedNode : function(){
31473         return this.selNode;    
31474     },
31475     
31476     /**
31477      * Returns true if the node is selected
31478      * @param {TreeNode} node The node to check
31479      * @return {Boolean}
31480      */
31481     isSelected : function(node){
31482         return this.selNode == node;  
31483     },
31484
31485     /**
31486      * Selects the node above the selected node in the tree, intelligently walking the nodes
31487      * @return TreeNode The new selection
31488      */
31489     selectPrevious : function(){
31490         var s = this.selNode || this.lastSelNode;
31491         if(!s){
31492             return null;
31493         }
31494         var ps = s.previousSibling;
31495         if(ps){
31496             if(!ps.isExpanded() || ps.childNodes.length < 1){
31497                 return this.select(ps);
31498             } else{
31499                 var lc = ps.lastChild;
31500                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31501                     lc = lc.lastChild;
31502                 }
31503                 return this.select(lc);
31504             }
31505         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31506             return this.select(s.parentNode);
31507         }
31508         return null;
31509     },
31510
31511     /**
31512      * Selects the node above the selected node in the tree, intelligently walking the nodes
31513      * @return TreeNode The new selection
31514      */
31515     selectNext : function(){
31516         var s = this.selNode || this.lastSelNode;
31517         if(!s){
31518             return null;
31519         }
31520         if(s.firstChild && s.isExpanded()){
31521              return this.select(s.firstChild);
31522          }else if(s.nextSibling){
31523              return this.select(s.nextSibling);
31524          }else if(s.parentNode){
31525             var newS = null;
31526             s.parentNode.bubble(function(){
31527                 if(this.nextSibling){
31528                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31529                     return false;
31530                 }
31531             });
31532             return newS;
31533          }
31534         return null;
31535     },
31536
31537     onKeyDown : function(e){
31538         var s = this.selNode || this.lastSelNode;
31539         // undesirable, but required
31540         var sm = this;
31541         if(!s){
31542             return;
31543         }
31544         var k = e.getKey();
31545         switch(k){
31546              case e.DOWN:
31547                  e.stopEvent();
31548                  this.selectNext();
31549              break;
31550              case e.UP:
31551                  e.stopEvent();
31552                  this.selectPrevious();
31553              break;
31554              case e.RIGHT:
31555                  e.preventDefault();
31556                  if(s.hasChildNodes()){
31557                      if(!s.isExpanded()){
31558                          s.expand();
31559                      }else if(s.firstChild){
31560                          this.select(s.firstChild, e);
31561                      }
31562                  }
31563              break;
31564              case e.LEFT:
31565                  e.preventDefault();
31566                  if(s.hasChildNodes() && s.isExpanded()){
31567                      s.collapse();
31568                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31569                      this.select(s.parentNode, e);
31570                  }
31571              break;
31572         };
31573     }
31574 });
31575
31576 /**
31577  * @class Roo.tree.MultiSelectionModel
31578  * @extends Roo.util.Observable
31579  * Multi selection for a TreePanel.
31580  * @param {Object} cfg Configuration
31581  */
31582 Roo.tree.MultiSelectionModel = function(){
31583    this.selNodes = [];
31584    this.selMap = {};
31585    this.addEvents({
31586        /**
31587         * @event selectionchange
31588         * Fires when the selected nodes change
31589         * @param {MultiSelectionModel} this
31590         * @param {Array} nodes Array of the selected nodes
31591         */
31592        "selectionchange" : true
31593    });
31594    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31595    
31596 };
31597
31598 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31599     init : function(tree){
31600         this.tree = tree;
31601         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31602         tree.on("click", this.onNodeClick, this);
31603     },
31604     
31605     onNodeClick : function(node, e){
31606         this.select(node, e, e.ctrlKey);
31607     },
31608     
31609     /**
31610      * Select a node.
31611      * @param {TreeNode} node The node to select
31612      * @param {EventObject} e (optional) An event associated with the selection
31613      * @param {Boolean} keepExisting True to retain existing selections
31614      * @return {TreeNode} The selected node
31615      */
31616     select : function(node, e, keepExisting){
31617         if(keepExisting !== true){
31618             this.clearSelections(true);
31619         }
31620         if(this.isSelected(node)){
31621             this.lastSelNode = node;
31622             return node;
31623         }
31624         this.selNodes.push(node);
31625         this.selMap[node.id] = node;
31626         this.lastSelNode = node;
31627         node.ui.onSelectedChange(true);
31628         this.fireEvent("selectionchange", this, this.selNodes);
31629         return node;
31630     },
31631     
31632     /**
31633      * Deselect a node.
31634      * @param {TreeNode} node The node to unselect
31635      */
31636     unselect : function(node){
31637         if(this.selMap[node.id]){
31638             node.ui.onSelectedChange(false);
31639             var sn = this.selNodes;
31640             var index = -1;
31641             if(sn.indexOf){
31642                 index = sn.indexOf(node);
31643             }else{
31644                 for(var i = 0, len = sn.length; i < len; i++){
31645                     if(sn[i] == node){
31646                         index = i;
31647                         break;
31648                     }
31649                 }
31650             }
31651             if(index != -1){
31652                 this.selNodes.splice(index, 1);
31653             }
31654             delete this.selMap[node.id];
31655             this.fireEvent("selectionchange", this, this.selNodes);
31656         }
31657     },
31658     
31659     /**
31660      * Clear all selections
31661      */
31662     clearSelections : function(suppressEvent){
31663         var sn = this.selNodes;
31664         if(sn.length > 0){
31665             for(var i = 0, len = sn.length; i < len; i++){
31666                 sn[i].ui.onSelectedChange(false);
31667             }
31668             this.selNodes = [];
31669             this.selMap = {};
31670             if(suppressEvent !== true){
31671                 this.fireEvent("selectionchange", this, this.selNodes);
31672             }
31673         }
31674     },
31675     
31676     /**
31677      * Returns true if the node is selected
31678      * @param {TreeNode} node The node to check
31679      * @return {Boolean}
31680      */
31681     isSelected : function(node){
31682         return this.selMap[node.id] ? true : false;  
31683     },
31684     
31685     /**
31686      * Returns an array of the selected nodes
31687      * @return {Array}
31688      */
31689     getSelectedNodes : function(){
31690         return this.selNodes;    
31691     },
31692
31693     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31694
31695     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31696
31697     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31698 });/*
31699  * Based on:
31700  * Ext JS Library 1.1.1
31701  * Copyright(c) 2006-2007, Ext JS, LLC.
31702  *
31703  * Originally Released Under LGPL - original licence link has changed is not relivant.
31704  *
31705  * Fork - LGPL
31706  * <script type="text/javascript">
31707  */
31708  
31709 /**
31710  * @class Roo.tree.TreeNode
31711  * @extends Roo.data.Node
31712  * @cfg {String} text The text for this node
31713  * @cfg {Boolean} expanded true to start the node expanded
31714  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31715  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31716  * @cfg {Boolean} disabled true to start the node disabled
31717  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31718  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31719  * @cfg {String} cls A css class to be added to the node
31720  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31721  * @cfg {String} href URL of the link used for the node (defaults to #)
31722  * @cfg {String} hrefTarget target frame for the link
31723  * @cfg {String} qtip An Ext QuickTip for the node
31724  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31725  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31726  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31727  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31728  * (defaults to undefined with no checkbox rendered)
31729  * @constructor
31730  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31731  */
31732 Roo.tree.TreeNode = function(attributes){
31733     attributes = attributes || {};
31734     if(typeof attributes == "string"){
31735         attributes = {text: attributes};
31736     }
31737     this.childrenRendered = false;
31738     this.rendered = false;
31739     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31740     this.expanded = attributes.expanded === true;
31741     this.isTarget = attributes.isTarget !== false;
31742     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31743     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31744
31745     /**
31746      * Read-only. The text for this node. To change it use setText().
31747      * @type String
31748      */
31749     this.text = attributes.text;
31750     /**
31751      * True if this node is disabled.
31752      * @type Boolean
31753      */
31754     this.disabled = attributes.disabled === true;
31755
31756     this.addEvents({
31757         /**
31758         * @event textchange
31759         * Fires when the text for this node is changed
31760         * @param {Node} this This node
31761         * @param {String} text The new text
31762         * @param {String} oldText The old text
31763         */
31764         "textchange" : true,
31765         /**
31766         * @event beforeexpand
31767         * Fires before this node is expanded, return false to cancel.
31768         * @param {Node} this This node
31769         * @param {Boolean} deep
31770         * @param {Boolean} anim
31771         */
31772         "beforeexpand" : true,
31773         /**
31774         * @event beforecollapse
31775         * Fires before this node is collapsed, return false to cancel.
31776         * @param {Node} this This node
31777         * @param {Boolean} deep
31778         * @param {Boolean} anim
31779         */
31780         "beforecollapse" : true,
31781         /**
31782         * @event expand
31783         * Fires when this node is expanded
31784         * @param {Node} this This node
31785         */
31786         "expand" : true,
31787         /**
31788         * @event disabledchange
31789         * Fires when the disabled status of this node changes
31790         * @param {Node} this This node
31791         * @param {Boolean} disabled
31792         */
31793         "disabledchange" : true,
31794         /**
31795         * @event collapse
31796         * Fires when this node is collapsed
31797         * @param {Node} this This node
31798         */
31799         "collapse" : true,
31800         /**
31801         * @event beforeclick
31802         * Fires before click processing. Return false to cancel the default action.
31803         * @param {Node} this This node
31804         * @param {Roo.EventObject} e The event object
31805         */
31806         "beforeclick":true,
31807         /**
31808         * @event checkchange
31809         * Fires when a node with a checkbox's checked property changes
31810         * @param {Node} this This node
31811         * @param {Boolean} checked
31812         */
31813         "checkchange":true,
31814         /**
31815         * @event click
31816         * Fires when this node is clicked
31817         * @param {Node} this This node
31818         * @param {Roo.EventObject} e The event object
31819         */
31820         "click":true,
31821         /**
31822         * @event dblclick
31823         * Fires when this node is double clicked
31824         * @param {Node} this This node
31825         * @param {Roo.EventObject} e The event object
31826         */
31827         "dblclick":true,
31828         /**
31829         * @event contextmenu
31830         * Fires when this node is right clicked
31831         * @param {Node} this This node
31832         * @param {Roo.EventObject} e The event object
31833         */
31834         "contextmenu":true,
31835         /**
31836         * @event beforechildrenrendered
31837         * Fires right before the child nodes for this node are rendered
31838         * @param {Node} this This node
31839         */
31840         "beforechildrenrendered":true
31841     });
31842
31843     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31844
31845     /**
31846      * Read-only. The UI for this node
31847      * @type TreeNodeUI
31848      */
31849     this.ui = new uiClass(this);
31850 };
31851 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31852     preventHScroll: true,
31853     /**
31854      * Returns true if this node is expanded
31855      * @return {Boolean}
31856      */
31857     isExpanded : function(){
31858         return this.expanded;
31859     },
31860
31861     /**
31862      * Returns the UI object for this node
31863      * @return {TreeNodeUI}
31864      */
31865     getUI : function(){
31866         return this.ui;
31867     },
31868
31869     // private override
31870     setFirstChild : function(node){
31871         var of = this.firstChild;
31872         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31873         if(this.childrenRendered && of && node != of){
31874             of.renderIndent(true, true);
31875         }
31876         if(this.rendered){
31877             this.renderIndent(true, true);
31878         }
31879     },
31880
31881     // private override
31882     setLastChild : function(node){
31883         var ol = this.lastChild;
31884         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31885         if(this.childrenRendered && ol && node != ol){
31886             ol.renderIndent(true, true);
31887         }
31888         if(this.rendered){
31889             this.renderIndent(true, true);
31890         }
31891     },
31892
31893     // these methods are overridden to provide lazy rendering support
31894     // private override
31895     appendChild : function(){
31896         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31897         if(node && this.childrenRendered){
31898             node.render();
31899         }
31900         this.ui.updateExpandIcon();
31901         return node;
31902     },
31903
31904     // private override
31905     removeChild : function(node){
31906         this.ownerTree.getSelectionModel().unselect(node);
31907         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31908         // if it's been rendered remove dom node
31909         if(this.childrenRendered){
31910             node.ui.remove();
31911         }
31912         if(this.childNodes.length < 1){
31913             this.collapse(false, false);
31914         }else{
31915             this.ui.updateExpandIcon();
31916         }
31917         if(!this.firstChild) {
31918             this.childrenRendered = false;
31919         }
31920         return node;
31921     },
31922
31923     // private override
31924     insertBefore : function(node, refNode){
31925         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31926         if(newNode && refNode && this.childrenRendered){
31927             node.render();
31928         }
31929         this.ui.updateExpandIcon();
31930         return newNode;
31931     },
31932
31933     /**
31934      * Sets the text for this node
31935      * @param {String} text
31936      */
31937     setText : function(text){
31938         var oldText = this.text;
31939         this.text = text;
31940         this.attributes.text = text;
31941         if(this.rendered){ // event without subscribing
31942             this.ui.onTextChange(this, text, oldText);
31943         }
31944         this.fireEvent("textchange", this, text, oldText);
31945     },
31946
31947     /**
31948      * Triggers selection of this node
31949      */
31950     select : function(){
31951         this.getOwnerTree().getSelectionModel().select(this);
31952     },
31953
31954     /**
31955      * Triggers deselection of this node
31956      */
31957     unselect : function(){
31958         this.getOwnerTree().getSelectionModel().unselect(this);
31959     },
31960
31961     /**
31962      * Returns true if this node is selected
31963      * @return {Boolean}
31964      */
31965     isSelected : function(){
31966         return this.getOwnerTree().getSelectionModel().isSelected(this);
31967     },
31968
31969     /**
31970      * Expand this node.
31971      * @param {Boolean} deep (optional) True to expand all children as well
31972      * @param {Boolean} anim (optional) false to cancel the default animation
31973      * @param {Function} callback (optional) A callback to be called when
31974      * expanding this node completes (does not wait for deep expand to complete).
31975      * Called with 1 parameter, this node.
31976      */
31977     expand : function(deep, anim, callback){
31978         if(!this.expanded){
31979             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31980                 return;
31981             }
31982             if(!this.childrenRendered){
31983                 this.renderChildren();
31984             }
31985             this.expanded = true;
31986             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31987                 this.ui.animExpand(function(){
31988                     this.fireEvent("expand", this);
31989                     if(typeof callback == "function"){
31990                         callback(this);
31991                     }
31992                     if(deep === true){
31993                         this.expandChildNodes(true);
31994                     }
31995                 }.createDelegate(this));
31996                 return;
31997             }else{
31998                 this.ui.expand();
31999                 this.fireEvent("expand", this);
32000                 if(typeof callback == "function"){
32001                     callback(this);
32002                 }
32003             }
32004         }else{
32005            if(typeof callback == "function"){
32006                callback(this);
32007            }
32008         }
32009         if(deep === true){
32010             this.expandChildNodes(true);
32011         }
32012     },
32013
32014     isHiddenRoot : function(){
32015         return this.isRoot && !this.getOwnerTree().rootVisible;
32016     },
32017
32018     /**
32019      * Collapse this node.
32020      * @param {Boolean} deep (optional) True to collapse all children as well
32021      * @param {Boolean} anim (optional) false to cancel the default animation
32022      */
32023     collapse : function(deep, anim){
32024         if(this.expanded && !this.isHiddenRoot()){
32025             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32026                 return;
32027             }
32028             this.expanded = false;
32029             if((this.getOwnerTree().animate && anim !== false) || anim){
32030                 this.ui.animCollapse(function(){
32031                     this.fireEvent("collapse", this);
32032                     if(deep === true){
32033                         this.collapseChildNodes(true);
32034                     }
32035                 }.createDelegate(this));
32036                 return;
32037             }else{
32038                 this.ui.collapse();
32039                 this.fireEvent("collapse", this);
32040             }
32041         }
32042         if(deep === true){
32043             var cs = this.childNodes;
32044             for(var i = 0, len = cs.length; i < len; i++) {
32045                 cs[i].collapse(true, false);
32046             }
32047         }
32048     },
32049
32050     // private
32051     delayedExpand : function(delay){
32052         if(!this.expandProcId){
32053             this.expandProcId = this.expand.defer(delay, this);
32054         }
32055     },
32056
32057     // private
32058     cancelExpand : function(){
32059         if(this.expandProcId){
32060             clearTimeout(this.expandProcId);
32061         }
32062         this.expandProcId = false;
32063     },
32064
32065     /**
32066      * Toggles expanded/collapsed state of the node
32067      */
32068     toggle : function(){
32069         if(this.expanded){
32070             this.collapse();
32071         }else{
32072             this.expand();
32073         }
32074     },
32075
32076     /**
32077      * Ensures all parent nodes are expanded
32078      */
32079     ensureVisible : function(callback){
32080         var tree = this.getOwnerTree();
32081         tree.expandPath(this.parentNode.getPath(), false, function(){
32082             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32083             Roo.callback(callback);
32084         }.createDelegate(this));
32085     },
32086
32087     /**
32088      * Expand all child nodes
32089      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32090      */
32091     expandChildNodes : function(deep){
32092         var cs = this.childNodes;
32093         for(var i = 0, len = cs.length; i < len; i++) {
32094                 cs[i].expand(deep);
32095         }
32096     },
32097
32098     /**
32099      * Collapse all child nodes
32100      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32101      */
32102     collapseChildNodes : function(deep){
32103         var cs = this.childNodes;
32104         for(var i = 0, len = cs.length; i < len; i++) {
32105                 cs[i].collapse(deep);
32106         }
32107     },
32108
32109     /**
32110      * Disables this node
32111      */
32112     disable : function(){
32113         this.disabled = true;
32114         this.unselect();
32115         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32116             this.ui.onDisableChange(this, true);
32117         }
32118         this.fireEvent("disabledchange", this, true);
32119     },
32120
32121     /**
32122      * Enables this node
32123      */
32124     enable : function(){
32125         this.disabled = false;
32126         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32127             this.ui.onDisableChange(this, false);
32128         }
32129         this.fireEvent("disabledchange", this, false);
32130     },
32131
32132     // private
32133     renderChildren : function(suppressEvent){
32134         if(suppressEvent !== false){
32135             this.fireEvent("beforechildrenrendered", this);
32136         }
32137         var cs = this.childNodes;
32138         for(var i = 0, len = cs.length; i < len; i++){
32139             cs[i].render(true);
32140         }
32141         this.childrenRendered = true;
32142     },
32143
32144     // private
32145     sort : function(fn, scope){
32146         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32147         if(this.childrenRendered){
32148             var cs = this.childNodes;
32149             for(var i = 0, len = cs.length; i < len; i++){
32150                 cs[i].render(true);
32151             }
32152         }
32153     },
32154
32155     // private
32156     render : function(bulkRender){
32157         this.ui.render(bulkRender);
32158         if(!this.rendered){
32159             this.rendered = true;
32160             if(this.expanded){
32161                 this.expanded = false;
32162                 this.expand(false, false);
32163             }
32164         }
32165     },
32166
32167     // private
32168     renderIndent : function(deep, refresh){
32169         if(refresh){
32170             this.ui.childIndent = null;
32171         }
32172         this.ui.renderIndent();
32173         if(deep === true && this.childrenRendered){
32174             var cs = this.childNodes;
32175             for(var i = 0, len = cs.length; i < len; i++){
32176                 cs[i].renderIndent(true, refresh);
32177             }
32178         }
32179     }
32180 });/*
32181  * Based on:
32182  * Ext JS Library 1.1.1
32183  * Copyright(c) 2006-2007, Ext JS, LLC.
32184  *
32185  * Originally Released Under LGPL - original licence link has changed is not relivant.
32186  *
32187  * Fork - LGPL
32188  * <script type="text/javascript">
32189  */
32190  
32191 /**
32192  * @class Roo.tree.AsyncTreeNode
32193  * @extends Roo.tree.TreeNode
32194  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32195  * @constructor
32196  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32197  */
32198  Roo.tree.AsyncTreeNode = function(config){
32199     this.loaded = false;
32200     this.loading = false;
32201     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32202     /**
32203     * @event beforeload
32204     * Fires before this node is loaded, return false to cancel
32205     * @param {Node} this This node
32206     */
32207     this.addEvents({'beforeload':true, 'load': true});
32208     /**
32209     * @event load
32210     * Fires when this node is loaded
32211     * @param {Node} this This node
32212     */
32213     /**
32214      * The loader used by this node (defaults to using the tree's defined loader)
32215      * @type TreeLoader
32216      * @property loader
32217      */
32218 };
32219 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32220     expand : function(deep, anim, callback){
32221         if(this.loading){ // if an async load is already running, waiting til it's done
32222             var timer;
32223             var f = function(){
32224                 if(!this.loading){ // done loading
32225                     clearInterval(timer);
32226                     this.expand(deep, anim, callback);
32227                 }
32228             }.createDelegate(this);
32229             timer = setInterval(f, 200);
32230             return;
32231         }
32232         if(!this.loaded){
32233             if(this.fireEvent("beforeload", this) === false){
32234                 return;
32235             }
32236             this.loading = true;
32237             this.ui.beforeLoad(this);
32238             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32239             if(loader){
32240                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32241                 return;
32242             }
32243         }
32244         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32245     },
32246     
32247     /**
32248      * Returns true if this node is currently loading
32249      * @return {Boolean}
32250      */
32251     isLoading : function(){
32252         return this.loading;  
32253     },
32254     
32255     loadComplete : function(deep, anim, callback){
32256         this.loading = false;
32257         this.loaded = true;
32258         this.ui.afterLoad(this);
32259         this.fireEvent("load", this);
32260         this.expand(deep, anim, callback);
32261     },
32262     
32263     /**
32264      * Returns true if this node has been loaded
32265      * @return {Boolean}
32266      */
32267     isLoaded : function(){
32268         return this.loaded;
32269     },
32270     
32271     hasChildNodes : function(){
32272         if(!this.isLeaf() && !this.loaded){
32273             return true;
32274         }else{
32275             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32276         }
32277     },
32278
32279     /**
32280      * Trigger a reload for this node
32281      * @param {Function} callback
32282      */
32283     reload : function(callback){
32284         this.collapse(false, false);
32285         while(this.firstChild){
32286             this.removeChild(this.firstChild);
32287         }
32288         this.childrenRendered = false;
32289         this.loaded = false;
32290         if(this.isHiddenRoot()){
32291             this.expanded = false;
32292         }
32293         this.expand(false, false, callback);
32294     }
32295 });/*
32296  * Based on:
32297  * Ext JS Library 1.1.1
32298  * Copyright(c) 2006-2007, Ext JS, LLC.
32299  *
32300  * Originally Released Under LGPL - original licence link has changed is not relivant.
32301  *
32302  * Fork - LGPL
32303  * <script type="text/javascript">
32304  */
32305  
32306 /**
32307  * @class Roo.tree.TreeNodeUI
32308  * @constructor
32309  * @param {Object} node The node to render
32310  * The TreeNode UI implementation is separate from the
32311  * tree implementation. Unless you are customizing the tree UI,
32312  * you should never have to use this directly.
32313  */
32314 Roo.tree.TreeNodeUI = function(node){
32315     this.node = node;
32316     this.rendered = false;
32317     this.animating = false;
32318     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32319 };
32320
32321 Roo.tree.TreeNodeUI.prototype = {
32322     removeChild : function(node){
32323         if(this.rendered){
32324             this.ctNode.removeChild(node.ui.getEl());
32325         }
32326     },
32327
32328     beforeLoad : function(){
32329          this.addClass("x-tree-node-loading");
32330     },
32331
32332     afterLoad : function(){
32333          this.removeClass("x-tree-node-loading");
32334     },
32335
32336     onTextChange : function(node, text, oldText){
32337         if(this.rendered){
32338             this.textNode.innerHTML = text;
32339         }
32340     },
32341
32342     onDisableChange : function(node, state){
32343         this.disabled = state;
32344         if(state){
32345             this.addClass("x-tree-node-disabled");
32346         }else{
32347             this.removeClass("x-tree-node-disabled");
32348         }
32349     },
32350
32351     onSelectedChange : function(state){
32352         if(state){
32353             this.focus();
32354             this.addClass("x-tree-selected");
32355         }else{
32356             //this.blur();
32357             this.removeClass("x-tree-selected");
32358         }
32359     },
32360
32361     onMove : function(tree, node, oldParent, newParent, index, refNode){
32362         this.childIndent = null;
32363         if(this.rendered){
32364             var targetNode = newParent.ui.getContainer();
32365             if(!targetNode){//target not rendered
32366                 this.holder = document.createElement("div");
32367                 this.holder.appendChild(this.wrap);
32368                 return;
32369             }
32370             var insertBefore = refNode ? refNode.ui.getEl() : null;
32371             if(insertBefore){
32372                 targetNode.insertBefore(this.wrap, insertBefore);
32373             }else{
32374                 targetNode.appendChild(this.wrap);
32375             }
32376             this.node.renderIndent(true);
32377         }
32378     },
32379
32380     addClass : function(cls){
32381         if(this.elNode){
32382             Roo.fly(this.elNode).addClass(cls);
32383         }
32384     },
32385
32386     removeClass : function(cls){
32387         if(this.elNode){
32388             Roo.fly(this.elNode).removeClass(cls);
32389         }
32390     },
32391
32392     remove : function(){
32393         if(this.rendered){
32394             this.holder = document.createElement("div");
32395             this.holder.appendChild(this.wrap);
32396         }
32397     },
32398
32399     fireEvent : function(){
32400         return this.node.fireEvent.apply(this.node, arguments);
32401     },
32402
32403     initEvents : function(){
32404         this.node.on("move", this.onMove, this);
32405         var E = Roo.EventManager;
32406         var a = this.anchor;
32407
32408         var el = Roo.fly(a, '_treeui');
32409
32410         if(Roo.isOpera){ // opera render bug ignores the CSS
32411             el.setStyle("text-decoration", "none");
32412         }
32413
32414         el.on("click", this.onClick, this);
32415         el.on("dblclick", this.onDblClick, this);
32416
32417         if(this.checkbox){
32418             Roo.EventManager.on(this.checkbox,
32419                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32420         }
32421
32422         el.on("contextmenu", this.onContextMenu, this);
32423
32424         var icon = Roo.fly(this.iconNode);
32425         icon.on("click", this.onClick, this);
32426         icon.on("dblclick", this.onDblClick, this);
32427         icon.on("contextmenu", this.onContextMenu, this);
32428         E.on(this.ecNode, "click", this.ecClick, this, true);
32429
32430         if(this.node.disabled){
32431             this.addClass("x-tree-node-disabled");
32432         }
32433         if(this.node.hidden){
32434             this.addClass("x-tree-node-disabled");
32435         }
32436         var ot = this.node.getOwnerTree();
32437         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32438         if(dd && (!this.node.isRoot || ot.rootVisible)){
32439             Roo.dd.Registry.register(this.elNode, {
32440                 node: this.node,
32441                 handles: this.getDDHandles(),
32442                 isHandle: false
32443             });
32444         }
32445     },
32446
32447     getDDHandles : function(){
32448         return [this.iconNode, this.textNode];
32449     },
32450
32451     hide : function(){
32452         if(this.rendered){
32453             this.wrap.style.display = "none";
32454         }
32455     },
32456
32457     show : function(){
32458         if(this.rendered){
32459             this.wrap.style.display = "";
32460         }
32461     },
32462
32463     onContextMenu : function(e){
32464         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32465             e.preventDefault();
32466             this.focus();
32467             this.fireEvent("contextmenu", this.node, e);
32468         }
32469     },
32470
32471     onClick : function(e){
32472         if(this.dropping){
32473             e.stopEvent();
32474             return;
32475         }
32476         if(this.fireEvent("beforeclick", this.node, e) !== false){
32477             if(!this.disabled && this.node.attributes.href){
32478                 this.fireEvent("click", this.node, e);
32479                 return;
32480             }
32481             e.preventDefault();
32482             if(this.disabled){
32483                 return;
32484             }
32485
32486             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32487                 this.node.toggle();
32488             }
32489
32490             this.fireEvent("click", this.node, e);
32491         }else{
32492             e.stopEvent();
32493         }
32494     },
32495
32496     onDblClick : function(e){
32497         e.preventDefault();
32498         if(this.disabled){
32499             return;
32500         }
32501         if(this.checkbox){
32502             this.toggleCheck();
32503         }
32504         if(!this.animating && this.node.hasChildNodes()){
32505             this.node.toggle();
32506         }
32507         this.fireEvent("dblclick", this.node, e);
32508     },
32509
32510     onCheckChange : function(){
32511         var checked = this.checkbox.checked;
32512         this.node.attributes.checked = checked;
32513         this.fireEvent('checkchange', this.node, checked);
32514     },
32515
32516     ecClick : function(e){
32517         if(!this.animating && this.node.hasChildNodes()){
32518             this.node.toggle();
32519         }
32520     },
32521
32522     startDrop : function(){
32523         this.dropping = true;
32524     },
32525
32526     // delayed drop so the click event doesn't get fired on a drop
32527     endDrop : function(){
32528        setTimeout(function(){
32529            this.dropping = false;
32530        }.createDelegate(this), 50);
32531     },
32532
32533     expand : function(){
32534         this.updateExpandIcon();
32535         this.ctNode.style.display = "";
32536     },
32537
32538     focus : function(){
32539         if(!this.node.preventHScroll){
32540             try{this.anchor.focus();
32541             }catch(e){}
32542         }else if(!Roo.isIE){
32543             try{
32544                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32545                 var l = noscroll.scrollLeft;
32546                 this.anchor.focus();
32547                 noscroll.scrollLeft = l;
32548             }catch(e){}
32549         }
32550     },
32551
32552     toggleCheck : function(value){
32553         var cb = this.checkbox;
32554         if(cb){
32555             cb.checked = (value === undefined ? !cb.checked : value);
32556         }
32557     },
32558
32559     blur : function(){
32560         try{
32561             this.anchor.blur();
32562         }catch(e){}
32563     },
32564
32565     animExpand : function(callback){
32566         var ct = Roo.get(this.ctNode);
32567         ct.stopFx();
32568         if(!this.node.hasChildNodes()){
32569             this.updateExpandIcon();
32570             this.ctNode.style.display = "";
32571             Roo.callback(callback);
32572             return;
32573         }
32574         this.animating = true;
32575         this.updateExpandIcon();
32576
32577         ct.slideIn('t', {
32578            callback : function(){
32579                this.animating = false;
32580                Roo.callback(callback);
32581             },
32582             scope: this,
32583             duration: this.node.ownerTree.duration || .25
32584         });
32585     },
32586
32587     highlight : function(){
32588         var tree = this.node.getOwnerTree();
32589         Roo.fly(this.wrap).highlight(
32590             tree.hlColor || "C3DAF9",
32591             {endColor: tree.hlBaseColor}
32592         );
32593     },
32594
32595     collapse : function(){
32596         this.updateExpandIcon();
32597         this.ctNode.style.display = "none";
32598     },
32599
32600     animCollapse : function(callback){
32601         var ct = Roo.get(this.ctNode);
32602         ct.enableDisplayMode('block');
32603         ct.stopFx();
32604
32605         this.animating = true;
32606         this.updateExpandIcon();
32607
32608         ct.slideOut('t', {
32609             callback : function(){
32610                this.animating = false;
32611                Roo.callback(callback);
32612             },
32613             scope: this,
32614             duration: this.node.ownerTree.duration || .25
32615         });
32616     },
32617
32618     getContainer : function(){
32619         return this.ctNode;
32620     },
32621
32622     getEl : function(){
32623         return this.wrap;
32624     },
32625
32626     appendDDGhost : function(ghostNode){
32627         ghostNode.appendChild(this.elNode.cloneNode(true));
32628     },
32629
32630     getDDRepairXY : function(){
32631         return Roo.lib.Dom.getXY(this.iconNode);
32632     },
32633
32634     onRender : function(){
32635         this.render();
32636     },
32637
32638     render : function(bulkRender){
32639         var n = this.node, a = n.attributes;
32640         var targetNode = n.parentNode ?
32641               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32642
32643         if(!this.rendered){
32644             this.rendered = true;
32645
32646             this.renderElements(n, a, targetNode, bulkRender);
32647
32648             if(a.qtip){
32649                if(this.textNode.setAttributeNS){
32650                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32651                    if(a.qtipTitle){
32652                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32653                    }
32654                }else{
32655                    this.textNode.setAttribute("ext:qtip", a.qtip);
32656                    if(a.qtipTitle){
32657                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32658                    }
32659                }
32660             }else if(a.qtipCfg){
32661                 a.qtipCfg.target = Roo.id(this.textNode);
32662                 Roo.QuickTips.register(a.qtipCfg);
32663             }
32664             this.initEvents();
32665             if(!this.node.expanded){
32666                 this.updateExpandIcon();
32667             }
32668         }else{
32669             if(bulkRender === true) {
32670                 targetNode.appendChild(this.wrap);
32671             }
32672         }
32673     },
32674
32675     renderElements : function(n, a, targetNode, bulkRender)
32676     {
32677         // add some indent caching, this helps performance when rendering a large tree
32678         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32679         var t = n.getOwnerTree();
32680         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32681         if (typeof(n.attributes.html) != 'undefined') {
32682             txt = n.attributes.html;
32683         }
32684         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32685         var cb = typeof a.checked == 'boolean';
32686         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32687         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32688             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32689             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32690             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32691             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32692             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32693              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32694                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32695             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32696             "</li>"];
32697
32698         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32699             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32700                                 n.nextSibling.ui.getEl(), buf.join(""));
32701         }else{
32702             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32703         }
32704
32705         this.elNode = this.wrap.childNodes[0];
32706         this.ctNode = this.wrap.childNodes[1];
32707         var cs = this.elNode.childNodes;
32708         this.indentNode = cs[0];
32709         this.ecNode = cs[1];
32710         this.iconNode = cs[2];
32711         var index = 3;
32712         if(cb){
32713             this.checkbox = cs[3];
32714             index++;
32715         }
32716         this.anchor = cs[index];
32717         this.textNode = cs[index].firstChild;
32718     },
32719
32720     getAnchor : function(){
32721         return this.anchor;
32722     },
32723
32724     getTextEl : function(){
32725         return this.textNode;
32726     },
32727
32728     getIconEl : function(){
32729         return this.iconNode;
32730     },
32731
32732     isChecked : function(){
32733         return this.checkbox ? this.checkbox.checked : false;
32734     },
32735
32736     updateExpandIcon : function(){
32737         if(this.rendered){
32738             var n = this.node, c1, c2;
32739             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32740             var hasChild = n.hasChildNodes();
32741             if(hasChild){
32742                 if(n.expanded){
32743                     cls += "-minus";
32744                     c1 = "x-tree-node-collapsed";
32745                     c2 = "x-tree-node-expanded";
32746                 }else{
32747                     cls += "-plus";
32748                     c1 = "x-tree-node-expanded";
32749                     c2 = "x-tree-node-collapsed";
32750                 }
32751                 if(this.wasLeaf){
32752                     this.removeClass("x-tree-node-leaf");
32753                     this.wasLeaf = false;
32754                 }
32755                 if(this.c1 != c1 || this.c2 != c2){
32756                     Roo.fly(this.elNode).replaceClass(c1, c2);
32757                     this.c1 = c1; this.c2 = c2;
32758                 }
32759             }else{
32760                 // this changes non-leafs into leafs if they have no children.
32761                 // it's not very rational behaviour..
32762                 
32763                 if(!this.wasLeaf && this.node.leaf){
32764                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32765                     delete this.c1;
32766                     delete this.c2;
32767                     this.wasLeaf = true;
32768                 }
32769             }
32770             var ecc = "x-tree-ec-icon "+cls;
32771             if(this.ecc != ecc){
32772                 this.ecNode.className = ecc;
32773                 this.ecc = ecc;
32774             }
32775         }
32776     },
32777
32778     getChildIndent : function(){
32779         if(!this.childIndent){
32780             var buf = [];
32781             var p = this.node;
32782             while(p){
32783                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32784                     if(!p.isLast()) {
32785                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32786                     } else {
32787                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32788                     }
32789                 }
32790                 p = p.parentNode;
32791             }
32792             this.childIndent = buf.join("");
32793         }
32794         return this.childIndent;
32795     },
32796
32797     renderIndent : function(){
32798         if(this.rendered){
32799             var indent = "";
32800             var p = this.node.parentNode;
32801             if(p){
32802                 indent = p.ui.getChildIndent();
32803             }
32804             if(this.indentMarkup != indent){ // don't rerender if not required
32805                 this.indentNode.innerHTML = indent;
32806                 this.indentMarkup = indent;
32807             }
32808             this.updateExpandIcon();
32809         }
32810     }
32811 };
32812
32813 Roo.tree.RootTreeNodeUI = function(){
32814     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32815 };
32816 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32817     render : function(){
32818         if(!this.rendered){
32819             var targetNode = this.node.ownerTree.innerCt.dom;
32820             this.node.expanded = true;
32821             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32822             this.wrap = this.ctNode = targetNode.firstChild;
32823         }
32824     },
32825     collapse : function(){
32826     },
32827     expand : function(){
32828     }
32829 });/*
32830  * Based on:
32831  * Ext JS Library 1.1.1
32832  * Copyright(c) 2006-2007, Ext JS, LLC.
32833  *
32834  * Originally Released Under LGPL - original licence link has changed is not relivant.
32835  *
32836  * Fork - LGPL
32837  * <script type="text/javascript">
32838  */
32839 /**
32840  * @class Roo.tree.TreeLoader
32841  * @extends Roo.util.Observable
32842  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32843  * nodes from a specified URL. The response must be a javascript Array definition
32844  * who's elements are node definition objects. eg:
32845  * <pre><code>
32846    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32847     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32848 </code></pre>
32849  * <br><br>
32850  * A server request is sent, and child nodes are loaded only when a node is expanded.
32851  * The loading node's id is passed to the server under the parameter name "node" to
32852  * enable the server to produce the correct child nodes.
32853  * <br><br>
32854  * To pass extra parameters, an event handler may be attached to the "beforeload"
32855  * event, and the parameters specified in the TreeLoader's baseParams property:
32856  * <pre><code>
32857     myTreeLoader.on("beforeload", function(treeLoader, node) {
32858         this.baseParams.category = node.attributes.category;
32859     }, this);
32860 </code></pre><
32861  * This would pass an HTTP parameter called "category" to the server containing
32862  * the value of the Node's "category" attribute.
32863  * @constructor
32864  * Creates a new Treeloader.
32865  * @param {Object} config A config object containing config properties.
32866  */
32867 Roo.tree.TreeLoader = function(config){
32868     this.baseParams = {};
32869     this.requestMethod = "POST";
32870     Roo.apply(this, config);
32871
32872     this.addEvents({
32873     
32874         /**
32875          * @event beforeload
32876          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32877          * @param {Object} This TreeLoader object.
32878          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32879          * @param {Object} callback The callback function specified in the {@link #load} call.
32880          */
32881         beforeload : true,
32882         /**
32883          * @event load
32884          * Fires when the node has been successfuly loaded.
32885          * @param {Object} This TreeLoader object.
32886          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32887          * @param {Object} response The response object containing the data from the server.
32888          */
32889         load : true,
32890         /**
32891          * @event loadexception
32892          * Fires if the network request failed.
32893          * @param {Object} This TreeLoader object.
32894          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32895          * @param {Object} response The response object containing the data from the server.
32896          */
32897         loadexception : true,
32898         /**
32899          * @event create
32900          * Fires before a node is created, enabling you to return custom Node types 
32901          * @param {Object} This TreeLoader object.
32902          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32903          */
32904         create : true
32905     });
32906
32907     Roo.tree.TreeLoader.superclass.constructor.call(this);
32908 };
32909
32910 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32911     /**
32912     * @cfg {String} dataUrl The URL from which to request a Json string which
32913     * specifies an array of node definition object representing the child nodes
32914     * to be loaded.
32915     */
32916     /**
32917     * @cfg {Object} baseParams (optional) An object containing properties which
32918     * specify HTTP parameters to be passed to each request for child nodes.
32919     */
32920     /**
32921     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32922     * created by this loader. If the attributes sent by the server have an attribute in this object,
32923     * they take priority.
32924     */
32925     /**
32926     * @cfg {Object} uiProviders (optional) An object containing properties which
32927     * 
32928     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32929     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32930     * <i>uiProvider</i> attribute of a returned child node is a string rather
32931     * than a reference to a TreeNodeUI implementation, this that string value
32932     * is used as a property name in the uiProviders object. You can define the provider named
32933     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32934     */
32935     uiProviders : {},
32936
32937     /**
32938     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32939     * child nodes before loading.
32940     */
32941     clearOnLoad : true,
32942
32943     /**
32944     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32945     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32946     * Grid query { data : [ .....] }
32947     */
32948     
32949     root : false,
32950      /**
32951     * @cfg {String} queryParam (optional) 
32952     * Name of the query as it will be passed on the querystring (defaults to 'node')
32953     * eg. the request will be ?node=[id]
32954     */
32955     
32956     
32957     queryParam: false,
32958     
32959     /**
32960      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32961      * This is called automatically when a node is expanded, but may be used to reload
32962      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32963      * @param {Roo.tree.TreeNode} node
32964      * @param {Function} callback
32965      */
32966     load : function(node, callback){
32967         if(this.clearOnLoad){
32968             while(node.firstChild){
32969                 node.removeChild(node.firstChild);
32970             }
32971         }
32972         if(node.attributes.children){ // preloaded json children
32973             var cs = node.attributes.children;
32974             for(var i = 0, len = cs.length; i < len; i++){
32975                 node.appendChild(this.createNode(cs[i]));
32976             }
32977             if(typeof callback == "function"){
32978                 callback();
32979             }
32980         }else if(this.dataUrl){
32981             this.requestData(node, callback);
32982         }
32983     },
32984
32985     getParams: function(node){
32986         var buf = [], bp = this.baseParams;
32987         for(var key in bp){
32988             if(typeof bp[key] != "function"){
32989                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32990             }
32991         }
32992         var n = this.queryParam === false ? 'node' : this.queryParam;
32993         buf.push(n + "=", encodeURIComponent(node.id));
32994         return buf.join("");
32995     },
32996
32997     requestData : function(node, callback){
32998         if(this.fireEvent("beforeload", this, node, callback) !== false){
32999             this.transId = Roo.Ajax.request({
33000                 method:this.requestMethod,
33001                 url: this.dataUrl||this.url,
33002                 success: this.handleResponse,
33003                 failure: this.handleFailure,
33004                 scope: this,
33005                 argument: {callback: callback, node: node},
33006                 params: this.getParams(node)
33007             });
33008         }else{
33009             // if the load is cancelled, make sure we notify
33010             // the node that we are done
33011             if(typeof callback == "function"){
33012                 callback();
33013             }
33014         }
33015     },
33016
33017     isLoading : function(){
33018         return this.transId ? true : false;
33019     },
33020
33021     abort : function(){
33022         if(this.isLoading()){
33023             Roo.Ajax.abort(this.transId);
33024         }
33025     },
33026
33027     // private
33028     createNode : function(attr)
33029     {
33030         // apply baseAttrs, nice idea Corey!
33031         if(this.baseAttrs){
33032             Roo.applyIf(attr, this.baseAttrs);
33033         }
33034         if(this.applyLoader !== false){
33035             attr.loader = this;
33036         }
33037         // uiProvider = depreciated..
33038         
33039         if(typeof(attr.uiProvider) == 'string'){
33040            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33041                 /**  eval:var:attr */ eval(attr.uiProvider);
33042         }
33043         if(typeof(this.uiProviders['default']) != 'undefined') {
33044             attr.uiProvider = this.uiProviders['default'];
33045         }
33046         
33047         this.fireEvent('create', this, attr);
33048         
33049         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33050         return(attr.leaf ?
33051                         new Roo.tree.TreeNode(attr) :
33052                         new Roo.tree.AsyncTreeNode(attr));
33053     },
33054
33055     processResponse : function(response, node, callback)
33056     {
33057         var json = response.responseText;
33058         try {
33059             
33060             var o = Roo.decode(json);
33061             
33062             if (!o.success) {
33063                 // it's a failure condition.
33064                 var a = response.argument;
33065                 this.fireEvent("loadexception", this, a.node, response);
33066                 Roo.log("Load failed - should have a handler really");
33067                 return;
33068             }
33069             
33070             if (this.root !== false) {
33071                 o = o[this.root];
33072             }
33073             
33074             for(var i = 0, len = o.length; i < len; i++){
33075                 var n = this.createNode(o[i]);
33076                 if(n){
33077                     node.appendChild(n);
33078                 }
33079             }
33080             if(typeof callback == "function"){
33081                 callback(this, node);
33082             }
33083         }catch(e){
33084             this.handleFailure(response);
33085         }
33086     },
33087
33088     handleResponse : function(response){
33089         this.transId = false;
33090         var a = response.argument;
33091         this.processResponse(response, a.node, a.callback);
33092         this.fireEvent("load", this, a.node, response);
33093     },
33094
33095     handleFailure : function(response)
33096     {
33097         // should handle failure better..
33098         this.transId = false;
33099         var a = response.argument;
33100         this.fireEvent("loadexception", this, a.node, response);
33101         if(typeof a.callback == "function"){
33102             a.callback(this, a.node);
33103         }
33104     }
33105 });/*
33106  * Based on:
33107  * Ext JS Library 1.1.1
33108  * Copyright(c) 2006-2007, Ext JS, LLC.
33109  *
33110  * Originally Released Under LGPL - original licence link has changed is not relivant.
33111  *
33112  * Fork - LGPL
33113  * <script type="text/javascript">
33114  */
33115
33116 /**
33117 * @class Roo.tree.TreeFilter
33118 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33119 * @param {TreePanel} tree
33120 * @param {Object} config (optional)
33121  */
33122 Roo.tree.TreeFilter = function(tree, config){
33123     this.tree = tree;
33124     this.filtered = {};
33125     Roo.apply(this, config);
33126 };
33127
33128 Roo.tree.TreeFilter.prototype = {
33129     clearBlank:false,
33130     reverse:false,
33131     autoClear:false,
33132     remove:false,
33133
33134      /**
33135      * Filter the data by a specific attribute.
33136      * @param {String/RegExp} value Either string that the attribute value
33137      * should start with or a RegExp to test against the attribute
33138      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33139      * @param {TreeNode} startNode (optional) The node to start the filter at.
33140      */
33141     filter : function(value, attr, startNode){
33142         attr = attr || "text";
33143         var f;
33144         if(typeof value == "string"){
33145             var vlen = value.length;
33146             // auto clear empty filter
33147             if(vlen == 0 && this.clearBlank){
33148                 this.clear();
33149                 return;
33150             }
33151             value = value.toLowerCase();
33152             f = function(n){
33153                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33154             };
33155         }else if(value.exec){ // regex?
33156             f = function(n){
33157                 return value.test(n.attributes[attr]);
33158             };
33159         }else{
33160             throw 'Illegal filter type, must be string or regex';
33161         }
33162         this.filterBy(f, null, startNode);
33163         },
33164
33165     /**
33166      * Filter by a function. The passed function will be called with each
33167      * node in the tree (or from the startNode). If the function returns true, the node is kept
33168      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33169      * @param {Function} fn The filter function
33170      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33171      */
33172     filterBy : function(fn, scope, startNode){
33173         startNode = startNode || this.tree.root;
33174         if(this.autoClear){
33175             this.clear();
33176         }
33177         var af = this.filtered, rv = this.reverse;
33178         var f = function(n){
33179             if(n == startNode){
33180                 return true;
33181             }
33182             if(af[n.id]){
33183                 return false;
33184             }
33185             var m = fn.call(scope || n, n);
33186             if(!m || rv){
33187                 af[n.id] = n;
33188                 n.ui.hide();
33189                 return false;
33190             }
33191             return true;
33192         };
33193         startNode.cascade(f);
33194         if(this.remove){
33195            for(var id in af){
33196                if(typeof id != "function"){
33197                    var n = af[id];
33198                    if(n && n.parentNode){
33199                        n.parentNode.removeChild(n);
33200                    }
33201                }
33202            }
33203         }
33204     },
33205
33206     /**
33207      * Clears the current filter. Note: with the "remove" option
33208      * set a filter cannot be cleared.
33209      */
33210     clear : function(){
33211         var t = this.tree;
33212         var af = this.filtered;
33213         for(var id in af){
33214             if(typeof id != "function"){
33215                 var n = af[id];
33216                 if(n){
33217                     n.ui.show();
33218                 }
33219             }
33220         }
33221         this.filtered = {};
33222     }
33223 };
33224 /*
33225  * Based on:
33226  * Ext JS Library 1.1.1
33227  * Copyright(c) 2006-2007, Ext JS, LLC.
33228  *
33229  * Originally Released Under LGPL - original licence link has changed is not relivant.
33230  *
33231  * Fork - LGPL
33232  * <script type="text/javascript">
33233  */
33234  
33235
33236 /**
33237  * @class Roo.tree.TreeSorter
33238  * Provides sorting of nodes in a TreePanel
33239  * 
33240  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33241  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33242  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33243  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33244  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33245  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33246  * @constructor
33247  * @param {TreePanel} tree
33248  * @param {Object} config
33249  */
33250 Roo.tree.TreeSorter = function(tree, config){
33251     Roo.apply(this, config);
33252     tree.on("beforechildrenrendered", this.doSort, this);
33253     tree.on("append", this.updateSort, this);
33254     tree.on("insert", this.updateSort, this);
33255     
33256     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33257     var p = this.property || "text";
33258     var sortType = this.sortType;
33259     var fs = this.folderSort;
33260     var cs = this.caseSensitive === true;
33261     var leafAttr = this.leafAttr || 'leaf';
33262
33263     this.sortFn = function(n1, n2){
33264         if(fs){
33265             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33266                 return 1;
33267             }
33268             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33269                 return -1;
33270             }
33271         }
33272         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33273         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33274         if(v1 < v2){
33275                         return dsc ? +1 : -1;
33276                 }else if(v1 > v2){
33277                         return dsc ? -1 : +1;
33278         }else{
33279                 return 0;
33280         }
33281     };
33282 };
33283
33284 Roo.tree.TreeSorter.prototype = {
33285     doSort : function(node){
33286         node.sort(this.sortFn);
33287     },
33288     
33289     compareNodes : function(n1, n2){
33290         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33291     },
33292     
33293     updateSort : function(tree, node){
33294         if(node.childrenRendered){
33295             this.doSort.defer(1, this, [node]);
33296         }
33297     }
33298 };/*
33299  * Based on:
33300  * Ext JS Library 1.1.1
33301  * Copyright(c) 2006-2007, Ext JS, LLC.
33302  *
33303  * Originally Released Under LGPL - original licence link has changed is not relivant.
33304  *
33305  * Fork - LGPL
33306  * <script type="text/javascript">
33307  */
33308
33309 if(Roo.dd.DropZone){
33310     
33311 Roo.tree.TreeDropZone = function(tree, config){
33312     this.allowParentInsert = false;
33313     this.allowContainerDrop = false;
33314     this.appendOnly = false;
33315     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33316     this.tree = tree;
33317     this.lastInsertClass = "x-tree-no-status";
33318     this.dragOverData = {};
33319 };
33320
33321 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33322     ddGroup : "TreeDD",
33323     
33324     expandDelay : 1000,
33325     
33326     expandNode : function(node){
33327         if(node.hasChildNodes() && !node.isExpanded()){
33328             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33329         }
33330     },
33331     
33332     queueExpand : function(node){
33333         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33334     },
33335     
33336     cancelExpand : function(){
33337         if(this.expandProcId){
33338             clearTimeout(this.expandProcId);
33339             this.expandProcId = false;
33340         }
33341     },
33342     
33343     isValidDropPoint : function(n, pt, dd, e, data){
33344         if(!n || !data){ return false; }
33345         var targetNode = n.node;
33346         var dropNode = data.node;
33347         // default drop rules
33348         if(!(targetNode && targetNode.isTarget && pt)){
33349             return false;
33350         }
33351         if(pt == "append" && targetNode.allowChildren === false){
33352             return false;
33353         }
33354         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33355             return false;
33356         }
33357         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33358             return false;
33359         }
33360         // reuse the object
33361         var overEvent = this.dragOverData;
33362         overEvent.tree = this.tree;
33363         overEvent.target = targetNode;
33364         overEvent.data = data;
33365         overEvent.point = pt;
33366         overEvent.source = dd;
33367         overEvent.rawEvent = e;
33368         overEvent.dropNode = dropNode;
33369         overEvent.cancel = false;  
33370         var result = this.tree.fireEvent("nodedragover", overEvent);
33371         return overEvent.cancel === false && result !== false;
33372     },
33373     
33374     getDropPoint : function(e, n, dd){
33375         var tn = n.node;
33376         if(tn.isRoot){
33377             return tn.allowChildren !== false ? "append" : false; // always append for root
33378         }
33379         var dragEl = n.ddel;
33380         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33381         var y = Roo.lib.Event.getPageY(e);
33382         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33383         
33384         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33385         var noAppend = tn.allowChildren === false;
33386         if(this.appendOnly || tn.parentNode.allowChildren === false){
33387             return noAppend ? false : "append";
33388         }
33389         var noBelow = false;
33390         if(!this.allowParentInsert){
33391             noBelow = tn.hasChildNodes() && tn.isExpanded();
33392         }
33393         var q = (b - t) / (noAppend ? 2 : 3);
33394         if(y >= t && y < (t + q)){
33395             return "above";
33396         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33397             return "below";
33398         }else{
33399             return "append";
33400         }
33401     },
33402     
33403     onNodeEnter : function(n, dd, e, data){
33404         this.cancelExpand();
33405     },
33406     
33407     onNodeOver : function(n, dd, e, data){
33408         var pt = this.getDropPoint(e, n, dd);
33409         var node = n.node;
33410         
33411         // auto node expand check
33412         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33413             this.queueExpand(node);
33414         }else if(pt != "append"){
33415             this.cancelExpand();
33416         }
33417         
33418         // set the insert point style on the target node
33419         var returnCls = this.dropNotAllowed;
33420         if(this.isValidDropPoint(n, pt, dd, e, data)){
33421            if(pt){
33422                var el = n.ddel;
33423                var cls;
33424                if(pt == "above"){
33425                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33426                    cls = "x-tree-drag-insert-above";
33427                }else if(pt == "below"){
33428                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33429                    cls = "x-tree-drag-insert-below";
33430                }else{
33431                    returnCls = "x-tree-drop-ok-append";
33432                    cls = "x-tree-drag-append";
33433                }
33434                if(this.lastInsertClass != cls){
33435                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33436                    this.lastInsertClass = cls;
33437                }
33438            }
33439        }
33440        return returnCls;
33441     },
33442     
33443     onNodeOut : function(n, dd, e, data){
33444         this.cancelExpand();
33445         this.removeDropIndicators(n);
33446     },
33447     
33448     onNodeDrop : function(n, dd, e, data){
33449         var point = this.getDropPoint(e, n, dd);
33450         var targetNode = n.node;
33451         targetNode.ui.startDrop();
33452         if(!this.isValidDropPoint(n, point, dd, e, data)){
33453             targetNode.ui.endDrop();
33454             return false;
33455         }
33456         // first try to find the drop node
33457         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33458         var dropEvent = {
33459             tree : this.tree,
33460             target: targetNode,
33461             data: data,
33462             point: point,
33463             source: dd,
33464             rawEvent: e,
33465             dropNode: dropNode,
33466             cancel: !dropNode   
33467         };
33468         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33469         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33470             targetNode.ui.endDrop();
33471             return false;
33472         }
33473         // allow target changing
33474         targetNode = dropEvent.target;
33475         if(point == "append" && !targetNode.isExpanded()){
33476             targetNode.expand(false, null, function(){
33477                 this.completeDrop(dropEvent);
33478             }.createDelegate(this));
33479         }else{
33480             this.completeDrop(dropEvent);
33481         }
33482         return true;
33483     },
33484     
33485     completeDrop : function(de){
33486         var ns = de.dropNode, p = de.point, t = de.target;
33487         if(!(ns instanceof Array)){
33488             ns = [ns];
33489         }
33490         var n;
33491         for(var i = 0, len = ns.length; i < len; i++){
33492             n = ns[i];
33493             if(p == "above"){
33494                 t.parentNode.insertBefore(n, t);
33495             }else if(p == "below"){
33496                 t.parentNode.insertBefore(n, t.nextSibling);
33497             }else{
33498                 t.appendChild(n);
33499             }
33500         }
33501         n.ui.focus();
33502         if(this.tree.hlDrop){
33503             n.ui.highlight();
33504         }
33505         t.ui.endDrop();
33506         this.tree.fireEvent("nodedrop", de);
33507     },
33508     
33509     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33510         if(this.tree.hlDrop){
33511             dropNode.ui.focus();
33512             dropNode.ui.highlight();
33513         }
33514         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33515     },
33516     
33517     getTree : function(){
33518         return this.tree;
33519     },
33520     
33521     removeDropIndicators : function(n){
33522         if(n && n.ddel){
33523             var el = n.ddel;
33524             Roo.fly(el).removeClass([
33525                     "x-tree-drag-insert-above",
33526                     "x-tree-drag-insert-below",
33527                     "x-tree-drag-append"]);
33528             this.lastInsertClass = "_noclass";
33529         }
33530     },
33531     
33532     beforeDragDrop : function(target, e, id){
33533         this.cancelExpand();
33534         return true;
33535     },
33536     
33537     afterRepair : function(data){
33538         if(data && Roo.enableFx){
33539             data.node.ui.highlight();
33540         }
33541         this.hideProxy();
33542     }    
33543 });
33544
33545 }
33546 /*
33547  * Based on:
33548  * Ext JS Library 1.1.1
33549  * Copyright(c) 2006-2007, Ext JS, LLC.
33550  *
33551  * Originally Released Under LGPL - original licence link has changed is not relivant.
33552  *
33553  * Fork - LGPL
33554  * <script type="text/javascript">
33555  */
33556  
33557
33558 if(Roo.dd.DragZone){
33559 Roo.tree.TreeDragZone = function(tree, config){
33560     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33561     this.tree = tree;
33562 };
33563
33564 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33565     ddGroup : "TreeDD",
33566     
33567     onBeforeDrag : function(data, e){
33568         var n = data.node;
33569         return n && n.draggable && !n.disabled;
33570     },
33571     
33572     onInitDrag : function(e){
33573         var data = this.dragData;
33574         this.tree.getSelectionModel().select(data.node);
33575         this.proxy.update("");
33576         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33577         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33578     },
33579     
33580     getRepairXY : function(e, data){
33581         return data.node.ui.getDDRepairXY();
33582     },
33583     
33584     onEndDrag : function(data, e){
33585         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33586     },
33587     
33588     onValidDrop : function(dd, e, id){
33589         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33590         this.hideProxy();
33591     },
33592     
33593     beforeInvalidDrop : function(e, id){
33594         // this scrolls the original position back into view
33595         var sm = this.tree.getSelectionModel();
33596         sm.clearSelections();
33597         sm.select(this.dragData.node);
33598     }
33599 });
33600 }/*
33601  * Based on:
33602  * Ext JS Library 1.1.1
33603  * Copyright(c) 2006-2007, Ext JS, LLC.
33604  *
33605  * Originally Released Under LGPL - original licence link has changed is not relivant.
33606  *
33607  * Fork - LGPL
33608  * <script type="text/javascript">
33609  */
33610 /**
33611  * @class Roo.tree.TreeEditor
33612  * @extends Roo.Editor
33613  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33614  * as the editor field.
33615  * @constructor
33616  * @param {Object} config (used to be the tree panel.)
33617  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33618  * 
33619  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33620  * @cfg {Roo.form.TextField|Object} field The field configuration
33621  *
33622  * 
33623  */
33624 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33625     var tree = config;
33626     var field;
33627     if (oldconfig) { // old style..
33628         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33629     } else {
33630         // new style..
33631         tree = config.tree;
33632         config.field = config.field  || {};
33633         config.field.xtype = 'TextField';
33634         field = Roo.factory(config.field, Roo.form);
33635     }
33636     config = config || {};
33637     
33638     
33639     this.addEvents({
33640         /**
33641          * @event beforenodeedit
33642          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33643          * false from the handler of this event.
33644          * @param {Editor} this
33645          * @param {Roo.tree.Node} node 
33646          */
33647         "beforenodeedit" : true
33648     });
33649     
33650     //Roo.log(config);
33651     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33652
33653     this.tree = tree;
33654
33655     tree.on('beforeclick', this.beforeNodeClick, this);
33656     tree.getTreeEl().on('mousedown', this.hide, this);
33657     this.on('complete', this.updateNode, this);
33658     this.on('beforestartedit', this.fitToTree, this);
33659     this.on('startedit', this.bindScroll, this, {delay:10});
33660     this.on('specialkey', this.onSpecialKey, this);
33661 };
33662
33663 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33664     /**
33665      * @cfg {String} alignment
33666      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33667      */
33668     alignment: "l-l",
33669     // inherit
33670     autoSize: false,
33671     /**
33672      * @cfg {Boolean} hideEl
33673      * True to hide the bound element while the editor is displayed (defaults to false)
33674      */
33675     hideEl : false,
33676     /**
33677      * @cfg {String} cls
33678      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33679      */
33680     cls: "x-small-editor x-tree-editor",
33681     /**
33682      * @cfg {Boolean} shim
33683      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33684      */
33685     shim:false,
33686     // inherit
33687     shadow:"frame",
33688     /**
33689      * @cfg {Number} maxWidth
33690      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33691      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33692      * scroll and client offsets into account prior to each edit.
33693      */
33694     maxWidth: 250,
33695
33696     editDelay : 350,
33697
33698     // private
33699     fitToTree : function(ed, el){
33700         var td = this.tree.getTreeEl().dom, nd = el.dom;
33701         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33702             td.scrollLeft = nd.offsetLeft;
33703         }
33704         var w = Math.min(
33705                 this.maxWidth,
33706                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33707         this.setSize(w, '');
33708         
33709         return this.fireEvent('beforenodeedit', this, this.editNode);
33710         
33711     },
33712
33713     // private
33714     triggerEdit : function(node){
33715         this.completeEdit();
33716         this.editNode = node;
33717         this.startEdit(node.ui.textNode, node.text);
33718     },
33719
33720     // private
33721     bindScroll : function(){
33722         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33723     },
33724
33725     // private
33726     beforeNodeClick : function(node, e){
33727         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33728         this.lastClick = new Date();
33729         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33730             e.stopEvent();
33731             this.triggerEdit(node);
33732             return false;
33733         }
33734         return true;
33735     },
33736
33737     // private
33738     updateNode : function(ed, value){
33739         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33740         this.editNode.setText(value);
33741     },
33742
33743     // private
33744     onHide : function(){
33745         Roo.tree.TreeEditor.superclass.onHide.call(this);
33746         if(this.editNode){
33747             this.editNode.ui.focus();
33748         }
33749     },
33750
33751     // private
33752     onSpecialKey : function(field, e){
33753         var k = e.getKey();
33754         if(k == e.ESC){
33755             e.stopEvent();
33756             this.cancelEdit();
33757         }else if(k == e.ENTER && !e.hasModifier()){
33758             e.stopEvent();
33759             this.completeEdit();
33760         }
33761     }
33762 });//<Script type="text/javascript">
33763 /*
33764  * Based on:
33765  * Ext JS Library 1.1.1
33766  * Copyright(c) 2006-2007, Ext JS, LLC.
33767  *
33768  * Originally Released Under LGPL - original licence link has changed is not relivant.
33769  *
33770  * Fork - LGPL
33771  * <script type="text/javascript">
33772  */
33773  
33774 /**
33775  * Not documented??? - probably should be...
33776  */
33777
33778 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33779     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33780     
33781     renderElements : function(n, a, targetNode, bulkRender){
33782         //consel.log("renderElements?");
33783         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33784
33785         var t = n.getOwnerTree();
33786         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33787         
33788         var cols = t.columns;
33789         var bw = t.borderWidth;
33790         var c = cols[0];
33791         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33792          var cb = typeof a.checked == "boolean";
33793         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33794         var colcls = 'x-t-' + tid + '-c0';
33795         var buf = [
33796             '<li class="x-tree-node">',
33797             
33798                 
33799                 '<div class="x-tree-node-el ', a.cls,'">',
33800                     // extran...
33801                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33802                 
33803                 
33804                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33805                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33806                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33807                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33808                            (a.iconCls ? ' '+a.iconCls : ''),
33809                            '" unselectable="on" />',
33810                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33811                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33812                              
33813                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33814                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33815                             '<span unselectable="on" qtip="' + tx + '">',
33816                              tx,
33817                              '</span></a>' ,
33818                     '</div>',
33819                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33820                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33821                  ];
33822         for(var i = 1, len = cols.length; i < len; i++){
33823             c = cols[i];
33824             colcls = 'x-t-' + tid + '-c' +i;
33825             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33826             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33827                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33828                       "</div>");
33829          }
33830          
33831          buf.push(
33832             '</a>',
33833             '<div class="x-clear"></div></div>',
33834             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33835             "</li>");
33836         
33837         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33838             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33839                                 n.nextSibling.ui.getEl(), buf.join(""));
33840         }else{
33841             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33842         }
33843         var el = this.wrap.firstChild;
33844         this.elRow = el;
33845         this.elNode = el.firstChild;
33846         this.ranchor = el.childNodes[1];
33847         this.ctNode = this.wrap.childNodes[1];
33848         var cs = el.firstChild.childNodes;
33849         this.indentNode = cs[0];
33850         this.ecNode = cs[1];
33851         this.iconNode = cs[2];
33852         var index = 3;
33853         if(cb){
33854             this.checkbox = cs[3];
33855             index++;
33856         }
33857         this.anchor = cs[index];
33858         
33859         this.textNode = cs[index].firstChild;
33860         
33861         //el.on("click", this.onClick, this);
33862         //el.on("dblclick", this.onDblClick, this);
33863         
33864         
33865        // console.log(this);
33866     },
33867     initEvents : function(){
33868         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33869         
33870             
33871         var a = this.ranchor;
33872
33873         var el = Roo.get(a);
33874
33875         if(Roo.isOpera){ // opera render bug ignores the CSS
33876             el.setStyle("text-decoration", "none");
33877         }
33878
33879         el.on("click", this.onClick, this);
33880         el.on("dblclick", this.onDblClick, this);
33881         el.on("contextmenu", this.onContextMenu, this);
33882         
33883     },
33884     
33885     /*onSelectedChange : function(state){
33886         if(state){
33887             this.focus();
33888             this.addClass("x-tree-selected");
33889         }else{
33890             //this.blur();
33891             this.removeClass("x-tree-selected");
33892         }
33893     },*/
33894     addClass : function(cls){
33895         if(this.elRow){
33896             Roo.fly(this.elRow).addClass(cls);
33897         }
33898         
33899     },
33900     
33901     
33902     removeClass : function(cls){
33903         if(this.elRow){
33904             Roo.fly(this.elRow).removeClass(cls);
33905         }
33906     }
33907
33908     
33909     
33910 });//<Script type="text/javascript">
33911
33912 /*
33913  * Based on:
33914  * Ext JS Library 1.1.1
33915  * Copyright(c) 2006-2007, Ext JS, LLC.
33916  *
33917  * Originally Released Under LGPL - original licence link has changed is not relivant.
33918  *
33919  * Fork - LGPL
33920  * <script type="text/javascript">
33921  */
33922  
33923
33924 /**
33925  * @class Roo.tree.ColumnTree
33926  * @extends Roo.data.TreePanel
33927  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33928  * @cfg {int} borderWidth  compined right/left border allowance
33929  * @constructor
33930  * @param {String/HTMLElement/Element} el The container element
33931  * @param {Object} config
33932  */
33933 Roo.tree.ColumnTree =  function(el, config)
33934 {
33935    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33936    this.addEvents({
33937         /**
33938         * @event resize
33939         * Fire this event on a container when it resizes
33940         * @param {int} w Width
33941         * @param {int} h Height
33942         */
33943        "resize" : true
33944     });
33945     this.on('resize', this.onResize, this);
33946 };
33947
33948 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33949     //lines:false,
33950     
33951     
33952     borderWidth: Roo.isBorderBox ? 0 : 2, 
33953     headEls : false,
33954     
33955     render : function(){
33956         // add the header.....
33957        
33958         Roo.tree.ColumnTree.superclass.render.apply(this);
33959         
33960         this.el.addClass('x-column-tree');
33961         
33962         this.headers = this.el.createChild(
33963             {cls:'x-tree-headers'},this.innerCt.dom);
33964    
33965         var cols = this.columns, c;
33966         var totalWidth = 0;
33967         this.headEls = [];
33968         var  len = cols.length;
33969         for(var i = 0; i < len; i++){
33970              c = cols[i];
33971              totalWidth += c.width;
33972             this.headEls.push(this.headers.createChild({
33973                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33974                  cn: {
33975                      cls:'x-tree-hd-text',
33976                      html: c.header
33977                  },
33978                  style:'width:'+(c.width-this.borderWidth)+'px;'
33979              }));
33980         }
33981         this.headers.createChild({cls:'x-clear'});
33982         // prevent floats from wrapping when clipped
33983         this.headers.setWidth(totalWidth);
33984         //this.innerCt.setWidth(totalWidth);
33985         this.innerCt.setStyle({ overflow: 'auto' });
33986         this.onResize(this.width, this.height);
33987              
33988         
33989     },
33990     onResize : function(w,h)
33991     {
33992         this.height = h;
33993         this.width = w;
33994         // resize cols..
33995         this.innerCt.setWidth(this.width);
33996         this.innerCt.setHeight(this.height-20);
33997         
33998         // headers...
33999         var cols = this.columns, c;
34000         var totalWidth = 0;
34001         var expEl = false;
34002         var len = cols.length;
34003         for(var i = 0; i < len; i++){
34004             c = cols[i];
34005             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34006                 // it's the expander..
34007                 expEl  = this.headEls[i];
34008                 continue;
34009             }
34010             totalWidth += c.width;
34011             
34012         }
34013         if (expEl) {
34014             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34015         }
34016         this.headers.setWidth(w-20);
34017
34018         
34019         
34020         
34021     }
34022 });
34023 /*
34024  * Based on:
34025  * Ext JS Library 1.1.1
34026  * Copyright(c) 2006-2007, Ext JS, LLC.
34027  *
34028  * Originally Released Under LGPL - original licence link has changed is not relivant.
34029  *
34030  * Fork - LGPL
34031  * <script type="text/javascript">
34032  */
34033  
34034 /**
34035  * @class Roo.menu.Menu
34036  * @extends Roo.util.Observable
34037  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34038  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34039  * @constructor
34040  * Creates a new Menu
34041  * @param {Object} config Configuration options
34042  */
34043 Roo.menu.Menu = function(config){
34044     Roo.apply(this, config);
34045     this.id = this.id || Roo.id();
34046     this.addEvents({
34047         /**
34048          * @event beforeshow
34049          * Fires before this menu is displayed
34050          * @param {Roo.menu.Menu} this
34051          */
34052         beforeshow : true,
34053         /**
34054          * @event beforehide
34055          * Fires before this menu is hidden
34056          * @param {Roo.menu.Menu} this
34057          */
34058         beforehide : true,
34059         /**
34060          * @event show
34061          * Fires after this menu is displayed
34062          * @param {Roo.menu.Menu} this
34063          */
34064         show : true,
34065         /**
34066          * @event hide
34067          * Fires after this menu is hidden
34068          * @param {Roo.menu.Menu} this
34069          */
34070         hide : true,
34071         /**
34072          * @event click
34073          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34074          * @param {Roo.menu.Menu} this
34075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34076          * @param {Roo.EventObject} e
34077          */
34078         click : true,
34079         /**
34080          * @event mouseover
34081          * Fires when the mouse is hovering over this menu
34082          * @param {Roo.menu.Menu} this
34083          * @param {Roo.EventObject} e
34084          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34085          */
34086         mouseover : true,
34087         /**
34088          * @event mouseout
34089          * Fires when the mouse exits this menu
34090          * @param {Roo.menu.Menu} this
34091          * @param {Roo.EventObject} e
34092          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34093          */
34094         mouseout : true,
34095         /**
34096          * @event itemclick
34097          * Fires when a menu item contained in this menu is clicked
34098          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34099          * @param {Roo.EventObject} e
34100          */
34101         itemclick: true
34102     });
34103     if (this.registerMenu) {
34104         Roo.menu.MenuMgr.register(this);
34105     }
34106     
34107     var mis = this.items;
34108     this.items = new Roo.util.MixedCollection();
34109     if(mis){
34110         this.add.apply(this, mis);
34111     }
34112 };
34113
34114 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34115     /**
34116      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34117      */
34118     minWidth : 120,
34119     /**
34120      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34121      * for bottom-right shadow (defaults to "sides")
34122      */
34123     shadow : "sides",
34124     /**
34125      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34126      * this menu (defaults to "tl-tr?")
34127      */
34128     subMenuAlign : "tl-tr?",
34129     /**
34130      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34131      * relative to its element of origin (defaults to "tl-bl?")
34132      */
34133     defaultAlign : "tl-bl?",
34134     /**
34135      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34136      */
34137     allowOtherMenus : false,
34138     /**
34139      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34140      */
34141     registerMenu : true,
34142
34143     hidden:true,
34144
34145     // private
34146     render : function(){
34147         if(this.el){
34148             return;
34149         }
34150         var el = this.el = new Roo.Layer({
34151             cls: "x-menu",
34152             shadow:this.shadow,
34153             constrain: false,
34154             parentEl: this.parentEl || document.body,
34155             zindex:15000
34156         });
34157
34158         this.keyNav = new Roo.menu.MenuNav(this);
34159
34160         if(this.plain){
34161             el.addClass("x-menu-plain");
34162         }
34163         if(this.cls){
34164             el.addClass(this.cls);
34165         }
34166         // generic focus element
34167         this.focusEl = el.createChild({
34168             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34169         });
34170         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34171         ul.on("click", this.onClick, this);
34172         ul.on("mouseover", this.onMouseOver, this);
34173         ul.on("mouseout", this.onMouseOut, this);
34174         this.items.each(function(item){
34175             var li = document.createElement("li");
34176             li.className = "x-menu-list-item";
34177             ul.dom.appendChild(li);
34178             item.render(li, this);
34179         }, this);
34180         this.ul = ul;
34181         this.autoWidth();
34182     },
34183
34184     // private
34185     autoWidth : function(){
34186         var el = this.el, ul = this.ul;
34187         if(!el){
34188             return;
34189         }
34190         var w = this.width;
34191         if(w){
34192             el.setWidth(w);
34193         }else if(Roo.isIE){
34194             el.setWidth(this.minWidth);
34195             var t = el.dom.offsetWidth; // force recalc
34196             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34197         }
34198     },
34199
34200     // private
34201     delayAutoWidth : function(){
34202         if(this.rendered){
34203             if(!this.awTask){
34204                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34205             }
34206             this.awTask.delay(20);
34207         }
34208     },
34209
34210     // private
34211     findTargetItem : function(e){
34212         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34213         if(t && t.menuItemId){
34214             return this.items.get(t.menuItemId);
34215         }
34216     },
34217
34218     // private
34219     onClick : function(e){
34220         var t;
34221         if(t = this.findTargetItem(e)){
34222             t.onClick(e);
34223             this.fireEvent("click", this, t, e);
34224         }
34225     },
34226
34227     // private
34228     setActiveItem : function(item, autoExpand){
34229         if(item != this.activeItem){
34230             if(this.activeItem){
34231                 this.activeItem.deactivate();
34232             }
34233             this.activeItem = item;
34234             item.activate(autoExpand);
34235         }else if(autoExpand){
34236             item.expandMenu();
34237         }
34238     },
34239
34240     // private
34241     tryActivate : function(start, step){
34242         var items = this.items;
34243         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34244             var item = items.get(i);
34245             if(!item.disabled && item.canActivate){
34246                 this.setActiveItem(item, false);
34247                 return item;
34248             }
34249         }
34250         return false;
34251     },
34252
34253     // private
34254     onMouseOver : function(e){
34255         var t;
34256         if(t = this.findTargetItem(e)){
34257             if(t.canActivate && !t.disabled){
34258                 this.setActiveItem(t, true);
34259             }
34260         }
34261         this.fireEvent("mouseover", this, e, t);
34262     },
34263
34264     // private
34265     onMouseOut : function(e){
34266         var t;
34267         if(t = this.findTargetItem(e)){
34268             if(t == this.activeItem && t.shouldDeactivate(e)){
34269                 this.activeItem.deactivate();
34270                 delete this.activeItem;
34271             }
34272         }
34273         this.fireEvent("mouseout", this, e, t);
34274     },
34275
34276     /**
34277      * Read-only.  Returns true if the menu is currently displayed, else false.
34278      * @type Boolean
34279      */
34280     isVisible : function(){
34281         return this.el && !this.hidden;
34282     },
34283
34284     /**
34285      * Displays this menu relative to another element
34286      * @param {String/HTMLElement/Roo.Element} element The element to align to
34287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34288      * the element (defaults to this.defaultAlign)
34289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34290      */
34291     show : function(el, pos, parentMenu){
34292         this.parentMenu = parentMenu;
34293         if(!this.el){
34294             this.render();
34295         }
34296         this.fireEvent("beforeshow", this);
34297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34298     },
34299
34300     /**
34301      * Displays this menu at a specific xy position
34302      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34303      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34304      */
34305     showAt : function(xy, parentMenu, /* private: */_e){
34306         this.parentMenu = parentMenu;
34307         if(!this.el){
34308             this.render();
34309         }
34310         if(_e !== false){
34311             this.fireEvent("beforeshow", this);
34312             xy = this.el.adjustForConstraints(xy);
34313         }
34314         this.el.setXY(xy);
34315         this.el.show();
34316         this.hidden = false;
34317         this.focus();
34318         this.fireEvent("show", this);
34319     },
34320
34321     focus : function(){
34322         if(!this.hidden){
34323             this.doFocus.defer(50, this);
34324         }
34325     },
34326
34327     doFocus : function(){
34328         if(!this.hidden){
34329             this.focusEl.focus();
34330         }
34331     },
34332
34333     /**
34334      * Hides this menu and optionally all parent menus
34335      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34336      */
34337     hide : function(deep){
34338         if(this.el && this.isVisible()){
34339             this.fireEvent("beforehide", this);
34340             if(this.activeItem){
34341                 this.activeItem.deactivate();
34342                 this.activeItem = null;
34343             }
34344             this.el.hide();
34345             this.hidden = true;
34346             this.fireEvent("hide", this);
34347         }
34348         if(deep === true && this.parentMenu){
34349             this.parentMenu.hide(true);
34350         }
34351     },
34352
34353     /**
34354      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34355      * Any of the following are valid:
34356      * <ul>
34357      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34358      * <li>An HTMLElement object which will be converted to a menu item</li>
34359      * <li>A menu item config object that will be created as a new menu item</li>
34360      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34361      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34362      * </ul>
34363      * Usage:
34364      * <pre><code>
34365 // Create the menu
34366 var menu = new Roo.menu.Menu();
34367
34368 // Create a menu item to add by reference
34369 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34370
34371 // Add a bunch of items at once using different methods.
34372 // Only the last item added will be returned.
34373 var item = menu.add(
34374     menuItem,                // add existing item by ref
34375     'Dynamic Item',          // new TextItem
34376     '-',                     // new separator
34377     { text: 'Config Item' }  // new item by config
34378 );
34379 </code></pre>
34380      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34381      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34382      */
34383     add : function(){
34384         var a = arguments, l = a.length, item;
34385         for(var i = 0; i < l; i++){
34386             var el = a[i];
34387             if ((typeof(el) == "object") && el.xtype && el.xns) {
34388                 el = Roo.factory(el, Roo.menu);
34389             }
34390             
34391             if(el.render){ // some kind of Item
34392                 item = this.addItem(el);
34393             }else if(typeof el == "string"){ // string
34394                 if(el == "separator" || el == "-"){
34395                     item = this.addSeparator();
34396                 }else{
34397                     item = this.addText(el);
34398                 }
34399             }else if(el.tagName || el.el){ // element
34400                 item = this.addElement(el);
34401             }else if(typeof el == "object"){ // must be menu item config?
34402                 item = this.addMenuItem(el);
34403             }
34404         }
34405         return item;
34406     },
34407
34408     /**
34409      * Returns this menu's underlying {@link Roo.Element} object
34410      * @return {Roo.Element} The element
34411      */
34412     getEl : function(){
34413         if(!this.el){
34414             this.render();
34415         }
34416         return this.el;
34417     },
34418
34419     /**
34420      * Adds a separator bar to the menu
34421      * @return {Roo.menu.Item} The menu item that was added
34422      */
34423     addSeparator : function(){
34424         return this.addItem(new Roo.menu.Separator());
34425     },
34426
34427     /**
34428      * Adds an {@link Roo.Element} object to the menu
34429      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34430      * @return {Roo.menu.Item} The menu item that was added
34431      */
34432     addElement : function(el){
34433         return this.addItem(new Roo.menu.BaseItem(el));
34434     },
34435
34436     /**
34437      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34438      * @param {Roo.menu.Item} item The menu item to add
34439      * @return {Roo.menu.Item} The menu item that was added
34440      */
34441     addItem : function(item){
34442         this.items.add(item);
34443         if(this.ul){
34444             var li = document.createElement("li");
34445             li.className = "x-menu-list-item";
34446             this.ul.dom.appendChild(li);
34447             item.render(li, this);
34448             this.delayAutoWidth();
34449         }
34450         return item;
34451     },
34452
34453     /**
34454      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34455      * @param {Object} config A MenuItem config object
34456      * @return {Roo.menu.Item} The menu item that was added
34457      */
34458     addMenuItem : function(config){
34459         if(!(config instanceof Roo.menu.Item)){
34460             if(typeof config.checked == "boolean"){ // must be check menu item config?
34461                 config = new Roo.menu.CheckItem(config);
34462             }else{
34463                 config = new Roo.menu.Item(config);
34464             }
34465         }
34466         return this.addItem(config);
34467     },
34468
34469     /**
34470      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34471      * @param {String} text The text to display in the menu item
34472      * @return {Roo.menu.Item} The menu item that was added
34473      */
34474     addText : function(text){
34475         return this.addItem(new Roo.menu.TextItem({ text : text }));
34476     },
34477
34478     /**
34479      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34480      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34481      * @param {Roo.menu.Item} item The menu item to add
34482      * @return {Roo.menu.Item} The menu item that was added
34483      */
34484     insert : function(index, item){
34485         this.items.insert(index, item);
34486         if(this.ul){
34487             var li = document.createElement("li");
34488             li.className = "x-menu-list-item";
34489             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34490             item.render(li, this);
34491             this.delayAutoWidth();
34492         }
34493         return item;
34494     },
34495
34496     /**
34497      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34498      * @param {Roo.menu.Item} item The menu item to remove
34499      */
34500     remove : function(item){
34501         this.items.removeKey(item.id);
34502         item.destroy();
34503     },
34504
34505     /**
34506      * Removes and destroys all items in the menu
34507      */
34508     removeAll : function(){
34509         var f;
34510         while(f = this.items.first()){
34511             this.remove(f);
34512         }
34513     }
34514 });
34515
34516 // MenuNav is a private utility class used internally by the Menu
34517 Roo.menu.MenuNav = function(menu){
34518     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34519     this.scope = this.menu = menu;
34520 };
34521
34522 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34523     doRelay : function(e, h){
34524         var k = e.getKey();
34525         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34526             this.menu.tryActivate(0, 1);
34527             return false;
34528         }
34529         return h.call(this.scope || this, e, this.menu);
34530     },
34531
34532     up : function(e, m){
34533         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34534             m.tryActivate(m.items.length-1, -1);
34535         }
34536     },
34537
34538     down : function(e, m){
34539         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34540             m.tryActivate(0, 1);
34541         }
34542     },
34543
34544     right : function(e, m){
34545         if(m.activeItem){
34546             m.activeItem.expandMenu(true);
34547         }
34548     },
34549
34550     left : function(e, m){
34551         m.hide();
34552         if(m.parentMenu && m.parentMenu.activeItem){
34553             m.parentMenu.activeItem.activate();
34554         }
34555     },
34556
34557     enter : function(e, m){
34558         if(m.activeItem){
34559             e.stopPropagation();
34560             m.activeItem.onClick(e);
34561             m.fireEvent("click", this, m.activeItem);
34562             return true;
34563         }
34564     }
34565 });/*
34566  * Based on:
34567  * Ext JS Library 1.1.1
34568  * Copyright(c) 2006-2007, Ext JS, LLC.
34569  *
34570  * Originally Released Under LGPL - original licence link has changed is not relivant.
34571  *
34572  * Fork - LGPL
34573  * <script type="text/javascript">
34574  */
34575  
34576 /**
34577  * @class Roo.menu.MenuMgr
34578  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34579  * @singleton
34580  */
34581 Roo.menu.MenuMgr = function(){
34582    var menus, active, groups = {}, attached = false, lastShow = new Date();
34583
34584    // private - called when first menu is created
34585    function init(){
34586        menus = {};
34587        active = new Roo.util.MixedCollection();
34588        Roo.get(document).addKeyListener(27, function(){
34589            if(active.length > 0){
34590                hideAll();
34591            }
34592        });
34593    }
34594
34595    // private
34596    function hideAll(){
34597        if(active && active.length > 0){
34598            var c = active.clone();
34599            c.each(function(m){
34600                m.hide();
34601            });
34602        }
34603    }
34604
34605    // private
34606    function onHide(m){
34607        active.remove(m);
34608        if(active.length < 1){
34609            Roo.get(document).un("mousedown", onMouseDown);
34610            attached = false;
34611        }
34612    }
34613
34614    // private
34615    function onShow(m){
34616        var last = active.last();
34617        lastShow = new Date();
34618        active.add(m);
34619        if(!attached){
34620            Roo.get(document).on("mousedown", onMouseDown);
34621            attached = true;
34622        }
34623        if(m.parentMenu){
34624           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34625           m.parentMenu.activeChild = m;
34626        }else if(last && last.isVisible()){
34627           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34628        }
34629    }
34630
34631    // private
34632    function onBeforeHide(m){
34633        if(m.activeChild){
34634            m.activeChild.hide();
34635        }
34636        if(m.autoHideTimer){
34637            clearTimeout(m.autoHideTimer);
34638            delete m.autoHideTimer;
34639        }
34640    }
34641
34642    // private
34643    function onBeforeShow(m){
34644        var pm = m.parentMenu;
34645        if(!pm && !m.allowOtherMenus){
34646            hideAll();
34647        }else if(pm && pm.activeChild && active != m){
34648            pm.activeChild.hide();
34649        }
34650    }
34651
34652    // private
34653    function onMouseDown(e){
34654        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34655            hideAll();
34656        }
34657    }
34658
34659    // private
34660    function onBeforeCheck(mi, state){
34661        if(state){
34662            var g = groups[mi.group];
34663            for(var i = 0, l = g.length; i < l; i++){
34664                if(g[i] != mi){
34665                    g[i].setChecked(false);
34666                }
34667            }
34668        }
34669    }
34670
34671    return {
34672
34673        /**
34674         * Hides all menus that are currently visible
34675         */
34676        hideAll : function(){
34677             hideAll();  
34678        },
34679
34680        // private
34681        register : function(menu){
34682            if(!menus){
34683                init();
34684            }
34685            menus[menu.id] = menu;
34686            menu.on("beforehide", onBeforeHide);
34687            menu.on("hide", onHide);
34688            menu.on("beforeshow", onBeforeShow);
34689            menu.on("show", onShow);
34690            var g = menu.group;
34691            if(g && menu.events["checkchange"]){
34692                if(!groups[g]){
34693                    groups[g] = [];
34694                }
34695                groups[g].push(menu);
34696                menu.on("checkchange", onCheck);
34697            }
34698        },
34699
34700         /**
34701          * Returns a {@link Roo.menu.Menu} object
34702          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34703          * be used to generate and return a new Menu instance.
34704          */
34705        get : function(menu){
34706            if(typeof menu == "string"){ // menu id
34707                return menus[menu];
34708            }else if(menu.events){  // menu instance
34709                return menu;
34710            }else if(typeof menu.length == 'number'){ // array of menu items?
34711                return new Roo.menu.Menu({items:menu});
34712            }else{ // otherwise, must be a config
34713                return new Roo.menu.Menu(menu);
34714            }
34715        },
34716
34717        // private
34718        unregister : function(menu){
34719            delete menus[menu.id];
34720            menu.un("beforehide", onBeforeHide);
34721            menu.un("hide", onHide);
34722            menu.un("beforeshow", onBeforeShow);
34723            menu.un("show", onShow);
34724            var g = menu.group;
34725            if(g && menu.events["checkchange"]){
34726                groups[g].remove(menu);
34727                menu.un("checkchange", onCheck);
34728            }
34729        },
34730
34731        // private
34732        registerCheckable : function(menuItem){
34733            var g = menuItem.group;
34734            if(g){
34735                if(!groups[g]){
34736                    groups[g] = [];
34737                }
34738                groups[g].push(menuItem);
34739                menuItem.on("beforecheckchange", onBeforeCheck);
34740            }
34741        },
34742
34743        // private
34744        unregisterCheckable : function(menuItem){
34745            var g = menuItem.group;
34746            if(g){
34747                groups[g].remove(menuItem);
34748                menuItem.un("beforecheckchange", onBeforeCheck);
34749            }
34750        }
34751    };
34752 }();/*
34753  * Based on:
34754  * Ext JS Library 1.1.1
34755  * Copyright(c) 2006-2007, Ext JS, LLC.
34756  *
34757  * Originally Released Under LGPL - original licence link has changed is not relivant.
34758  *
34759  * Fork - LGPL
34760  * <script type="text/javascript">
34761  */
34762  
34763
34764 /**
34765  * @class Roo.menu.BaseItem
34766  * @extends Roo.Component
34767  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34768  * management and base configuration options shared by all menu components.
34769  * @constructor
34770  * Creates a new BaseItem
34771  * @param {Object} config Configuration options
34772  */
34773 Roo.menu.BaseItem = function(config){
34774     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34775
34776     this.addEvents({
34777         /**
34778          * @event click
34779          * Fires when this item is clicked
34780          * @param {Roo.menu.BaseItem} this
34781          * @param {Roo.EventObject} e
34782          */
34783         click: true,
34784         /**
34785          * @event activate
34786          * Fires when this item is activated
34787          * @param {Roo.menu.BaseItem} this
34788          */
34789         activate : true,
34790         /**
34791          * @event deactivate
34792          * Fires when this item is deactivated
34793          * @param {Roo.menu.BaseItem} this
34794          */
34795         deactivate : true
34796     });
34797
34798     if(this.handler){
34799         this.on("click", this.handler, this.scope, true);
34800     }
34801 };
34802
34803 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34804     /**
34805      * @cfg {Function} handler
34806      * A function that will handle the click event of this menu item (defaults to undefined)
34807      */
34808     /**
34809      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34810      */
34811     canActivate : false,
34812     /**
34813      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34814      */
34815     activeClass : "x-menu-item-active",
34816     /**
34817      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34818      */
34819     hideOnClick : true,
34820     /**
34821      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34822      */
34823     hideDelay : 100,
34824
34825     // private
34826     ctype: "Roo.menu.BaseItem",
34827
34828     // private
34829     actionMode : "container",
34830
34831     // private
34832     render : function(container, parentMenu){
34833         this.parentMenu = parentMenu;
34834         Roo.menu.BaseItem.superclass.render.call(this, container);
34835         this.container.menuItemId = this.id;
34836     },
34837
34838     // private
34839     onRender : function(container, position){
34840         this.el = Roo.get(this.el);
34841         container.dom.appendChild(this.el.dom);
34842     },
34843
34844     // private
34845     onClick : function(e){
34846         if(!this.disabled && this.fireEvent("click", this, e) !== false
34847                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34848             this.handleClick(e);
34849         }else{
34850             e.stopEvent();
34851         }
34852     },
34853
34854     // private
34855     activate : function(){
34856         if(this.disabled){
34857             return false;
34858         }
34859         var li = this.container;
34860         li.addClass(this.activeClass);
34861         this.region = li.getRegion().adjust(2, 2, -2, -2);
34862         this.fireEvent("activate", this);
34863         return true;
34864     },
34865
34866     // private
34867     deactivate : function(){
34868         this.container.removeClass(this.activeClass);
34869         this.fireEvent("deactivate", this);
34870     },
34871
34872     // private
34873     shouldDeactivate : function(e){
34874         return !this.region || !this.region.contains(e.getPoint());
34875     },
34876
34877     // private
34878     handleClick : function(e){
34879         if(this.hideOnClick){
34880             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34881         }
34882     },
34883
34884     // private
34885     expandMenu : function(autoActivate){
34886         // do nothing
34887     },
34888
34889     // private
34890     hideMenu : function(){
34891         // do nothing
34892     }
34893 });/*
34894  * Based on:
34895  * Ext JS Library 1.1.1
34896  * Copyright(c) 2006-2007, Ext JS, LLC.
34897  *
34898  * Originally Released Under LGPL - original licence link has changed is not relivant.
34899  *
34900  * Fork - LGPL
34901  * <script type="text/javascript">
34902  */
34903  
34904 /**
34905  * @class Roo.menu.Adapter
34906  * @extends Roo.menu.BaseItem
34907  * 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.
34908  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34909  * @constructor
34910  * Creates a new Adapter
34911  * @param {Object} config Configuration options
34912  */
34913 Roo.menu.Adapter = function(component, config){
34914     Roo.menu.Adapter.superclass.constructor.call(this, config);
34915     this.component = component;
34916 };
34917 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34918     // private
34919     canActivate : true,
34920
34921     // private
34922     onRender : function(container, position){
34923         this.component.render(container);
34924         this.el = this.component.getEl();
34925     },
34926
34927     // private
34928     activate : function(){
34929         if(this.disabled){
34930             return false;
34931         }
34932         this.component.focus();
34933         this.fireEvent("activate", this);
34934         return true;
34935     },
34936
34937     // private
34938     deactivate : function(){
34939         this.fireEvent("deactivate", this);
34940     },
34941
34942     // private
34943     disable : function(){
34944         this.component.disable();
34945         Roo.menu.Adapter.superclass.disable.call(this);
34946     },
34947
34948     // private
34949     enable : function(){
34950         this.component.enable();
34951         Roo.menu.Adapter.superclass.enable.call(this);
34952     }
34953 });/*
34954  * Based on:
34955  * Ext JS Library 1.1.1
34956  * Copyright(c) 2006-2007, Ext JS, LLC.
34957  *
34958  * Originally Released Under LGPL - original licence link has changed is not relivant.
34959  *
34960  * Fork - LGPL
34961  * <script type="text/javascript">
34962  */
34963
34964 /**
34965  * @class Roo.menu.TextItem
34966  * @extends Roo.menu.BaseItem
34967  * Adds a static text string to a menu, usually used as either a heading or group separator.
34968  * Note: old style constructor with text is still supported.
34969  * 
34970  * @constructor
34971  * Creates a new TextItem
34972  * @param {Object} cfg Configuration
34973  */
34974 Roo.menu.TextItem = function(cfg){
34975     if (typeof(cfg) == 'string') {
34976         this.text = cfg;
34977     } else {
34978         Roo.apply(this,cfg);
34979     }
34980     
34981     Roo.menu.TextItem.superclass.constructor.call(this);
34982 };
34983
34984 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34985     /**
34986      * @cfg {Boolean} text Text to show on item.
34987      */
34988     text : '',
34989     
34990     /**
34991      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34992      */
34993     hideOnClick : false,
34994     /**
34995      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34996      */
34997     itemCls : "x-menu-text",
34998
34999     // private
35000     onRender : function(){
35001         var s = document.createElement("span");
35002         s.className = this.itemCls;
35003         s.innerHTML = this.text;
35004         this.el = s;
35005         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35006     }
35007 });/*
35008  * Based on:
35009  * Ext JS Library 1.1.1
35010  * Copyright(c) 2006-2007, Ext JS, LLC.
35011  *
35012  * Originally Released Under LGPL - original licence link has changed is not relivant.
35013  *
35014  * Fork - LGPL
35015  * <script type="text/javascript">
35016  */
35017
35018 /**
35019  * @class Roo.menu.Separator
35020  * @extends Roo.menu.BaseItem
35021  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35022  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35023  * @constructor
35024  * @param {Object} config Configuration options
35025  */
35026 Roo.menu.Separator = function(config){
35027     Roo.menu.Separator.superclass.constructor.call(this, config);
35028 };
35029
35030 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35031     /**
35032      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35033      */
35034     itemCls : "x-menu-sep",
35035     /**
35036      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35037      */
35038     hideOnClick : false,
35039
35040     // private
35041     onRender : function(li){
35042         var s = document.createElement("span");
35043         s.className = this.itemCls;
35044         s.innerHTML = "&#160;";
35045         this.el = s;
35046         li.addClass("x-menu-sep-li");
35047         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35048     }
35049 });/*
35050  * Based on:
35051  * Ext JS Library 1.1.1
35052  * Copyright(c) 2006-2007, Ext JS, LLC.
35053  *
35054  * Originally Released Under LGPL - original licence link has changed is not relivant.
35055  *
35056  * Fork - LGPL
35057  * <script type="text/javascript">
35058  */
35059 /**
35060  * @class Roo.menu.Item
35061  * @extends Roo.menu.BaseItem
35062  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35063  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35064  * activation and click handling.
35065  * @constructor
35066  * Creates a new Item
35067  * @param {Object} config Configuration options
35068  */
35069 Roo.menu.Item = function(config){
35070     Roo.menu.Item.superclass.constructor.call(this, config);
35071     if(this.menu){
35072         this.menu = Roo.menu.MenuMgr.get(this.menu);
35073     }
35074 };
35075 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35076     
35077     /**
35078      * @cfg {String} text
35079      * The text to show on the menu item.
35080      */
35081     text: '',
35082      /**
35083      * @cfg {String} HTML to render in menu
35084      * The text to show on the menu item (HTML version).
35085      */
35086     html: '',
35087     /**
35088      * @cfg {String} icon
35089      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35090      */
35091     icon: undefined,
35092     /**
35093      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35094      */
35095     itemCls : "x-menu-item",
35096     /**
35097      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35098      */
35099     canActivate : true,
35100     /**
35101      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35102      */
35103     showDelay: 200,
35104     // doc'd in BaseItem
35105     hideDelay: 200,
35106
35107     // private
35108     ctype: "Roo.menu.Item",
35109     
35110     // private
35111     onRender : function(container, position){
35112         var el = document.createElement("a");
35113         el.hideFocus = true;
35114         el.unselectable = "on";
35115         el.href = this.href || "#";
35116         if(this.hrefTarget){
35117             el.target = this.hrefTarget;
35118         }
35119         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35120         
35121         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35122         
35123         el.innerHTML = String.format(
35124                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35125                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35126         this.el = el;
35127         Roo.menu.Item.superclass.onRender.call(this, container, position);
35128     },
35129
35130     /**
35131      * Sets the text to display in this menu item
35132      * @param {String} text The text to display
35133      * @param {Boolean} isHTML true to indicate text is pure html.
35134      */
35135     setText : function(text, isHTML){
35136         if (isHTML) {
35137             this.html = text;
35138         } else {
35139             this.text = text;
35140             this.html = '';
35141         }
35142         if(this.rendered){
35143             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35144      
35145             this.el.update(String.format(
35146                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35147                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35148             this.parentMenu.autoWidth();
35149         }
35150     },
35151
35152     // private
35153     handleClick : function(e){
35154         if(!this.href){ // if no link defined, stop the event automatically
35155             e.stopEvent();
35156         }
35157         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35158     },
35159
35160     // private
35161     activate : function(autoExpand){
35162         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35163             this.focus();
35164             if(autoExpand){
35165                 this.expandMenu();
35166             }
35167         }
35168         return true;
35169     },
35170
35171     // private
35172     shouldDeactivate : function(e){
35173         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35174             if(this.menu && this.menu.isVisible()){
35175                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35176             }
35177             return true;
35178         }
35179         return false;
35180     },
35181
35182     // private
35183     deactivate : function(){
35184         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35185         this.hideMenu();
35186     },
35187
35188     // private
35189     expandMenu : function(autoActivate){
35190         if(!this.disabled && this.menu){
35191             clearTimeout(this.hideTimer);
35192             delete this.hideTimer;
35193             if(!this.menu.isVisible() && !this.showTimer){
35194                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35195             }else if (this.menu.isVisible() && autoActivate){
35196                 this.menu.tryActivate(0, 1);
35197             }
35198         }
35199     },
35200
35201     // private
35202     deferExpand : function(autoActivate){
35203         delete this.showTimer;
35204         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35205         if(autoActivate){
35206             this.menu.tryActivate(0, 1);
35207         }
35208     },
35209
35210     // private
35211     hideMenu : function(){
35212         clearTimeout(this.showTimer);
35213         delete this.showTimer;
35214         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35215             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35216         }
35217     },
35218
35219     // private
35220     deferHide : function(){
35221         delete this.hideTimer;
35222         this.menu.hide();
35223     }
35224 });/*
35225  * Based on:
35226  * Ext JS Library 1.1.1
35227  * Copyright(c) 2006-2007, Ext JS, LLC.
35228  *
35229  * Originally Released Under LGPL - original licence link has changed is not relivant.
35230  *
35231  * Fork - LGPL
35232  * <script type="text/javascript">
35233  */
35234  
35235 /**
35236  * @class Roo.menu.CheckItem
35237  * @extends Roo.menu.Item
35238  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35239  * @constructor
35240  * Creates a new CheckItem
35241  * @param {Object} config Configuration options
35242  */
35243 Roo.menu.CheckItem = function(config){
35244     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35245     this.addEvents({
35246         /**
35247          * @event beforecheckchange
35248          * Fires before the checked value is set, providing an opportunity to cancel if needed
35249          * @param {Roo.menu.CheckItem} this
35250          * @param {Boolean} checked The new checked value that will be set
35251          */
35252         "beforecheckchange" : true,
35253         /**
35254          * @event checkchange
35255          * Fires after the checked value has been set
35256          * @param {Roo.menu.CheckItem} this
35257          * @param {Boolean} checked The checked value that was set
35258          */
35259         "checkchange" : true
35260     });
35261     if(this.checkHandler){
35262         this.on('checkchange', this.checkHandler, this.scope);
35263     }
35264 };
35265 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35266     /**
35267      * @cfg {String} group
35268      * All check items with the same group name will automatically be grouped into a single-select
35269      * radio button group (defaults to '')
35270      */
35271     /**
35272      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35273      */
35274     itemCls : "x-menu-item x-menu-check-item",
35275     /**
35276      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35277      */
35278     groupClass : "x-menu-group-item",
35279
35280     /**
35281      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35282      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35283      * initialized with checked = true will be rendered as checked.
35284      */
35285     checked: false,
35286
35287     // private
35288     ctype: "Roo.menu.CheckItem",
35289
35290     // private
35291     onRender : function(c){
35292         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35293         if(this.group){
35294             this.el.addClass(this.groupClass);
35295         }
35296         Roo.menu.MenuMgr.registerCheckable(this);
35297         if(this.checked){
35298             this.checked = false;
35299             this.setChecked(true, true);
35300         }
35301     },
35302
35303     // private
35304     destroy : function(){
35305         if(this.rendered){
35306             Roo.menu.MenuMgr.unregisterCheckable(this);
35307         }
35308         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35309     },
35310
35311     /**
35312      * Set the checked state of this item
35313      * @param {Boolean} checked The new checked value
35314      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35315      */
35316     setChecked : function(state, suppressEvent){
35317         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35318             if(this.container){
35319                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35320             }
35321             this.checked = state;
35322             if(suppressEvent !== true){
35323                 this.fireEvent("checkchange", this, state);
35324             }
35325         }
35326     },
35327
35328     // private
35329     handleClick : function(e){
35330        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35331            this.setChecked(!this.checked);
35332        }
35333        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35334     }
35335 });/*
35336  * Based on:
35337  * Ext JS Library 1.1.1
35338  * Copyright(c) 2006-2007, Ext JS, LLC.
35339  *
35340  * Originally Released Under LGPL - original licence link has changed is not relivant.
35341  *
35342  * Fork - LGPL
35343  * <script type="text/javascript">
35344  */
35345  
35346 /**
35347  * @class Roo.menu.DateItem
35348  * @extends Roo.menu.Adapter
35349  * A menu item that wraps the {@link Roo.DatPicker} component.
35350  * @constructor
35351  * Creates a new DateItem
35352  * @param {Object} config Configuration options
35353  */
35354 Roo.menu.DateItem = function(config){
35355     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35356     /** The Roo.DatePicker object @type Roo.DatePicker */
35357     this.picker = this.component;
35358     this.addEvents({select: true});
35359     
35360     this.picker.on("render", function(picker){
35361         picker.getEl().swallowEvent("click");
35362         picker.container.addClass("x-menu-date-item");
35363     });
35364
35365     this.picker.on("select", this.onSelect, this);
35366 };
35367
35368 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35369     // private
35370     onSelect : function(picker, date){
35371         this.fireEvent("select", this, date, picker);
35372         Roo.menu.DateItem.superclass.handleClick.call(this);
35373     }
35374 });/*
35375  * Based on:
35376  * Ext JS Library 1.1.1
35377  * Copyright(c) 2006-2007, Ext JS, LLC.
35378  *
35379  * Originally Released Under LGPL - original licence link has changed is not relivant.
35380  *
35381  * Fork - LGPL
35382  * <script type="text/javascript">
35383  */
35384  
35385 /**
35386  * @class Roo.menu.ColorItem
35387  * @extends Roo.menu.Adapter
35388  * A menu item that wraps the {@link Roo.ColorPalette} component.
35389  * @constructor
35390  * Creates a new ColorItem
35391  * @param {Object} config Configuration options
35392  */
35393 Roo.menu.ColorItem = function(config){
35394     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35395     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35396     this.palette = this.component;
35397     this.relayEvents(this.palette, ["select"]);
35398     if(this.selectHandler){
35399         this.on('select', this.selectHandler, this.scope);
35400     }
35401 };
35402 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35403  * Based on:
35404  * Ext JS Library 1.1.1
35405  * Copyright(c) 2006-2007, Ext JS, LLC.
35406  *
35407  * Originally Released Under LGPL - original licence link has changed is not relivant.
35408  *
35409  * Fork - LGPL
35410  * <script type="text/javascript">
35411  */
35412  
35413
35414 /**
35415  * @class Roo.menu.DateMenu
35416  * @extends Roo.menu.Menu
35417  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35418  * @constructor
35419  * Creates a new DateMenu
35420  * @param {Object} config Configuration options
35421  */
35422 Roo.menu.DateMenu = function(config){
35423     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35424     this.plain = true;
35425     var di = new Roo.menu.DateItem(config);
35426     this.add(di);
35427     /**
35428      * The {@link Roo.DatePicker} instance for this DateMenu
35429      * @type DatePicker
35430      */
35431     this.picker = di.picker;
35432     /**
35433      * @event select
35434      * @param {DatePicker} picker
35435      * @param {Date} date
35436      */
35437     this.relayEvents(di, ["select"]);
35438
35439     this.on('beforeshow', function(){
35440         if(this.picker){
35441             this.picker.hideMonthPicker(true);
35442         }
35443     }, this);
35444 };
35445 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35446     cls:'x-date-menu'
35447 });/*
35448  * Based on:
35449  * Ext JS Library 1.1.1
35450  * Copyright(c) 2006-2007, Ext JS, LLC.
35451  *
35452  * Originally Released Under LGPL - original licence link has changed is not relivant.
35453  *
35454  * Fork - LGPL
35455  * <script type="text/javascript">
35456  */
35457  
35458
35459 /**
35460  * @class Roo.menu.ColorMenu
35461  * @extends Roo.menu.Menu
35462  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35463  * @constructor
35464  * Creates a new ColorMenu
35465  * @param {Object} config Configuration options
35466  */
35467 Roo.menu.ColorMenu = function(config){
35468     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35469     this.plain = true;
35470     var ci = new Roo.menu.ColorItem(config);
35471     this.add(ci);
35472     /**
35473      * The {@link Roo.ColorPalette} instance for this ColorMenu
35474      * @type ColorPalette
35475      */
35476     this.palette = ci.palette;
35477     /**
35478      * @event select
35479      * @param {ColorPalette} palette
35480      * @param {String} color
35481      */
35482     this.relayEvents(ci, ["select"]);
35483 };
35484 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35485  * Based on:
35486  * Ext JS Library 1.1.1
35487  * Copyright(c) 2006-2007, Ext JS, LLC.
35488  *
35489  * Originally Released Under LGPL - original licence link has changed is not relivant.
35490  *
35491  * Fork - LGPL
35492  * <script type="text/javascript">
35493  */
35494  
35495 /**
35496  * @class Roo.form.Field
35497  * @extends Roo.BoxComponent
35498  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35499  * @constructor
35500  * Creates a new Field
35501  * @param {Object} config Configuration options
35502  */
35503 Roo.form.Field = function(config){
35504     Roo.form.Field.superclass.constructor.call(this, config);
35505 };
35506
35507 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35508     /**
35509      * @cfg {String} fieldLabel Label to use when rendering a form.
35510      */
35511        /**
35512      * @cfg {String} qtip Mouse over tip
35513      */
35514      
35515     /**
35516      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35517      */
35518     invalidClass : "x-form-invalid",
35519     /**
35520      * @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")
35521      */
35522     invalidText : "The value in this field is invalid",
35523     /**
35524      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35525      */
35526     focusClass : "x-form-focus",
35527     /**
35528      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35529       automatic validation (defaults to "keyup").
35530      */
35531     validationEvent : "keyup",
35532     /**
35533      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35534      */
35535     validateOnBlur : true,
35536     /**
35537      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35538      */
35539     validationDelay : 250,
35540     /**
35541      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35542      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35543      */
35544     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35545     /**
35546      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35547      */
35548     fieldClass : "x-form-field",
35549     /**
35550      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35551      *<pre>
35552 Value         Description
35553 -----------   ----------------------------------------------------------------------
35554 qtip          Display a quick tip when the user hovers over the field
35555 title         Display a default browser title attribute popup
35556 under         Add a block div beneath the field containing the error text
35557 side          Add an error icon to the right of the field with a popup on hover
35558 [element id]  Add the error text directly to the innerHTML of the specified element
35559 </pre>
35560      */
35561     msgTarget : 'qtip',
35562     /**
35563      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35564      */
35565     msgFx : 'normal',
35566
35567     /**
35568      * @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.
35569      */
35570     readOnly : false,
35571
35572     /**
35573      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35574      */
35575     disabled : false,
35576
35577     /**
35578      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35579      */
35580     inputType : undefined,
35581     
35582     /**
35583      * @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).
35584          */
35585         tabIndex : undefined,
35586         
35587     // private
35588     isFormField : true,
35589
35590     // private
35591     hasFocus : false,
35592     /**
35593      * @property {Roo.Element} fieldEl
35594      * Element Containing the rendered Field (with label etc.)
35595      */
35596     /**
35597      * @cfg {Mixed} value A value to initialize this field with.
35598      */
35599     value : undefined,
35600
35601     /**
35602      * @cfg {String} name The field's HTML name attribute.
35603      */
35604     /**
35605      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35606      */
35607
35608         // private ??
35609         initComponent : function(){
35610         Roo.form.Field.superclass.initComponent.call(this);
35611         this.addEvents({
35612             /**
35613              * @event focus
35614              * Fires when this field receives input focus.
35615              * @param {Roo.form.Field} this
35616              */
35617             focus : true,
35618             /**
35619              * @event blur
35620              * Fires when this field loses input focus.
35621              * @param {Roo.form.Field} this
35622              */
35623             blur : true,
35624             /**
35625              * @event specialkey
35626              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35627              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35628              * @param {Roo.form.Field} this
35629              * @param {Roo.EventObject} e The event object
35630              */
35631             specialkey : true,
35632             /**
35633              * @event change
35634              * Fires just before the field blurs if the field value has changed.
35635              * @param {Roo.form.Field} this
35636              * @param {Mixed} newValue The new value
35637              * @param {Mixed} oldValue The original value
35638              */
35639             change : true,
35640             /**
35641              * @event invalid
35642              * Fires after the field has been marked as invalid.
35643              * @param {Roo.form.Field} this
35644              * @param {String} msg The validation message
35645              */
35646             invalid : true,
35647             /**
35648              * @event valid
35649              * Fires after the field has been validated with no errors.
35650              * @param {Roo.form.Field} this
35651              */
35652             valid : true,
35653              /**
35654              * @event keyup
35655              * Fires after the key up
35656              * @param {Roo.form.Field} this
35657              * @param {Roo.EventObject}  e The event Object
35658              */
35659             keyup : true
35660         });
35661     },
35662
35663     /**
35664      * Returns the name attribute of the field if available
35665      * @return {String} name The field name
35666      */
35667     getName: function(){
35668          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35669     },
35670
35671     // private
35672     onRender : function(ct, position){
35673         Roo.form.Field.superclass.onRender.call(this, ct, position);
35674         if(!this.el){
35675             var cfg = this.getAutoCreate();
35676             if(!cfg.name){
35677                 cfg.name = this.name || this.id;
35678             }
35679             if(this.inputType){
35680                 cfg.type = this.inputType;
35681             }
35682             this.el = ct.createChild(cfg, position);
35683         }
35684         var type = this.el.dom.type;
35685         if(type){
35686             if(type == 'password'){
35687                 type = 'text';
35688             }
35689             this.el.addClass('x-form-'+type);
35690         }
35691         if(this.readOnly){
35692             this.el.dom.readOnly = true;
35693         }
35694         if(this.tabIndex !== undefined){
35695             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35696         }
35697
35698         this.el.addClass([this.fieldClass, this.cls]);
35699         this.initValue();
35700     },
35701
35702     /**
35703      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35704      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35705      * @return {Roo.form.Field} this
35706      */
35707     applyTo : function(target){
35708         this.allowDomMove = false;
35709         this.el = Roo.get(target);
35710         this.render(this.el.dom.parentNode);
35711         return this;
35712     },
35713
35714     // private
35715     initValue : function(){
35716         if(this.value !== undefined){
35717             this.setValue(this.value);
35718         }else if(this.el.dom.value.length > 0){
35719             this.setValue(this.el.dom.value);
35720         }
35721     },
35722
35723     /**
35724      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35725      */
35726     isDirty : function() {
35727         if(this.disabled) {
35728             return false;
35729         }
35730         return String(this.getValue()) !== String(this.originalValue);
35731     },
35732
35733     // private
35734     afterRender : function(){
35735         Roo.form.Field.superclass.afterRender.call(this);
35736         this.initEvents();
35737     },
35738
35739     // private
35740     fireKey : function(e){
35741         //Roo.log('field ' + e.getKey());
35742         if(e.isNavKeyPress()){
35743             this.fireEvent("specialkey", this, e);
35744         }
35745     },
35746
35747     /**
35748      * Resets the current field value to the originally loaded value and clears any validation messages
35749      */
35750     reset : function(){
35751         this.setValue(this.originalValue);
35752         this.clearInvalid();
35753     },
35754
35755     // private
35756     initEvents : function(){
35757         // safari killled keypress - so keydown is now used..
35758         this.el.on("keydown" , this.fireKey,  this);
35759         this.el.on("focus", this.onFocus,  this);
35760         this.el.on("blur", this.onBlur,  this);
35761         this.el.relayEvent('keyup', this);
35762
35763         // reference to original value for reset
35764         this.originalValue = this.getValue();
35765     },
35766
35767     // private
35768     onFocus : function(){
35769         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35770             this.el.addClass(this.focusClass);
35771         }
35772         if(!this.hasFocus){
35773             this.hasFocus = true;
35774             this.startValue = this.getValue();
35775             this.fireEvent("focus", this);
35776         }
35777     },
35778
35779     beforeBlur : Roo.emptyFn,
35780
35781     // private
35782     onBlur : function(){
35783         this.beforeBlur();
35784         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35785             this.el.removeClass(this.focusClass);
35786         }
35787         this.hasFocus = false;
35788         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35789             this.validate();
35790         }
35791         var v = this.getValue();
35792         if(String(v) !== String(this.startValue)){
35793             this.fireEvent('change', this, v, this.startValue);
35794         }
35795         this.fireEvent("blur", this);
35796     },
35797
35798     /**
35799      * Returns whether or not the field value is currently valid
35800      * @param {Boolean} preventMark True to disable marking the field invalid
35801      * @return {Boolean} True if the value is valid, else false
35802      */
35803     isValid : function(preventMark){
35804         if(this.disabled){
35805             return true;
35806         }
35807         var restore = this.preventMark;
35808         this.preventMark = preventMark === true;
35809         var v = this.validateValue(this.processValue(this.getRawValue()));
35810         this.preventMark = restore;
35811         return v;
35812     },
35813
35814     /**
35815      * Validates the field value
35816      * @return {Boolean} True if the value is valid, else false
35817      */
35818     validate : function(){
35819         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35820             this.clearInvalid();
35821             return true;
35822         }
35823         return false;
35824     },
35825
35826     processValue : function(value){
35827         return value;
35828     },
35829
35830     // private
35831     // Subclasses should provide the validation implementation by overriding this
35832     validateValue : function(value){
35833         return true;
35834     },
35835
35836     /**
35837      * Mark this field as invalid
35838      * @param {String} msg The validation message
35839      */
35840     markInvalid : function(msg){
35841         if(!this.rendered || this.preventMark){ // not rendered
35842             return;
35843         }
35844         this.el.addClass(this.invalidClass);
35845         msg = msg || this.invalidText;
35846         switch(this.msgTarget){
35847             case 'qtip':
35848                 this.el.dom.qtip = msg;
35849                 this.el.dom.qclass = 'x-form-invalid-tip';
35850                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35851                     Roo.QuickTips.enable();
35852                 }
35853                 break;
35854             case 'title':
35855                 this.el.dom.title = msg;
35856                 break;
35857             case 'under':
35858                 if(!this.errorEl){
35859                     var elp = this.el.findParent('.x-form-element', 5, true);
35860                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35861                     this.errorEl.setWidth(elp.getWidth(true)-20);
35862                 }
35863                 this.errorEl.update(msg);
35864                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35865                 break;
35866             case 'side':
35867                 if(!this.errorIcon){
35868                     var elp = this.el.findParent('.x-form-element', 5, true);
35869                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35870                 }
35871                 this.alignErrorIcon();
35872                 this.errorIcon.dom.qtip = msg;
35873                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35874                 this.errorIcon.show();
35875                 this.on('resize', this.alignErrorIcon, this);
35876                 break;
35877             default:
35878                 var t = Roo.getDom(this.msgTarget);
35879                 t.innerHTML = msg;
35880                 t.style.display = this.msgDisplay;
35881                 break;
35882         }
35883         this.fireEvent('invalid', this, msg);
35884     },
35885
35886     // private
35887     alignErrorIcon : function(){
35888         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35889     },
35890
35891     /**
35892      * Clear any invalid styles/messages for this field
35893      */
35894     clearInvalid : function(){
35895         if(!this.rendered || this.preventMark){ // not rendered
35896             return;
35897         }
35898         this.el.removeClass(this.invalidClass);
35899         switch(this.msgTarget){
35900             case 'qtip':
35901                 this.el.dom.qtip = '';
35902                 break;
35903             case 'title':
35904                 this.el.dom.title = '';
35905                 break;
35906             case 'under':
35907                 if(this.errorEl){
35908                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35909                 }
35910                 break;
35911             case 'side':
35912                 if(this.errorIcon){
35913                     this.errorIcon.dom.qtip = '';
35914                     this.errorIcon.hide();
35915                     this.un('resize', this.alignErrorIcon, this);
35916                 }
35917                 break;
35918             default:
35919                 var t = Roo.getDom(this.msgTarget);
35920                 t.innerHTML = '';
35921                 t.style.display = 'none';
35922                 break;
35923         }
35924         this.fireEvent('valid', this);
35925     },
35926
35927     /**
35928      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35929      * @return {Mixed} value The field value
35930      */
35931     getRawValue : function(){
35932         var v = this.el.getValue();
35933         if(v === this.emptyText){
35934             v = '';
35935         }
35936         return v;
35937     },
35938
35939     /**
35940      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35941      * @return {Mixed} value The field value
35942      */
35943     getValue : function(){
35944         var v = this.el.getValue();
35945         if(v === this.emptyText || v === undefined){
35946             v = '';
35947         }
35948         return v;
35949     },
35950
35951     /**
35952      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35953      * @param {Mixed} value The value to set
35954      */
35955     setRawValue : function(v){
35956         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35957     },
35958
35959     /**
35960      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35961      * @param {Mixed} value The value to set
35962      */
35963     setValue : function(v){
35964         this.value = v;
35965         if(this.rendered){
35966             this.el.dom.value = (v === null || v === undefined ? '' : v);
35967              this.validate();
35968         }
35969     },
35970
35971     adjustSize : function(w, h){
35972         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35973         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35974         return s;
35975     },
35976
35977     adjustWidth : function(tag, w){
35978         tag = tag.toLowerCase();
35979         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35980             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35981                 if(tag == 'input'){
35982                     return w + 2;
35983                 }
35984                 if(tag = 'textarea'){
35985                     return w-2;
35986                 }
35987             }else if(Roo.isOpera){
35988                 if(tag == 'input'){
35989                     return w + 2;
35990                 }
35991                 if(tag = 'textarea'){
35992                     return w-2;
35993                 }
35994             }
35995         }
35996         return w;
35997     }
35998 });
35999
36000
36001 // anything other than normal should be considered experimental
36002 Roo.form.Field.msgFx = {
36003     normal : {
36004         show: function(msgEl, f){
36005             msgEl.setDisplayed('block');
36006         },
36007
36008         hide : function(msgEl, f){
36009             msgEl.setDisplayed(false).update('');
36010         }
36011     },
36012
36013     slide : {
36014         show: function(msgEl, f){
36015             msgEl.slideIn('t', {stopFx:true});
36016         },
36017
36018         hide : function(msgEl, f){
36019             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36020         }
36021     },
36022
36023     slideRight : {
36024         show: function(msgEl, f){
36025             msgEl.fixDisplay();
36026             msgEl.alignTo(f.el, 'tl-tr');
36027             msgEl.slideIn('l', {stopFx:true});
36028         },
36029
36030         hide : function(msgEl, f){
36031             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36032         }
36033     }
36034 };/*
36035  * Based on:
36036  * Ext JS Library 1.1.1
36037  * Copyright(c) 2006-2007, Ext JS, LLC.
36038  *
36039  * Originally Released Under LGPL - original licence link has changed is not relivant.
36040  *
36041  * Fork - LGPL
36042  * <script type="text/javascript">
36043  */
36044  
36045
36046 /**
36047  * @class Roo.form.TextField
36048  * @extends Roo.form.Field
36049  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36050  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36051  * @constructor
36052  * Creates a new TextField
36053  * @param {Object} config Configuration options
36054  */
36055 Roo.form.TextField = function(config){
36056     Roo.form.TextField.superclass.constructor.call(this, config);
36057     this.addEvents({
36058         /**
36059          * @event autosize
36060          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36061          * according to the default logic, but this event provides a hook for the developer to apply additional
36062          * logic at runtime to resize the field if needed.
36063              * @param {Roo.form.Field} this This text field
36064              * @param {Number} width The new field width
36065              */
36066         autosize : true
36067     });
36068 };
36069
36070 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36071     /**
36072      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36073      */
36074     grow : false,
36075     /**
36076      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36077      */
36078     growMin : 30,
36079     /**
36080      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36081      */
36082     growMax : 800,
36083     /**
36084      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36085      */
36086     vtype : null,
36087     /**
36088      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36089      */
36090     maskRe : null,
36091     /**
36092      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36093      */
36094     disableKeyFilter : false,
36095     /**
36096      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36097      */
36098     allowBlank : true,
36099     /**
36100      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36101      */
36102     minLength : 0,
36103     /**
36104      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36105      */
36106     maxLength : Number.MAX_VALUE,
36107     /**
36108      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36109      */
36110     minLengthText : "The minimum length for this field is {0}",
36111     /**
36112      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36113      */
36114     maxLengthText : "The maximum length for this field is {0}",
36115     /**
36116      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36117      */
36118     selectOnFocus : false,
36119     /**
36120      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36121      */
36122     blankText : "This field is required",
36123     /**
36124      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36125      * If available, this function will be called only after the basic validators all return true, and will be passed the
36126      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36127      */
36128     validator : null,
36129     /**
36130      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36131      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36132      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36133      */
36134     regex : null,
36135     /**
36136      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36137      */
36138     regexText : "",
36139     /**
36140      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36141      */
36142     emptyText : null,
36143     /**
36144      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36145      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36146      */
36147     emptyClass : 'x-form-empty-field',
36148
36149     // private
36150     initEvents : function(){
36151         Roo.form.TextField.superclass.initEvents.call(this);
36152         if(this.validationEvent == 'keyup'){
36153             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36154             this.el.on('keyup', this.filterValidation, this);
36155         }
36156         else if(this.validationEvent !== false){
36157             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36158         }
36159         if(this.selectOnFocus || this.emptyText){
36160             this.on("focus", this.preFocus, this);
36161             if(this.emptyText){
36162                 this.on('blur', this.postBlur, this);
36163                 this.applyEmptyText();
36164             }
36165         }
36166         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36167             this.el.on("keypress", this.filterKeys, this);
36168         }
36169         if(this.grow){
36170             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36171             this.el.on("click", this.autoSize,  this);
36172         }
36173     },
36174
36175     processValue : function(value){
36176         if(this.stripCharsRe){
36177             var newValue = value.replace(this.stripCharsRe, '');
36178             if(newValue !== value){
36179                 this.setRawValue(newValue);
36180                 return newValue;
36181             }
36182         }
36183         return value;
36184     },
36185
36186     filterValidation : function(e){
36187         if(!e.isNavKeyPress()){
36188             this.validationTask.delay(this.validationDelay);
36189         }
36190     },
36191
36192     // private
36193     onKeyUp : function(e){
36194         if(!e.isNavKeyPress()){
36195             this.autoSize();
36196         }
36197     },
36198
36199     /**
36200      * Resets the current field value to the originally-loaded value and clears any validation messages.
36201      * Also adds emptyText and emptyClass if the original value was blank.
36202      */
36203     reset : function(){
36204         Roo.form.TextField.superclass.reset.call(this);
36205         this.applyEmptyText();
36206     },
36207
36208     applyEmptyText : function(){
36209         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36210             this.setRawValue(this.emptyText);
36211             this.el.addClass(this.emptyClass);
36212         }
36213     },
36214
36215     // private
36216     preFocus : function(){
36217         if(this.emptyText){
36218             if(this.el.dom.value == this.emptyText){
36219                 this.setRawValue('');
36220             }
36221             this.el.removeClass(this.emptyClass);
36222         }
36223         if(this.selectOnFocus){
36224             this.el.dom.select();
36225         }
36226     },
36227
36228     // private
36229     postBlur : function(){
36230         this.applyEmptyText();
36231     },
36232
36233     // private
36234     filterKeys : function(e){
36235         var k = e.getKey();
36236         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36237             return;
36238         }
36239         var c = e.getCharCode(), cc = String.fromCharCode(c);
36240         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36241             return;
36242         }
36243         if(!this.maskRe.test(cc)){
36244             e.stopEvent();
36245         }
36246     },
36247
36248     setValue : function(v){
36249         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36250             this.el.removeClass(this.emptyClass);
36251         }
36252         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36253         this.applyEmptyText();
36254         this.autoSize();
36255     },
36256
36257     /**
36258      * Validates a value according to the field's validation rules and marks the field as invalid
36259      * if the validation fails
36260      * @param {Mixed} value The value to validate
36261      * @return {Boolean} True if the value is valid, else false
36262      */
36263     validateValue : function(value){
36264         if(value.length < 1 || value === this.emptyText){ // if it's blank
36265              if(this.allowBlank){
36266                 this.clearInvalid();
36267                 return true;
36268              }else{
36269                 this.markInvalid(this.blankText);
36270                 return false;
36271              }
36272         }
36273         if(value.length < this.minLength){
36274             this.markInvalid(String.format(this.minLengthText, this.minLength));
36275             return false;
36276         }
36277         if(value.length > this.maxLength){
36278             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36279             return false;
36280         }
36281         if(this.vtype){
36282             var vt = Roo.form.VTypes;
36283             if(!vt[this.vtype](value, this)){
36284                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36285                 return false;
36286             }
36287         }
36288         if(typeof this.validator == "function"){
36289             var msg = this.validator(value);
36290             if(msg !== true){
36291                 this.markInvalid(msg);
36292                 return false;
36293             }
36294         }
36295         if(this.regex && !this.regex.test(value)){
36296             this.markInvalid(this.regexText);
36297             return false;
36298         }
36299         return true;
36300     },
36301
36302     /**
36303      * Selects text in this field
36304      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36305      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36306      */
36307     selectText : function(start, end){
36308         var v = this.getRawValue();
36309         if(v.length > 0){
36310             start = start === undefined ? 0 : start;
36311             end = end === undefined ? v.length : end;
36312             var d = this.el.dom;
36313             if(d.setSelectionRange){
36314                 d.setSelectionRange(start, end);
36315             }else if(d.createTextRange){
36316                 var range = d.createTextRange();
36317                 range.moveStart("character", start);
36318                 range.moveEnd("character", v.length-end);
36319                 range.select();
36320             }
36321         }
36322     },
36323
36324     /**
36325      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36326      * This only takes effect if grow = true, and fires the autosize event.
36327      */
36328     autoSize : function(){
36329         if(!this.grow || !this.rendered){
36330             return;
36331         }
36332         if(!this.metrics){
36333             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36334         }
36335         var el = this.el;
36336         var v = el.dom.value;
36337         var d = document.createElement('div');
36338         d.appendChild(document.createTextNode(v));
36339         v = d.innerHTML;
36340         d = null;
36341         v += "&#160;";
36342         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36343         this.el.setWidth(w);
36344         this.fireEvent("autosize", this, w);
36345     }
36346 });/*
36347  * Based on:
36348  * Ext JS Library 1.1.1
36349  * Copyright(c) 2006-2007, Ext JS, LLC.
36350  *
36351  * Originally Released Under LGPL - original licence link has changed is not relivant.
36352  *
36353  * Fork - LGPL
36354  * <script type="text/javascript">
36355  */
36356  
36357 /**
36358  * @class Roo.form.Hidden
36359  * @extends Roo.form.TextField
36360  * Simple Hidden element used on forms 
36361  * 
36362  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36363  * 
36364  * @constructor
36365  * Creates a new Hidden form element.
36366  * @param {Object} config Configuration options
36367  */
36368
36369
36370
36371 // easy hidden field...
36372 Roo.form.Hidden = function(config){
36373     Roo.form.Hidden.superclass.constructor.call(this, config);
36374 };
36375   
36376 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36377     fieldLabel:      '',
36378     inputType:      'hidden',
36379     width:          50,
36380     allowBlank:     true,
36381     labelSeparator: '',
36382     hidden:         true,
36383     itemCls :       'x-form-item-display-none'
36384
36385
36386 });
36387
36388
36389 /*
36390  * Based on:
36391  * Ext JS Library 1.1.1
36392  * Copyright(c) 2006-2007, Ext JS, LLC.
36393  *
36394  * Originally Released Under LGPL - original licence link has changed is not relivant.
36395  *
36396  * Fork - LGPL
36397  * <script type="text/javascript">
36398  */
36399  
36400 /**
36401  * @class Roo.form.TriggerField
36402  * @extends Roo.form.TextField
36403  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36404  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36405  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36406  * for which you can provide a custom implementation.  For example:
36407  * <pre><code>
36408 var trigger = new Roo.form.TriggerField();
36409 trigger.onTriggerClick = myTriggerFn;
36410 trigger.applyTo('my-field');
36411 </code></pre>
36412  *
36413  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36414  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36415  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36416  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36417  * @constructor
36418  * Create a new TriggerField.
36419  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36420  * to the base TextField)
36421  */
36422 Roo.form.TriggerField = function(config){
36423     this.mimicing = false;
36424     Roo.form.TriggerField.superclass.constructor.call(this, config);
36425 };
36426
36427 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36428     /**
36429      * @cfg {String} triggerClass A CSS class to apply to the trigger
36430      */
36431     /**
36432      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36433      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36434      */
36435     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36436     /**
36437      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36438      */
36439     hideTrigger:false,
36440
36441     /** @cfg {Boolean} grow @hide */
36442     /** @cfg {Number} growMin @hide */
36443     /** @cfg {Number} growMax @hide */
36444
36445     /**
36446      * @hide 
36447      * @method
36448      */
36449     autoSize: Roo.emptyFn,
36450     // private
36451     monitorTab : true,
36452     // private
36453     deferHeight : true,
36454
36455     
36456     actionMode : 'wrap',
36457     // private
36458     onResize : function(w, h){
36459         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36460         if(typeof w == 'number'){
36461             var x = w - this.trigger.getWidth();
36462             this.el.setWidth(this.adjustWidth('input', x));
36463             this.trigger.setStyle('left', x+'px');
36464         }
36465     },
36466
36467     // private
36468     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36469
36470     // private
36471     getResizeEl : function(){
36472         return this.wrap;
36473     },
36474
36475     // private
36476     getPositionEl : function(){
36477         return this.wrap;
36478     },
36479
36480     // private
36481     alignErrorIcon : function(){
36482         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36483     },
36484
36485     // private
36486     onRender : function(ct, position){
36487         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36488         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36489         this.trigger = this.wrap.createChild(this.triggerConfig ||
36490                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36491         if(this.hideTrigger){
36492             this.trigger.setDisplayed(false);
36493         }
36494         this.initTrigger();
36495         if(!this.width){
36496             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36497         }
36498     },
36499
36500     // private
36501     initTrigger : function(){
36502         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36503         this.trigger.addClassOnOver('x-form-trigger-over');
36504         this.trigger.addClassOnClick('x-form-trigger-click');
36505     },
36506
36507     // private
36508     onDestroy : function(){
36509         if(this.trigger){
36510             this.trigger.removeAllListeners();
36511             this.trigger.remove();
36512         }
36513         if(this.wrap){
36514             this.wrap.remove();
36515         }
36516         Roo.form.TriggerField.superclass.onDestroy.call(this);
36517     },
36518
36519     // private
36520     onFocus : function(){
36521         Roo.form.TriggerField.superclass.onFocus.call(this);
36522         if(!this.mimicing){
36523             this.wrap.addClass('x-trigger-wrap-focus');
36524             this.mimicing = true;
36525             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36526             if(this.monitorTab){
36527                 this.el.on("keydown", this.checkTab, this);
36528             }
36529         }
36530     },
36531
36532     // private
36533     checkTab : function(e){
36534         if(e.getKey() == e.TAB){
36535             this.triggerBlur();
36536         }
36537     },
36538
36539     // private
36540     onBlur : function(){
36541         // do nothing
36542     },
36543
36544     // private
36545     mimicBlur : function(e, t){
36546         if(!this.wrap.contains(t) && this.validateBlur()){
36547             this.triggerBlur();
36548         }
36549     },
36550
36551     // private
36552     triggerBlur : function(){
36553         this.mimicing = false;
36554         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36555         if(this.monitorTab){
36556             this.el.un("keydown", this.checkTab, this);
36557         }
36558         this.wrap.removeClass('x-trigger-wrap-focus');
36559         Roo.form.TriggerField.superclass.onBlur.call(this);
36560     },
36561
36562     // private
36563     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36564     validateBlur : function(e, t){
36565         return true;
36566     },
36567
36568     // private
36569     onDisable : function(){
36570         Roo.form.TriggerField.superclass.onDisable.call(this);
36571         if(this.wrap){
36572             this.wrap.addClass('x-item-disabled');
36573         }
36574     },
36575
36576     // private
36577     onEnable : function(){
36578         Roo.form.TriggerField.superclass.onEnable.call(this);
36579         if(this.wrap){
36580             this.wrap.removeClass('x-item-disabled');
36581         }
36582     },
36583
36584     // private
36585     onShow : function(){
36586         var ae = this.getActionEl();
36587         
36588         if(ae){
36589             ae.dom.style.display = '';
36590             ae.dom.style.visibility = 'visible';
36591         }
36592     },
36593
36594     // private
36595     
36596     onHide : function(){
36597         var ae = this.getActionEl();
36598         ae.dom.style.display = 'none';
36599     },
36600
36601     /**
36602      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36603      * by an implementing function.
36604      * @method
36605      * @param {EventObject} e
36606      */
36607     onTriggerClick : Roo.emptyFn
36608 });
36609
36610 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36611 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36612 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36613 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36614     initComponent : function(){
36615         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36616
36617         this.triggerConfig = {
36618             tag:'span', cls:'x-form-twin-triggers', cn:[
36619             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36620             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36621         ]};
36622     },
36623
36624     getTrigger : function(index){
36625         return this.triggers[index];
36626     },
36627
36628     initTrigger : function(){
36629         var ts = this.trigger.select('.x-form-trigger', true);
36630         this.wrap.setStyle('overflow', 'hidden');
36631         var triggerField = this;
36632         ts.each(function(t, all, index){
36633             t.hide = function(){
36634                 var w = triggerField.wrap.getWidth();
36635                 this.dom.style.display = 'none';
36636                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36637             };
36638             t.show = function(){
36639                 var w = triggerField.wrap.getWidth();
36640                 this.dom.style.display = '';
36641                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36642             };
36643             var triggerIndex = 'Trigger'+(index+1);
36644
36645             if(this['hide'+triggerIndex]){
36646                 t.dom.style.display = 'none';
36647             }
36648             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36649             t.addClassOnOver('x-form-trigger-over');
36650             t.addClassOnClick('x-form-trigger-click');
36651         }, this);
36652         this.triggers = ts.elements;
36653     },
36654
36655     onTrigger1Click : Roo.emptyFn,
36656     onTrigger2Click : Roo.emptyFn
36657 });/*
36658  * Based on:
36659  * Ext JS Library 1.1.1
36660  * Copyright(c) 2006-2007, Ext JS, LLC.
36661  *
36662  * Originally Released Under LGPL - original licence link has changed is not relivant.
36663  *
36664  * Fork - LGPL
36665  * <script type="text/javascript">
36666  */
36667  
36668 /**
36669  * @class Roo.form.TextArea
36670  * @extends Roo.form.TextField
36671  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36672  * support for auto-sizing.
36673  * @constructor
36674  * Creates a new TextArea
36675  * @param {Object} config Configuration options
36676  */
36677 Roo.form.TextArea = function(config){
36678     Roo.form.TextArea.superclass.constructor.call(this, config);
36679     // these are provided exchanges for backwards compat
36680     // minHeight/maxHeight were replaced by growMin/growMax to be
36681     // compatible with TextField growing config values
36682     if(this.minHeight !== undefined){
36683         this.growMin = this.minHeight;
36684     }
36685     if(this.maxHeight !== undefined){
36686         this.growMax = this.maxHeight;
36687     }
36688 };
36689
36690 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36691     /**
36692      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36693      */
36694     growMin : 60,
36695     /**
36696      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36697      */
36698     growMax: 1000,
36699     /**
36700      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36701      * in the field (equivalent to setting overflow: hidden, defaults to false)
36702      */
36703     preventScrollbars: false,
36704     /**
36705      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36706      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36707      */
36708
36709     // private
36710     onRender : function(ct, position){
36711         if(!this.el){
36712             this.defaultAutoCreate = {
36713                 tag: "textarea",
36714                 style:"width:300px;height:60px;",
36715                 autocomplete: "off"
36716             };
36717         }
36718         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36719         if(this.grow){
36720             this.textSizeEl = Roo.DomHelper.append(document.body, {
36721                 tag: "pre", cls: "x-form-grow-sizer"
36722             });
36723             if(this.preventScrollbars){
36724                 this.el.setStyle("overflow", "hidden");
36725             }
36726             this.el.setHeight(this.growMin);
36727         }
36728     },
36729
36730     onDestroy : function(){
36731         if(this.textSizeEl){
36732             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36733         }
36734         Roo.form.TextArea.superclass.onDestroy.call(this);
36735     },
36736
36737     // private
36738     onKeyUp : function(e){
36739         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36740             this.autoSize();
36741         }
36742     },
36743
36744     /**
36745      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36746      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36747      */
36748     autoSize : function(){
36749         if(!this.grow || !this.textSizeEl){
36750             return;
36751         }
36752         var el = this.el;
36753         var v = el.dom.value;
36754         var ts = this.textSizeEl;
36755
36756         ts.innerHTML = '';
36757         ts.appendChild(document.createTextNode(v));
36758         v = ts.innerHTML;
36759
36760         Roo.fly(ts).setWidth(this.el.getWidth());
36761         if(v.length < 1){
36762             v = "&#160;&#160;";
36763         }else{
36764             if(Roo.isIE){
36765                 v = v.replace(/\n/g, '<p>&#160;</p>');
36766             }
36767             v += "&#160;\n&#160;";
36768         }
36769         ts.innerHTML = v;
36770         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36771         if(h != this.lastHeight){
36772             this.lastHeight = h;
36773             this.el.setHeight(h);
36774             this.fireEvent("autosize", this, h);
36775         }
36776     }
36777 });/*
36778  * Based on:
36779  * Ext JS Library 1.1.1
36780  * Copyright(c) 2006-2007, Ext JS, LLC.
36781  *
36782  * Originally Released Under LGPL - original licence link has changed is not relivant.
36783  *
36784  * Fork - LGPL
36785  * <script type="text/javascript">
36786  */
36787  
36788
36789 /**
36790  * @class Roo.form.NumberField
36791  * @extends Roo.form.TextField
36792  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36793  * @constructor
36794  * Creates a new NumberField
36795  * @param {Object} config Configuration options
36796  */
36797 Roo.form.NumberField = function(config){
36798     Roo.form.NumberField.superclass.constructor.call(this, config);
36799 };
36800
36801 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36802     /**
36803      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36804      */
36805     fieldClass: "x-form-field x-form-num-field",
36806     /**
36807      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36808      */
36809     allowDecimals : true,
36810     /**
36811      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36812      */
36813     decimalSeparator : ".",
36814     /**
36815      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36816      */
36817     decimalPrecision : 2,
36818     /**
36819      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36820      */
36821     allowNegative : true,
36822     /**
36823      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36824      */
36825     minValue : Number.NEGATIVE_INFINITY,
36826     /**
36827      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36828      */
36829     maxValue : Number.MAX_VALUE,
36830     /**
36831      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36832      */
36833     minText : "The minimum value for this field is {0}",
36834     /**
36835      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36836      */
36837     maxText : "The maximum value for this field is {0}",
36838     /**
36839      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36840      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36841      */
36842     nanText : "{0} is not a valid number",
36843
36844     // private
36845     initEvents : function(){
36846         Roo.form.NumberField.superclass.initEvents.call(this);
36847         var allowed = "0123456789";
36848         if(this.allowDecimals){
36849             allowed += this.decimalSeparator;
36850         }
36851         if(this.allowNegative){
36852             allowed += "-";
36853         }
36854         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36855         var keyPress = function(e){
36856             var k = e.getKey();
36857             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36858                 return;
36859             }
36860             var c = e.getCharCode();
36861             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36862                 e.stopEvent();
36863             }
36864         };
36865         this.el.on("keypress", keyPress, this);
36866     },
36867
36868     // private
36869     validateValue : function(value){
36870         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36871             return false;
36872         }
36873         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36874              return true;
36875         }
36876         var num = this.parseValue(value);
36877         if(isNaN(num)){
36878             this.markInvalid(String.format(this.nanText, value));
36879             return false;
36880         }
36881         if(num < this.minValue){
36882             this.markInvalid(String.format(this.minText, this.minValue));
36883             return false;
36884         }
36885         if(num > this.maxValue){
36886             this.markInvalid(String.format(this.maxText, this.maxValue));
36887             return false;
36888         }
36889         return true;
36890     },
36891
36892     getValue : function(){
36893         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36894     },
36895
36896     // private
36897     parseValue : function(value){
36898         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36899         return isNaN(value) ? '' : value;
36900     },
36901
36902     // private
36903     fixPrecision : function(value){
36904         var nan = isNaN(value);
36905         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36906             return nan ? '' : value;
36907         }
36908         return parseFloat(value).toFixed(this.decimalPrecision);
36909     },
36910
36911     setValue : function(v){
36912         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36913     },
36914
36915     // private
36916     decimalPrecisionFcn : function(v){
36917         return Math.floor(v);
36918     },
36919
36920     beforeBlur : function(){
36921         var v = this.parseValue(this.getRawValue());
36922         if(v){
36923             this.setValue(this.fixPrecision(v));
36924         }
36925     }
36926 });/*
36927  * Based on:
36928  * Ext JS Library 1.1.1
36929  * Copyright(c) 2006-2007, Ext JS, LLC.
36930  *
36931  * Originally Released Under LGPL - original licence link has changed is not relivant.
36932  *
36933  * Fork - LGPL
36934  * <script type="text/javascript">
36935  */
36936  
36937 /**
36938  * @class Roo.form.DateField
36939  * @extends Roo.form.TriggerField
36940  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36941 * @constructor
36942 * Create a new DateField
36943 * @param {Object} config
36944  */
36945 Roo.form.DateField = function(config){
36946     Roo.form.DateField.superclass.constructor.call(this, config);
36947     
36948       this.addEvents({
36949          
36950         /**
36951          * @event select
36952          * Fires when a date is selected
36953              * @param {Roo.form.DateField} combo This combo box
36954              * @param {Date} date The date selected
36955              */
36956         'select' : true
36957          
36958     });
36959     
36960     
36961     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36962     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36963     this.ddMatch = null;
36964     if(this.disabledDates){
36965         var dd = this.disabledDates;
36966         var re = "(?:";
36967         for(var i = 0; i < dd.length; i++){
36968             re += dd[i];
36969             if(i != dd.length-1) re += "|";
36970         }
36971         this.ddMatch = new RegExp(re + ")");
36972     }
36973 };
36974
36975 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36976     /**
36977      * @cfg {String} format
36978      * The default date format string which can be overriden for localization support.  The format must be
36979      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36980      */
36981     format : "m/d/y",
36982     /**
36983      * @cfg {String} altFormats
36984      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36985      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36986      */
36987     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36988     /**
36989      * @cfg {Array} disabledDays
36990      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36991      */
36992     disabledDays : null,
36993     /**
36994      * @cfg {String} disabledDaysText
36995      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36996      */
36997     disabledDaysText : "Disabled",
36998     /**
36999      * @cfg {Array} disabledDates
37000      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37001      * expression so they are very powerful. Some examples:
37002      * <ul>
37003      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37004      * <li>["03/08", "09/16"] would disable those days for every year</li>
37005      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37006      * <li>["03/../2006"] would disable every day in March 2006</li>
37007      * <li>["^03"] would disable every day in every March</li>
37008      * </ul>
37009      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37010      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37011      */
37012     disabledDates : null,
37013     /**
37014      * @cfg {String} disabledDatesText
37015      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37016      */
37017     disabledDatesText : "Disabled",
37018     /**
37019      * @cfg {Date/String} minValue
37020      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37021      * valid format (defaults to null).
37022      */
37023     minValue : null,
37024     /**
37025      * @cfg {Date/String} maxValue
37026      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37027      * valid format (defaults to null).
37028      */
37029     maxValue : null,
37030     /**
37031      * @cfg {String} minText
37032      * The error text to display when the date in the cell is before minValue (defaults to
37033      * 'The date in this field must be after {minValue}').
37034      */
37035     minText : "The date in this field must be equal to or after {0}",
37036     /**
37037      * @cfg {String} maxText
37038      * The error text to display when the date in the cell is after maxValue (defaults to
37039      * 'The date in this field must be before {maxValue}').
37040      */
37041     maxText : "The date in this field must be equal to or before {0}",
37042     /**
37043      * @cfg {String} invalidText
37044      * The error text to display when the date in the field is invalid (defaults to
37045      * '{value} is not a valid date - it must be in the format {format}').
37046      */
37047     invalidText : "{0} is not a valid date - it must be in the format {1}",
37048     /**
37049      * @cfg {String} triggerClass
37050      * An additional CSS class used to style the trigger button.  The trigger will always get the
37051      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37052      * which displays a calendar icon).
37053      */
37054     triggerClass : 'x-form-date-trigger',
37055     
37056
37057     /**
37058      * @cfg {bool} useIso
37059      * if enabled, then the date field will use a hidden field to store the 
37060      * real value as iso formated date. default (false)
37061      */ 
37062     useIso : false,
37063     /**
37064      * @cfg {String/Object} autoCreate
37065      * A DomHelper element spec, or true for a default element spec (defaults to
37066      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37067      */ 
37068     // private
37069     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37070     
37071     // private
37072     hiddenField: false,
37073     
37074     onRender : function(ct, position)
37075     {
37076         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37077         if (this.useIso) {
37078             this.el.dom.removeAttribute('name'); 
37079             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37080                     'before', true);
37081             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37082             // prevent input submission
37083             this.hiddenName = this.name;
37084         }
37085             
37086             
37087     },
37088     
37089     // private
37090     validateValue : function(value)
37091     {
37092         value = this.formatDate(value);
37093         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37094             return false;
37095         }
37096         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37097              return true;
37098         }
37099         var svalue = value;
37100         value = this.parseDate(value);
37101         if(!value){
37102             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37103             return false;
37104         }
37105         var time = value.getTime();
37106         if(this.minValue && time < this.minValue.getTime()){
37107             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37108             return false;
37109         }
37110         if(this.maxValue && time > this.maxValue.getTime()){
37111             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37112             return false;
37113         }
37114         if(this.disabledDays){
37115             var day = value.getDay();
37116             for(var i = 0; i < this.disabledDays.length; i++) {
37117                 if(day === this.disabledDays[i]){
37118                     this.markInvalid(this.disabledDaysText);
37119                     return false;
37120                 }
37121             }
37122         }
37123         var fvalue = this.formatDate(value);
37124         if(this.ddMatch && this.ddMatch.test(fvalue)){
37125             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37126             return false;
37127         }
37128         return true;
37129     },
37130
37131     // private
37132     // Provides logic to override the default TriggerField.validateBlur which just returns true
37133     validateBlur : function(){
37134         return !this.menu || !this.menu.isVisible();
37135     },
37136
37137     /**
37138      * Returns the current date value of the date field.
37139      * @return {Date} The date value
37140      */
37141     getValue : function(){
37142         
37143         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37144     },
37145
37146     /**
37147      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37148      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37149      * (the default format used is "m/d/y").
37150      * <br />Usage:
37151      * <pre><code>
37152 //All of these calls set the same date value (May 4, 2006)
37153
37154 //Pass a date object:
37155 var dt = new Date('5/4/06');
37156 dateField.setValue(dt);
37157
37158 //Pass a date string (default format):
37159 dateField.setValue('5/4/06');
37160
37161 //Pass a date string (custom format):
37162 dateField.format = 'Y-m-d';
37163 dateField.setValue('2006-5-4');
37164 </code></pre>
37165      * @param {String/Date} date The date or valid date string
37166      */
37167     setValue : function(date){
37168         if (this.hiddenField) {
37169             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37170         }
37171         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37172     },
37173
37174     // private
37175     parseDate : function(value){
37176         if(!value || value instanceof Date){
37177             return value;
37178         }
37179         var v = Date.parseDate(value, this.format);
37180         if(!v && this.altFormats){
37181             if(!this.altFormatsArray){
37182                 this.altFormatsArray = this.altFormats.split("|");
37183             }
37184             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37185                 v = Date.parseDate(value, this.altFormatsArray[i]);
37186             }
37187         }
37188         return v;
37189     },
37190
37191     // private
37192     formatDate : function(date, fmt){
37193         return (!date || !(date instanceof Date)) ?
37194                date : date.dateFormat(fmt || this.format);
37195     },
37196
37197     // private
37198     menuListeners : {
37199         select: function(m, d){
37200             this.setValue(d);
37201             this.fireEvent('select', this, d);
37202         },
37203         show : function(){ // retain focus styling
37204             this.onFocus();
37205         },
37206         hide : function(){
37207             this.focus.defer(10, this);
37208             var ml = this.menuListeners;
37209             this.menu.un("select", ml.select,  this);
37210             this.menu.un("show", ml.show,  this);
37211             this.menu.un("hide", ml.hide,  this);
37212         }
37213     },
37214
37215     // private
37216     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37217     onTriggerClick : function(){
37218         if(this.disabled){
37219             return;
37220         }
37221         if(this.menu == null){
37222             this.menu = new Roo.menu.DateMenu();
37223         }
37224         Roo.apply(this.menu.picker,  {
37225             showClear: this.allowBlank,
37226             minDate : this.minValue,
37227             maxDate : this.maxValue,
37228             disabledDatesRE : this.ddMatch,
37229             disabledDatesText : this.disabledDatesText,
37230             disabledDays : this.disabledDays,
37231             disabledDaysText : this.disabledDaysText,
37232             format : this.format,
37233             minText : String.format(this.minText, this.formatDate(this.minValue)),
37234             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37235         });
37236         this.menu.on(Roo.apply({}, this.menuListeners, {
37237             scope:this
37238         }));
37239         this.menu.picker.setValue(this.getValue() || new Date());
37240         this.menu.show(this.el, "tl-bl?");
37241     },
37242
37243     beforeBlur : function(){
37244         var v = this.parseDate(this.getRawValue());
37245         if(v){
37246             this.setValue(v);
37247         }
37248     }
37249
37250     /** @cfg {Boolean} grow @hide */
37251     /** @cfg {Number} growMin @hide */
37252     /** @cfg {Number} growMax @hide */
37253     /**
37254      * @hide
37255      * @method autoSize
37256      */
37257 });/*
37258  * Based on:
37259  * Ext JS Library 1.1.1
37260  * Copyright(c) 2006-2007, Ext JS, LLC.
37261  *
37262  * Originally Released Under LGPL - original licence link has changed is not relivant.
37263  *
37264  * Fork - LGPL
37265  * <script type="text/javascript">
37266  */
37267  
37268
37269 /**
37270  * @class Roo.form.ComboBox
37271  * @extends Roo.form.TriggerField
37272  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37273  * @constructor
37274  * Create a new ComboBox.
37275  * @param {Object} config Configuration options
37276  */
37277 Roo.form.ComboBox = function(config){
37278     Roo.form.ComboBox.superclass.constructor.call(this, config);
37279     this.addEvents({
37280         /**
37281          * @event expand
37282          * Fires when the dropdown list is expanded
37283              * @param {Roo.form.ComboBox} combo This combo box
37284              */
37285         'expand' : true,
37286         /**
37287          * @event collapse
37288          * Fires when the dropdown list is collapsed
37289              * @param {Roo.form.ComboBox} combo This combo box
37290              */
37291         'collapse' : true,
37292         /**
37293          * @event beforeselect
37294          * Fires before a list item is selected. Return false to cancel the selection.
37295              * @param {Roo.form.ComboBox} combo This combo box
37296              * @param {Roo.data.Record} record The data record returned from the underlying store
37297              * @param {Number} index The index of the selected item in the dropdown list
37298              */
37299         'beforeselect' : true,
37300         /**
37301          * @event select
37302          * Fires when a list item is selected
37303              * @param {Roo.form.ComboBox} combo This combo box
37304              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37305              * @param {Number} index The index of the selected item in the dropdown list
37306              */
37307         'select' : true,
37308         /**
37309          * @event beforequery
37310          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37311          * The event object passed has these properties:
37312              * @param {Roo.form.ComboBox} combo This combo box
37313              * @param {String} query The query
37314              * @param {Boolean} forceAll true to force "all" query
37315              * @param {Boolean} cancel true to cancel the query
37316              * @param {Object} e The query event object
37317              */
37318         'beforequery': true,
37319          /**
37320          * @event add
37321          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37322              * @param {Roo.form.ComboBox} combo This combo box
37323              */
37324         'add' : true,
37325         /**
37326          * @event edit
37327          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37328              * @param {Roo.form.ComboBox} combo This combo box
37329              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37330              */
37331         'edit' : true
37332         
37333         
37334     });
37335     if(this.transform){
37336         this.allowDomMove = false;
37337         var s = Roo.getDom(this.transform);
37338         if(!this.hiddenName){
37339             this.hiddenName = s.name;
37340         }
37341         if(!this.store){
37342             this.mode = 'local';
37343             var d = [], opts = s.options;
37344             for(var i = 0, len = opts.length;i < len; i++){
37345                 var o = opts[i];
37346                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37347                 if(o.selected) {
37348                     this.value = value;
37349                 }
37350                 d.push([value, o.text]);
37351             }
37352             this.store = new Roo.data.SimpleStore({
37353                 'id': 0,
37354                 fields: ['value', 'text'],
37355                 data : d
37356             });
37357             this.valueField = 'value';
37358             this.displayField = 'text';
37359         }
37360         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37361         if(!this.lazyRender){
37362             this.target = true;
37363             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37364             s.parentNode.removeChild(s); // remove it
37365             this.render(this.el.parentNode);
37366         }else{
37367             s.parentNode.removeChild(s); // remove it
37368         }
37369
37370     }
37371     if (this.store) {
37372         this.store = Roo.factory(this.store, Roo.data);
37373     }
37374     
37375     this.selectedIndex = -1;
37376     if(this.mode == 'local'){
37377         if(config.queryDelay === undefined){
37378             this.queryDelay = 10;
37379         }
37380         if(config.minChars === undefined){
37381             this.minChars = 0;
37382         }
37383     }
37384 };
37385
37386 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37387     /**
37388      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37389      */
37390     /**
37391      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37392      * rendering into an Roo.Editor, defaults to false)
37393      */
37394     /**
37395      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37396      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37397      */
37398     /**
37399      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37400      */
37401     /**
37402      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37403      * the dropdown list (defaults to undefined, with no header element)
37404      */
37405
37406      /**
37407      * @cfg {String/Roo.Template} tpl The template to use to render the output
37408      */
37409      
37410     // private
37411     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37412     /**
37413      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37414      */
37415     listWidth: undefined,
37416     /**
37417      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37418      * mode = 'remote' or 'text' if mode = 'local')
37419      */
37420     displayField: undefined,
37421     /**
37422      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37423      * mode = 'remote' or 'value' if mode = 'local'). 
37424      * Note: use of a valueField requires the user make a selection
37425      * in order for a value to be mapped.
37426      */
37427     valueField: undefined,
37428     
37429     
37430     /**
37431      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37432      * field's data value (defaults to the underlying DOM element's name)
37433      */
37434     hiddenName: undefined,
37435     /**
37436      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37437      */
37438     listClass: '',
37439     /**
37440      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37441      */
37442     selectedClass: 'x-combo-selected',
37443     /**
37444      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37445      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37446      * which displays a downward arrow icon).
37447      */
37448     triggerClass : 'x-form-arrow-trigger',
37449     /**
37450      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37451      */
37452     shadow:'sides',
37453     /**
37454      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37455      * anchor positions (defaults to 'tl-bl')
37456      */
37457     listAlign: 'tl-bl?',
37458     /**
37459      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37460      */
37461     maxHeight: 300,
37462     /**
37463      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37464      * query specified by the allQuery config option (defaults to 'query')
37465      */
37466     triggerAction: 'query',
37467     /**
37468      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37469      * (defaults to 4, does not apply if editable = false)
37470      */
37471     minChars : 4,
37472     /**
37473      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37474      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37475      */
37476     typeAhead: false,
37477     /**
37478      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37479      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37480      */
37481     queryDelay: 500,
37482     /**
37483      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37484      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37485      */
37486     pageSize: 0,
37487     /**
37488      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37489      * when editable = true (defaults to false)
37490      */
37491     selectOnFocus:false,
37492     /**
37493      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37494      */
37495     queryParam: 'query',
37496     /**
37497      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37498      * when mode = 'remote' (defaults to 'Loading...')
37499      */
37500     loadingText: 'Loading...',
37501     /**
37502      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37503      */
37504     resizable: false,
37505     /**
37506      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37507      */
37508     handleHeight : 8,
37509     /**
37510      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37511      * traditional select (defaults to true)
37512      */
37513     editable: true,
37514     /**
37515      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37516      */
37517     allQuery: '',
37518     /**
37519      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37520      */
37521     mode: 'remote',
37522     /**
37523      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37524      * listWidth has a higher value)
37525      */
37526     minListWidth : 70,
37527     /**
37528      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37529      * allow the user to set arbitrary text into the field (defaults to false)
37530      */
37531     forceSelection:false,
37532     /**
37533      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37534      * if typeAhead = true (defaults to 250)
37535      */
37536     typeAheadDelay : 250,
37537     /**
37538      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37539      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37540      */
37541     valueNotFoundText : undefined,
37542     /**
37543      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37544      */
37545     blockFocus : false,
37546     
37547     /**
37548      * @cfg {Boolean} disableClear Disable showing of clear button.
37549      */
37550     disableClear : false,
37551     /**
37552      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37553      */
37554     alwaysQuery : false,
37555     
37556     //private
37557     addicon : false,
37558     editicon: false,
37559     
37560     // element that contains real text value.. (when hidden is used..)
37561      
37562     // private
37563     onRender : function(ct, position){
37564         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37565         if(this.hiddenName){
37566             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37567                     'before', true);
37568             this.hiddenField.value =
37569                 this.hiddenValue !== undefined ? this.hiddenValue :
37570                 this.value !== undefined ? this.value : '';
37571
37572             // prevent input submission
37573             this.el.dom.removeAttribute('name');
37574              
37575              
37576         }
37577         if(Roo.isGecko){
37578             this.el.dom.setAttribute('autocomplete', 'off');
37579         }
37580
37581         var cls = 'x-combo-list';
37582
37583         this.list = new Roo.Layer({
37584             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37585         });
37586
37587         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37588         this.list.setWidth(lw);
37589         this.list.swallowEvent('mousewheel');
37590         this.assetHeight = 0;
37591
37592         if(this.title){
37593             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37594             this.assetHeight += this.header.getHeight();
37595         }
37596
37597         this.innerList = this.list.createChild({cls:cls+'-inner'});
37598         this.innerList.on('mouseover', this.onViewOver, this);
37599         this.innerList.on('mousemove', this.onViewMove, this);
37600         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37601         
37602         if(this.allowBlank && !this.pageSize && !this.disableClear){
37603             this.footer = this.list.createChild({cls:cls+'-ft'});
37604             this.pageTb = new Roo.Toolbar(this.footer);
37605            
37606         }
37607         if(this.pageSize){
37608             this.footer = this.list.createChild({cls:cls+'-ft'});
37609             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37610                     {pageSize: this.pageSize});
37611             
37612         }
37613         
37614         if (this.pageTb && this.allowBlank && !this.disableClear) {
37615             var _this = this;
37616             this.pageTb.add(new Roo.Toolbar.Fill(), {
37617                 cls: 'x-btn-icon x-btn-clear',
37618                 text: '&#160;',
37619                 handler: function()
37620                 {
37621                     _this.collapse();
37622                     _this.clearValue();
37623                     _this.onSelect(false, -1);
37624                 }
37625             });
37626         }
37627         if (this.footer) {
37628             this.assetHeight += this.footer.getHeight();
37629         }
37630         
37631
37632         if(!this.tpl){
37633             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37634         }
37635
37636         this.view = new Roo.View(this.innerList, this.tpl, {
37637             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37638         });
37639
37640         this.view.on('click', this.onViewClick, this);
37641
37642         this.store.on('beforeload', this.onBeforeLoad, this);
37643         this.store.on('load', this.onLoad, this);
37644         this.store.on('loadexception', this.collapse, this);
37645
37646         if(this.resizable){
37647             this.resizer = new Roo.Resizable(this.list,  {
37648                pinned:true, handles:'se'
37649             });
37650             this.resizer.on('resize', function(r, w, h){
37651                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37652                 this.listWidth = w;
37653                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37654                 this.restrictHeight();
37655             }, this);
37656             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37657         }
37658         if(!this.editable){
37659             this.editable = true;
37660             this.setEditable(false);
37661         }  
37662         
37663         
37664         if (typeof(this.events.add.listeners) != 'undefined') {
37665             
37666             this.addicon = this.wrap.createChild(
37667                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37668        
37669             this.addicon.on('click', function(e) {
37670                 this.fireEvent('add', this);
37671             }, this);
37672         }
37673         if (typeof(this.events.edit.listeners) != 'undefined') {
37674             
37675             this.editicon = this.wrap.createChild(
37676                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37677             if (this.addicon) {
37678                 this.editicon.setStyle('margin-left', '40px');
37679             }
37680             this.editicon.on('click', function(e) {
37681                 
37682                 // we fire even  if inothing is selected..
37683                 this.fireEvent('edit', this, this.lastData );
37684                 
37685             }, this);
37686         }
37687         
37688         
37689         
37690     },
37691
37692     // private
37693     initEvents : function(){
37694         Roo.form.ComboBox.superclass.initEvents.call(this);
37695
37696         this.keyNav = new Roo.KeyNav(this.el, {
37697             "up" : function(e){
37698                 this.inKeyMode = true;
37699                 this.selectPrev();
37700             },
37701
37702             "down" : function(e){
37703                 if(!this.isExpanded()){
37704                     this.onTriggerClick();
37705                 }else{
37706                     this.inKeyMode = true;
37707                     this.selectNext();
37708                 }
37709             },
37710
37711             "enter" : function(e){
37712                 this.onViewClick();
37713                 //return true;
37714             },
37715
37716             "esc" : function(e){
37717                 this.collapse();
37718             },
37719
37720             "tab" : function(e){
37721                 this.onViewClick(false);
37722                 this.fireEvent("specialkey", this, e);
37723                 return true;
37724             },
37725
37726             scope : this,
37727
37728             doRelay : function(foo, bar, hname){
37729                 if(hname == 'down' || this.scope.isExpanded()){
37730                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37731                 }
37732                 return true;
37733             },
37734
37735             forceKeyDown: true
37736         });
37737         this.queryDelay = Math.max(this.queryDelay || 10,
37738                 this.mode == 'local' ? 10 : 250);
37739         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37740         if(this.typeAhead){
37741             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37742         }
37743         if(this.editable !== false){
37744             this.el.on("keyup", this.onKeyUp, this);
37745         }
37746         if(this.forceSelection){
37747             this.on('blur', this.doForce, this);
37748         }
37749     },
37750
37751     onDestroy : function(){
37752         if(this.view){
37753             this.view.setStore(null);
37754             this.view.el.removeAllListeners();
37755             this.view.el.remove();
37756             this.view.purgeListeners();
37757         }
37758         if(this.list){
37759             this.list.destroy();
37760         }
37761         if(this.store){
37762             this.store.un('beforeload', this.onBeforeLoad, this);
37763             this.store.un('load', this.onLoad, this);
37764             this.store.un('loadexception', this.collapse, this);
37765         }
37766         Roo.form.ComboBox.superclass.onDestroy.call(this);
37767     },
37768
37769     // private
37770     fireKey : function(e){
37771         if(e.isNavKeyPress() && !this.list.isVisible()){
37772             this.fireEvent("specialkey", this, e);
37773         }
37774     },
37775
37776     // private
37777     onResize: function(w, h){
37778         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37779         
37780         if(typeof w != 'number'){
37781             // we do not handle it!?!?
37782             return;
37783         }
37784         var tw = this.trigger.getWidth();
37785         tw += this.addicon ? this.addicon.getWidth() : 0;
37786         tw += this.editicon ? this.editicon.getWidth() : 0;
37787         var x = w - tw;
37788         this.el.setWidth( this.adjustWidth('input', x));
37789             
37790         this.trigger.setStyle('left', x+'px');
37791         
37792         if(this.list && this.listWidth === undefined){
37793             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37794             this.list.setWidth(lw);
37795             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37796         }
37797         
37798     
37799         
37800     },
37801
37802     /**
37803      * Allow or prevent the user from directly editing the field text.  If false is passed,
37804      * the user will only be able to select from the items defined in the dropdown list.  This method
37805      * is the runtime equivalent of setting the 'editable' config option at config time.
37806      * @param {Boolean} value True to allow the user to directly edit the field text
37807      */
37808     setEditable : function(value){
37809         if(value == this.editable){
37810             return;
37811         }
37812         this.editable = value;
37813         if(!value){
37814             this.el.dom.setAttribute('readOnly', true);
37815             this.el.on('mousedown', this.onTriggerClick,  this);
37816             this.el.addClass('x-combo-noedit');
37817         }else{
37818             this.el.dom.setAttribute('readOnly', false);
37819             this.el.un('mousedown', this.onTriggerClick,  this);
37820             this.el.removeClass('x-combo-noedit');
37821         }
37822     },
37823
37824     // private
37825     onBeforeLoad : function(){
37826         if(!this.hasFocus){
37827             return;
37828         }
37829         this.innerList.update(this.loadingText ?
37830                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37831         this.restrictHeight();
37832         this.selectedIndex = -1;
37833     },
37834
37835     // private
37836     onLoad : function(){
37837         if(!this.hasFocus){
37838             return;
37839         }
37840         if(this.store.getCount() > 0){
37841             this.expand();
37842             this.restrictHeight();
37843             if(this.lastQuery == this.allQuery){
37844                 if(this.editable){
37845                     this.el.dom.select();
37846                 }
37847                 if(!this.selectByValue(this.value, true)){
37848                     this.select(0, true);
37849                 }
37850             }else{
37851                 this.selectNext();
37852                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37853                     this.taTask.delay(this.typeAheadDelay);
37854                 }
37855             }
37856         }else{
37857             this.onEmptyResults();
37858         }
37859         //this.el.focus();
37860     },
37861
37862     // private
37863     onTypeAhead : function(){
37864         if(this.store.getCount() > 0){
37865             var r = this.store.getAt(0);
37866             var newValue = r.data[this.displayField];
37867             var len = newValue.length;
37868             var selStart = this.getRawValue().length;
37869             if(selStart != len){
37870                 this.setRawValue(newValue);
37871                 this.selectText(selStart, newValue.length);
37872             }
37873         }
37874     },
37875
37876     // private
37877     onSelect : function(record, index){
37878         if(this.fireEvent('beforeselect', this, record, index) !== false){
37879             this.setFromData(index > -1 ? record.data : false);
37880             this.collapse();
37881             this.fireEvent('select', this, record, index);
37882         }
37883     },
37884
37885     /**
37886      * Returns the currently selected field value or empty string if no value is set.
37887      * @return {String} value The selected value
37888      */
37889     getValue : function(){
37890         if(this.valueField){
37891             return typeof this.value != 'undefined' ? this.value : '';
37892         }else{
37893             return Roo.form.ComboBox.superclass.getValue.call(this);
37894         }
37895     },
37896
37897     /**
37898      * Clears any text/value currently set in the field
37899      */
37900     clearValue : function(){
37901         if(this.hiddenField){
37902             this.hiddenField.value = '';
37903         }
37904         this.value = '';
37905         this.setRawValue('');
37906         this.lastSelectionText = '';
37907         this.applyEmptyText();
37908     },
37909
37910     /**
37911      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37912      * will be displayed in the field.  If the value does not match the data value of an existing item,
37913      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37914      * Otherwise the field will be blank (although the value will still be set).
37915      * @param {String} value The value to match
37916      */
37917     setValue : function(v){
37918         var text = v;
37919         if(this.valueField){
37920             var r = this.findRecord(this.valueField, v);
37921             if(r){
37922                 text = r.data[this.displayField];
37923             }else if(this.valueNotFoundText !== undefined){
37924                 text = this.valueNotFoundText;
37925             }
37926         }
37927         this.lastSelectionText = text;
37928         if(this.hiddenField){
37929             this.hiddenField.value = v;
37930         }
37931         Roo.form.ComboBox.superclass.setValue.call(this, text);
37932         this.value = v;
37933     },
37934     /**
37935      * @property {Object} the last set data for the element
37936      */
37937     
37938     lastData : false,
37939     /**
37940      * Sets the value of the field based on a object which is related to the record format for the store.
37941      * @param {Object} value the value to set as. or false on reset?
37942      */
37943     setFromData : function(o){
37944         var dv = ''; // display value
37945         var vv = ''; // value value..
37946         this.lastData = o;
37947         if (this.displayField) {
37948             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37949         } else {
37950             // this is an error condition!!!
37951             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37952         }
37953         
37954         if(this.valueField){
37955             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37956         }
37957         if(this.hiddenField){
37958             this.hiddenField.value = vv;
37959             
37960             this.lastSelectionText = dv;
37961             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37962             this.value = vv;
37963             return;
37964         }
37965         // no hidden field.. - we store the value in 'value', but still display
37966         // display field!!!!
37967         this.lastSelectionText = dv;
37968         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37969         this.value = vv;
37970         
37971         
37972     },
37973     // private
37974     reset : function(){
37975         // overridden so that last data is reset..
37976         this.setValue(this.originalValue);
37977         this.clearInvalid();
37978         this.lastData = false;
37979     },
37980     // private
37981     findRecord : function(prop, value){
37982         var record;
37983         if(this.store.getCount() > 0){
37984             this.store.each(function(r){
37985                 if(r.data[prop] == value){
37986                     record = r;
37987                     return false;
37988                 }
37989                 return true;
37990             });
37991         }
37992         return record;
37993     },
37994     
37995     getName: function()
37996     {
37997         // returns hidden if it's set..
37998         if (!this.rendered) {return ''};
37999         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38000         
38001     },
38002     // private
38003     onViewMove : function(e, t){
38004         this.inKeyMode = false;
38005     },
38006
38007     // private
38008     onViewOver : function(e, t){
38009         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38010             return;
38011         }
38012         var item = this.view.findItemFromChild(t);
38013         if(item){
38014             var index = this.view.indexOf(item);
38015             this.select(index, false);
38016         }
38017     },
38018
38019     // private
38020     onViewClick : function(doFocus)
38021     {
38022         var index = this.view.getSelectedIndexes()[0];
38023         var r = this.store.getAt(index);
38024         if(r){
38025             this.onSelect(r, index);
38026         }
38027         if(doFocus !== false && !this.blockFocus){
38028             this.el.focus();
38029         }
38030     },
38031
38032     // private
38033     restrictHeight : function(){
38034         this.innerList.dom.style.height = '';
38035         var inner = this.innerList.dom;
38036         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38037         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38038         this.list.beginUpdate();
38039         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38040         this.list.alignTo(this.el, this.listAlign);
38041         this.list.endUpdate();
38042     },
38043
38044     // private
38045     onEmptyResults : function(){
38046         this.collapse();
38047     },
38048
38049     /**
38050      * Returns true if the dropdown list is expanded, else false.
38051      */
38052     isExpanded : function(){
38053         return this.list.isVisible();
38054     },
38055
38056     /**
38057      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38058      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38059      * @param {String} value The data value of the item to select
38060      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38061      * selected item if it is not currently in view (defaults to true)
38062      * @return {Boolean} True if the value matched an item in the list, else false
38063      */
38064     selectByValue : function(v, scrollIntoView){
38065         if(v !== undefined && v !== null){
38066             var r = this.findRecord(this.valueField || this.displayField, v);
38067             if(r){
38068                 this.select(this.store.indexOf(r), scrollIntoView);
38069                 return true;
38070             }
38071         }
38072         return false;
38073     },
38074
38075     /**
38076      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38077      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38078      * @param {Number} index The zero-based index of the list item to select
38079      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38080      * selected item if it is not currently in view (defaults to true)
38081      */
38082     select : function(index, scrollIntoView){
38083         this.selectedIndex = index;
38084         this.view.select(index);
38085         if(scrollIntoView !== false){
38086             var el = this.view.getNode(index);
38087             if(el){
38088                 this.innerList.scrollChildIntoView(el, false);
38089             }
38090         }
38091     },
38092
38093     // private
38094     selectNext : function(){
38095         var ct = this.store.getCount();
38096         if(ct > 0){
38097             if(this.selectedIndex == -1){
38098                 this.select(0);
38099             }else if(this.selectedIndex < ct-1){
38100                 this.select(this.selectedIndex+1);
38101             }
38102         }
38103     },
38104
38105     // private
38106     selectPrev : function(){
38107         var ct = this.store.getCount();
38108         if(ct > 0){
38109             if(this.selectedIndex == -1){
38110                 this.select(0);
38111             }else if(this.selectedIndex != 0){
38112                 this.select(this.selectedIndex-1);
38113             }
38114         }
38115     },
38116
38117     // private
38118     onKeyUp : function(e){
38119         if(this.editable !== false && !e.isSpecialKey()){
38120             this.lastKey = e.getKey();
38121             this.dqTask.delay(this.queryDelay);
38122         }
38123     },
38124
38125     // private
38126     validateBlur : function(){
38127         return !this.list || !this.list.isVisible();   
38128     },
38129
38130     // private
38131     initQuery : function(){
38132         this.doQuery(this.getRawValue());
38133     },
38134
38135     // private
38136     doForce : function(){
38137         if(this.el.dom.value.length > 0){
38138             this.el.dom.value =
38139                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38140             this.applyEmptyText();
38141         }
38142     },
38143
38144     /**
38145      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38146      * query allowing the query action to be canceled if needed.
38147      * @param {String} query The SQL query to execute
38148      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38149      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38150      * saved in the current store (defaults to false)
38151      */
38152     doQuery : function(q, forceAll){
38153         if(q === undefined || q === null){
38154             q = '';
38155         }
38156         var qe = {
38157             query: q,
38158             forceAll: forceAll,
38159             combo: this,
38160             cancel:false
38161         };
38162         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38163             return false;
38164         }
38165         q = qe.query;
38166         forceAll = qe.forceAll;
38167         if(forceAll === true || (q.length >= this.minChars)){
38168             if(this.lastQuery != q || this.alwaysQuery){
38169                 this.lastQuery = q;
38170                 if(this.mode == 'local'){
38171                     this.selectedIndex = -1;
38172                     if(forceAll){
38173                         this.store.clearFilter();
38174                     }else{
38175                         this.store.filter(this.displayField, q);
38176                     }
38177                     this.onLoad();
38178                 }else{
38179                     this.store.baseParams[this.queryParam] = q;
38180                     this.store.load({
38181                         params: this.getParams(q)
38182                     });
38183                     this.expand();
38184                 }
38185             }else{
38186                 this.selectedIndex = -1;
38187                 this.onLoad();   
38188             }
38189         }
38190     },
38191
38192     // private
38193     getParams : function(q){
38194         var p = {};
38195         //p[this.queryParam] = q;
38196         if(this.pageSize){
38197             p.start = 0;
38198             p.limit = this.pageSize;
38199         }
38200         return p;
38201     },
38202
38203     /**
38204      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38205      */
38206     collapse : function(){
38207         if(!this.isExpanded()){
38208             return;
38209         }
38210         this.list.hide();
38211         Roo.get(document).un('mousedown', this.collapseIf, this);
38212         Roo.get(document).un('mousewheel', this.collapseIf, this);
38213         if (!this.editable) {
38214             Roo.get(document).un('keydown', this.listKeyPress, this);
38215         }
38216         this.fireEvent('collapse', this);
38217     },
38218
38219     // private
38220     collapseIf : function(e){
38221         if(!e.within(this.wrap) && !e.within(this.list)){
38222             this.collapse();
38223         }
38224     },
38225
38226     /**
38227      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38228      */
38229     expand : function(){
38230         if(this.isExpanded() || !this.hasFocus){
38231             return;
38232         }
38233         this.list.alignTo(this.el, this.listAlign);
38234         this.list.show();
38235         Roo.get(document).on('mousedown', this.collapseIf, this);
38236         Roo.get(document).on('mousewheel', this.collapseIf, this);
38237         if (!this.editable) {
38238             Roo.get(document).on('keydown', this.listKeyPress, this);
38239         }
38240         
38241         this.fireEvent('expand', this);
38242     },
38243
38244     // private
38245     // Implements the default empty TriggerField.onTriggerClick function
38246     onTriggerClick : function(){
38247         if(this.disabled){
38248             return;
38249         }
38250         if(this.isExpanded()){
38251             this.collapse();
38252             if (!this.blockFocus) {
38253                 this.el.focus();
38254             }
38255             
38256         }else {
38257             this.hasFocus = true;
38258             if(this.triggerAction == 'all') {
38259                 this.doQuery(this.allQuery, true);
38260             } else {
38261                 this.doQuery(this.getRawValue());
38262             }
38263             if (!this.blockFocus) {
38264                 this.el.focus();
38265             }
38266         }
38267     },
38268     listKeyPress : function(e)
38269     {
38270         //Roo.log('listkeypress');
38271         // scroll to first matching element based on key pres..
38272         if (e.isSpecialKey()) {
38273             return false;
38274         }
38275         var k = String.fromCharCode(e.getKey()).toUpperCase();
38276         //Roo.log(k);
38277         var match  = false;
38278         var csel = this.view.getSelectedNodes();
38279         var cselitem = false;
38280         if (csel.length) {
38281             var ix = this.view.indexOf(csel[0]);
38282             cselitem  = this.store.getAt(ix);
38283             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38284                 cselitem = false;
38285             }
38286             
38287         }
38288         
38289         this.store.each(function(v) { 
38290             if (cselitem) {
38291                 // start at existing selection.
38292                 if (cselitem.id == v.id) {
38293                     cselitem = false;
38294                 }
38295                 return;
38296             }
38297                 
38298             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38299                 match = this.store.indexOf(v);
38300                 return false;
38301             }
38302         }, this);
38303         
38304         if (match === false) {
38305             return true; // no more action?
38306         }
38307         // scroll to?
38308         this.view.select(match);
38309         var sn = Roo.get(this.view.getSelectedNodes()[0])
38310         sn.scrollIntoView(sn.dom.parentNode, false);
38311     }
38312
38313     /** 
38314     * @cfg {Boolean} grow 
38315     * @hide 
38316     */
38317     /** 
38318     * @cfg {Number} growMin 
38319     * @hide 
38320     */
38321     /** 
38322     * @cfg {Number} growMax 
38323     * @hide 
38324     */
38325     /**
38326      * @hide
38327      * @method autoSize
38328      */
38329 });/*
38330  * Based on:
38331  * Ext JS Library 1.1.1
38332  * Copyright(c) 2006-2007, Ext JS, LLC.
38333  *
38334  * Originally Released Under LGPL - original licence link has changed is not relivant.
38335  *
38336  * Fork - LGPL
38337  * <script type="text/javascript">
38338  */
38339 /**
38340  * @class Roo.form.Checkbox
38341  * @extends Roo.form.Field
38342  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38343  * @constructor
38344  * Creates a new Checkbox
38345  * @param {Object} config Configuration options
38346  */
38347 Roo.form.Checkbox = function(config){
38348     Roo.form.Checkbox.superclass.constructor.call(this, config);
38349     this.addEvents({
38350         /**
38351          * @event check
38352          * Fires when the checkbox is checked or unchecked.
38353              * @param {Roo.form.Checkbox} this This checkbox
38354              * @param {Boolean} checked The new checked value
38355              */
38356         check : true
38357     });
38358 };
38359
38360 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38361     /**
38362      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38363      */
38364     focusClass : undefined,
38365     /**
38366      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38367      */
38368     fieldClass: "x-form-field",
38369     /**
38370      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38371      */
38372     checked: false,
38373     /**
38374      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38375      * {tag: "input", type: "checkbox", autocomplete: "off"})
38376      */
38377     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38378     /**
38379      * @cfg {String} boxLabel The text that appears beside the checkbox
38380      */
38381     boxLabel : "",
38382     /**
38383      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38384      */  
38385     inputValue : '1',
38386     /**
38387      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38388      */
38389      valueOff: '0', // value when not checked..
38390
38391     actionMode : 'viewEl', 
38392     //
38393     // private
38394     itemCls : 'x-menu-check-item x-form-item',
38395     groupClass : 'x-menu-group-item',
38396     inputType : 'hidden',
38397     
38398     
38399     inSetChecked: false, // check that we are not calling self...
38400     
38401     inputElement: false, // real input element?
38402     basedOn: false, // ????
38403     
38404     isFormField: true, // not sure where this is needed!!!!
38405
38406     onResize : function(){
38407         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38408         if(!this.boxLabel){
38409             this.el.alignTo(this.wrap, 'c-c');
38410         }
38411     },
38412
38413     initEvents : function(){
38414         Roo.form.Checkbox.superclass.initEvents.call(this);
38415         this.el.on("click", this.onClick,  this);
38416         this.el.on("change", this.onClick,  this);
38417     },
38418
38419
38420     getResizeEl : function(){
38421         return this.wrap;
38422     },
38423
38424     getPositionEl : function(){
38425         return this.wrap;
38426     },
38427
38428     // private
38429     onRender : function(ct, position){
38430         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38431         /*
38432         if(this.inputValue !== undefined){
38433             this.el.dom.value = this.inputValue;
38434         }
38435         */
38436         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38437         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38438         var viewEl = this.wrap.createChild({ 
38439             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38440         this.viewEl = viewEl;   
38441         this.wrap.on('click', this.onClick,  this); 
38442         
38443         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38444         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38445         
38446         
38447         
38448         if(this.boxLabel){
38449             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38450         //    viewEl.on('click', this.onClick,  this); 
38451         }
38452         //if(this.checked){
38453             this.setChecked(this.checked);
38454         //}else{
38455             //this.checked = this.el.dom;
38456         //}
38457
38458     },
38459
38460     // private
38461     initValue : Roo.emptyFn,
38462
38463     /**
38464      * Returns the checked state of the checkbox.
38465      * @return {Boolean} True if checked, else false
38466      */
38467     getValue : function(){
38468         if(this.el){
38469             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38470         }
38471         return this.valueOff;
38472         
38473     },
38474
38475         // private
38476     onClick : function(){ 
38477         this.setChecked(!this.checked);
38478
38479         //if(this.el.dom.checked != this.checked){
38480         //    this.setValue(this.el.dom.checked);
38481        // }
38482     },
38483
38484     /**
38485      * Sets the checked state of the checkbox.
38486      * On is always based on a string comparison between inputValue and the param.
38487      * @param {Boolean/String} value - the value to set 
38488      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38489      */
38490     setValue : function(v,suppressEvent){
38491         
38492         
38493         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38494         //if(this.el && this.el.dom){
38495         //    this.el.dom.checked = this.checked;
38496         //    this.el.dom.defaultChecked = this.checked;
38497         //}
38498         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38499         //this.fireEvent("check", this, this.checked);
38500     },
38501     // private..
38502     setChecked : function(state,suppressEvent)
38503     {
38504         if (this.inSetChecked) {
38505             this.checked = state;
38506             return;
38507         }
38508         
38509     
38510         if(this.wrap){
38511             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38512         }
38513         this.checked = state;
38514         if(suppressEvent !== true){
38515             this.fireEvent('check', this, state);
38516         }
38517         this.inSetChecked = true;
38518         this.el.dom.value = state ? this.inputValue : this.valueOff;
38519         this.inSetChecked = false;
38520         
38521     },
38522     // handle setting of hidden value by some other method!!?!?
38523     setFromHidden: function()
38524     {
38525         if(!this.el){
38526             return;
38527         }
38528         //console.log("SET FROM HIDDEN");
38529         //alert('setFrom hidden');
38530         this.setValue(this.el.dom.value);
38531     },
38532     
38533     onDestroy : function()
38534     {
38535         if(this.viewEl){
38536             Roo.get(this.viewEl).remove();
38537         }
38538          
38539         Roo.form.Checkbox.superclass.onDestroy.call(this);
38540     }
38541
38542 });/*
38543  * Based on:
38544  * Ext JS Library 1.1.1
38545  * Copyright(c) 2006-2007, Ext JS, LLC.
38546  *
38547  * Originally Released Under LGPL - original licence link has changed is not relivant.
38548  *
38549  * Fork - LGPL
38550  * <script type="text/javascript">
38551  */
38552  
38553 /**
38554  * @class Roo.form.Radio
38555  * @extends Roo.form.Checkbox
38556  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38557  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38558  * @constructor
38559  * Creates a new Radio
38560  * @param {Object} config Configuration options
38561  */
38562 Roo.form.Radio = function(){
38563     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38564 };
38565 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38566     inputType: 'radio',
38567
38568     /**
38569      * If this radio is part of a group, it will return the selected value
38570      * @return {String}
38571      */
38572     getGroupValue : function(){
38573         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38574     }
38575 });//<script type="text/javascript">
38576
38577 /*
38578  * Ext JS Library 1.1.1
38579  * Copyright(c) 2006-2007, Ext JS, LLC.
38580  * licensing@extjs.com
38581  * 
38582  * http://www.extjs.com/license
38583  */
38584  
38585  /*
38586   * 
38587   * Known bugs:
38588   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38589   * - IE ? - no idea how much works there.
38590   * 
38591   * 
38592   * 
38593   */
38594  
38595
38596 /**
38597  * @class Ext.form.HtmlEditor
38598  * @extends Ext.form.Field
38599  * Provides a lightweight HTML Editor component.
38600  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38601  * 
38602  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38603  * supported by this editor.</b><br/><br/>
38604  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38605  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38606  */
38607 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38608       /**
38609      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38610      */
38611     toolbars : false,
38612     /**
38613      * @cfg {String} createLinkText The default text for the create link prompt
38614      */
38615     createLinkText : 'Please enter the URL for the link:',
38616     /**
38617      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38618      */
38619     defaultLinkValue : 'http:/'+'/',
38620    
38621      /**
38622      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38623      *                        Roo.resizable.
38624      */
38625     resizable : false,
38626      /**
38627      * @cfg {Number} height (in pixels)
38628      */   
38629     height: 300,
38630    /**
38631      * @cfg {Number} width (in pixels)
38632      */   
38633     width: 500,
38634     
38635     /**
38636      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38637      * 
38638      */
38639     stylesheets: false,
38640     
38641     // id of frame..
38642     frameId: false,
38643     
38644     // private properties
38645     validationEvent : false,
38646     deferHeight: true,
38647     initialized : false,
38648     activated : false,
38649     sourceEditMode : false,
38650     onFocus : Roo.emptyFn,
38651     iframePad:3,
38652     hideMode:'offsets',
38653     
38654     defaultAutoCreate : { // modified by initCompnoent..
38655         tag: "textarea",
38656         style:"width:500px;height:300px;",
38657         autocomplete: "off"
38658     },
38659
38660     // private
38661     initComponent : function(){
38662         this.addEvents({
38663             /**
38664              * @event initialize
38665              * Fires when the editor is fully initialized (including the iframe)
38666              * @param {HtmlEditor} this
38667              */
38668             initialize: true,
38669             /**
38670              * @event activate
38671              * Fires when the editor is first receives the focus. Any insertion must wait
38672              * until after this event.
38673              * @param {HtmlEditor} this
38674              */
38675             activate: true,
38676              /**
38677              * @event beforesync
38678              * Fires before the textarea is updated with content from the editor iframe. Return false
38679              * to cancel the sync.
38680              * @param {HtmlEditor} this
38681              * @param {String} html
38682              */
38683             beforesync: true,
38684              /**
38685              * @event beforepush
38686              * Fires before the iframe editor is updated with content from the textarea. Return false
38687              * to cancel the push.
38688              * @param {HtmlEditor} this
38689              * @param {String} html
38690              */
38691             beforepush: true,
38692              /**
38693              * @event sync
38694              * Fires when the textarea is updated with content from the editor iframe.
38695              * @param {HtmlEditor} this
38696              * @param {String} html
38697              */
38698             sync: true,
38699              /**
38700              * @event push
38701              * Fires when the iframe editor is updated with content from the textarea.
38702              * @param {HtmlEditor} this
38703              * @param {String} html
38704              */
38705             push: true,
38706              /**
38707              * @event editmodechange
38708              * Fires when the editor switches edit modes
38709              * @param {HtmlEditor} this
38710              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38711              */
38712             editmodechange: true,
38713             /**
38714              * @event editorevent
38715              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38716              * @param {HtmlEditor} this
38717              */
38718             editorevent: true
38719         });
38720         this.defaultAutoCreate =  {
38721             tag: "textarea",
38722             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38723             autocomplete: "off"
38724         };
38725     },
38726
38727     /**
38728      * Protected method that will not generally be called directly. It
38729      * is called when the editor creates its toolbar. Override this method if you need to
38730      * add custom toolbar buttons.
38731      * @param {HtmlEditor} editor
38732      */
38733     createToolbar : function(editor){
38734         if (!editor.toolbars || !editor.toolbars.length) {
38735             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38736         }
38737         
38738         for (var i =0 ; i < editor.toolbars.length;i++) {
38739             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38740             editor.toolbars[i].init(editor);
38741         }
38742          
38743         
38744     },
38745
38746     /**
38747      * Protected method that will not generally be called directly. It
38748      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38749      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38750      */
38751     getDocMarkup : function(){
38752         // body styles..
38753         var st = '';
38754         if (this.stylesheets === false) {
38755             
38756             Roo.get(document.head).select('style').each(function(node) {
38757                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38758             });
38759             
38760             Roo.get(document.head).select('link').each(function(node) { 
38761                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38762             });
38763             
38764         } else if (!this.stylesheets.length) {
38765                 // simple..
38766                 st = '<style type="text/css">' +
38767                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38768                    '</style>';
38769         } else {
38770             Roo.each(this.stylesheets, function(s) {
38771                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38772             });
38773             
38774         }
38775         
38776         return '<html><head>' + st  +
38777             //<style type="text/css">' +
38778             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38779             //'</style>' +
38780             ' </head><body></body></html>';
38781     },
38782
38783     // private
38784     onRender : function(ct, position)
38785     {
38786         var _t = this;
38787         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38788         this.el.dom.style.border = '0 none';
38789         this.el.dom.setAttribute('tabIndex', -1);
38790         this.el.addClass('x-hidden');
38791         if(Roo.isIE){ // fix IE 1px bogus margin
38792             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38793         }
38794         this.wrap = this.el.wrap({
38795             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38796         });
38797         
38798         if (this.resizable) {
38799             this.resizeEl = new Roo.Resizable(this.wrap, {
38800                 pinned : true,
38801                 wrap: true,
38802                 dynamic : true,
38803                 minHeight : this.height,
38804                 height: this.height,
38805                 handles : this.resizable,
38806                 width: this.width,
38807                 listeners : {
38808                     resize : function(r, w, h) {
38809                         _t.onResize(w,h); // -something
38810                     }
38811                 }
38812             });
38813             
38814         }
38815
38816         this.frameId = Roo.id();
38817         
38818         this.createToolbar(this);
38819         
38820       
38821         
38822         var iframe = this.wrap.createChild({
38823             tag: 'iframe',
38824             id: this.frameId,
38825             name: this.frameId,
38826             frameBorder : 'no',
38827             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38828         }, this.el
38829         );
38830         
38831        // console.log(iframe);
38832         //this.wrap.dom.appendChild(iframe);
38833
38834         this.iframe = iframe.dom;
38835
38836          this.assignDocWin();
38837         
38838         this.doc.designMode = 'on';
38839        
38840         this.doc.open();
38841         this.doc.write(this.getDocMarkup());
38842         this.doc.close();
38843
38844         
38845         var task = { // must defer to wait for browser to be ready
38846             run : function(){
38847                 //console.log("run task?" + this.doc.readyState);
38848                 this.assignDocWin();
38849                 if(this.doc.body || this.doc.readyState == 'complete'){
38850                     try {
38851                         this.doc.designMode="on";
38852                     } catch (e) {
38853                         return;
38854                     }
38855                     Roo.TaskMgr.stop(task);
38856                     this.initEditor.defer(10, this);
38857                 }
38858             },
38859             interval : 10,
38860             duration:10000,
38861             scope: this
38862         };
38863         Roo.TaskMgr.start(task);
38864
38865         if(!this.width){
38866             this.setSize(this.wrap.getSize());
38867         }
38868         if (this.resizeEl) {
38869             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38870             // should trigger onReize..
38871         }
38872     },
38873
38874     // private
38875     onResize : function(w, h)
38876     {
38877         //Roo.log('resize: ' +w + ',' + h );
38878         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38879         if(this.el && this.iframe){
38880             if(typeof w == 'number'){
38881                 var aw = w - this.wrap.getFrameWidth('lr');
38882                 this.el.setWidth(this.adjustWidth('textarea', aw));
38883                 this.iframe.style.width = aw + 'px';
38884             }
38885             if(typeof h == 'number'){
38886                 var tbh = 0;
38887                 for (var i =0; i < this.toolbars.length;i++) {
38888                     // fixme - ask toolbars for heights?
38889                     tbh += this.toolbars[i].tb.el.getHeight();
38890                     if (this.toolbars[i].footer) {
38891                         tbh += this.toolbars[i].footer.el.getHeight();
38892                     }
38893                 }
38894                 
38895                 
38896                 
38897                 
38898                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38899                 ah -= 5; // knock a few pixes off for look..
38900                 this.el.setHeight(this.adjustWidth('textarea', ah));
38901                 this.iframe.style.height = ah + 'px';
38902                 if(this.doc){
38903                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38904                 }
38905             }
38906         }
38907     },
38908
38909     /**
38910      * Toggles the editor between standard and source edit mode.
38911      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38912      */
38913     toggleSourceEdit : function(sourceEditMode){
38914         
38915         this.sourceEditMode = sourceEditMode === true;
38916         
38917         if(this.sourceEditMode){
38918           
38919             this.syncValue();
38920             this.iframe.className = 'x-hidden';
38921             this.el.removeClass('x-hidden');
38922             this.el.dom.removeAttribute('tabIndex');
38923             this.el.focus();
38924         }else{
38925              
38926             this.pushValue();
38927             this.iframe.className = '';
38928             this.el.addClass('x-hidden');
38929             this.el.dom.setAttribute('tabIndex', -1);
38930             this.deferFocus();
38931         }
38932         this.setSize(this.wrap.getSize());
38933         this.fireEvent('editmodechange', this, this.sourceEditMode);
38934     },
38935
38936     // private used internally
38937     createLink : function(){
38938         var url = prompt(this.createLinkText, this.defaultLinkValue);
38939         if(url && url != 'http:/'+'/'){
38940             this.relayCmd('createlink', url);
38941         }
38942     },
38943
38944     // private (for BoxComponent)
38945     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38946
38947     // private (for BoxComponent)
38948     getResizeEl : function(){
38949         return this.wrap;
38950     },
38951
38952     // private (for BoxComponent)
38953     getPositionEl : function(){
38954         return this.wrap;
38955     },
38956
38957     // private
38958     initEvents : function(){
38959         this.originalValue = this.getValue();
38960     },
38961
38962     /**
38963      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38964      * @method
38965      */
38966     markInvalid : Roo.emptyFn,
38967     /**
38968      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38969      * @method
38970      */
38971     clearInvalid : Roo.emptyFn,
38972
38973     setValue : function(v){
38974         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38975         this.pushValue();
38976     },
38977
38978     /**
38979      * Protected method that will not generally be called directly. If you need/want
38980      * custom HTML cleanup, this is the method you should override.
38981      * @param {String} html The HTML to be cleaned
38982      * return {String} The cleaned HTML
38983      */
38984     cleanHtml : function(html){
38985         html = String(html);
38986         if(html.length > 5){
38987             if(Roo.isSafari){ // strip safari nonsense
38988                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38989             }
38990         }
38991         if(html == '&nbsp;'){
38992             html = '';
38993         }
38994         return html;
38995     },
38996
38997     /**
38998      * Protected method that will not generally be called directly. Syncs the contents
38999      * of the editor iframe with the textarea.
39000      */
39001     syncValue : function(){
39002         if(this.initialized){
39003             var bd = (this.doc.body || this.doc.documentElement);
39004             this.cleanUpPaste();
39005             var html = bd.innerHTML;
39006             if(Roo.isSafari){
39007                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39008                 var m = bs.match(/text-align:(.*?);/i);
39009                 if(m && m[1]){
39010                     html = '<div style="'+m[0]+'">' + html + '</div>';
39011                 }
39012             }
39013             html = this.cleanHtml(html);
39014             if(this.fireEvent('beforesync', this, html) !== false){
39015                 this.el.dom.value = html;
39016                 this.fireEvent('sync', this, html);
39017             }
39018         }
39019     },
39020
39021     /**
39022      * Protected method that will not generally be called directly. Pushes the value of the textarea
39023      * into the iframe editor.
39024      */
39025     pushValue : function(){
39026         if(this.initialized){
39027             var v = this.el.dom.value;
39028             if(v.length < 1){
39029                 v = '&#160;';
39030             }
39031             
39032             if(this.fireEvent('beforepush', this, v) !== false){
39033                 var d = (this.doc.body || this.doc.documentElement);
39034                 d.innerHTML = v;
39035                 this.cleanUpPaste();
39036                 this.el.dom.value = d.innerHTML;
39037                 this.fireEvent('push', this, v);
39038             }
39039         }
39040     },
39041
39042     // private
39043     deferFocus : function(){
39044         this.focus.defer(10, this);
39045     },
39046
39047     // doc'ed in Field
39048     focus : function(){
39049         if(this.win && !this.sourceEditMode){
39050             this.win.focus();
39051         }else{
39052             this.el.focus();
39053         }
39054     },
39055     
39056     assignDocWin: function()
39057     {
39058         var iframe = this.iframe;
39059         
39060          if(Roo.isIE){
39061             this.doc = iframe.contentWindow.document;
39062             this.win = iframe.contentWindow;
39063         } else {
39064             if (!Roo.get(this.frameId)) {
39065                 return;
39066             }
39067             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39068             this.win = Roo.get(this.frameId).dom.contentWindow;
39069         }
39070     },
39071     
39072     // private
39073     initEditor : function(){
39074         //console.log("INIT EDITOR");
39075         this.assignDocWin();
39076         
39077         
39078         
39079         this.doc.designMode="on";
39080         this.doc.open();
39081         this.doc.write(this.getDocMarkup());
39082         this.doc.close();
39083         
39084         var dbody = (this.doc.body || this.doc.documentElement);
39085         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39086         // this copies styles from the containing element into thsi one..
39087         // not sure why we need all of this..
39088         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39089         ss['background-attachment'] = 'fixed'; // w3c
39090         dbody.bgProperties = 'fixed'; // ie
39091         Roo.DomHelper.applyStyles(dbody, ss);
39092         Roo.EventManager.on(this.doc, {
39093             //'mousedown': this.onEditorEvent,
39094             'mouseup': this.onEditorEvent,
39095             'dblclick': this.onEditorEvent,
39096             'click': this.onEditorEvent,
39097             'keyup': this.onEditorEvent,
39098             buffer:100,
39099             scope: this
39100         });
39101         if(Roo.isGecko){
39102             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39103         }
39104         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39105             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39106         }
39107         this.initialized = true;
39108
39109         this.fireEvent('initialize', this);
39110         this.pushValue();
39111     },
39112
39113     // private
39114     onDestroy : function(){
39115         
39116         
39117         
39118         if(this.rendered){
39119             
39120             for (var i =0; i < this.toolbars.length;i++) {
39121                 // fixme - ask toolbars for heights?
39122                 this.toolbars[i].onDestroy();
39123             }
39124             
39125             this.wrap.dom.innerHTML = '';
39126             this.wrap.remove();
39127         }
39128     },
39129
39130     // private
39131     onFirstFocus : function(){
39132         
39133         this.assignDocWin();
39134         
39135         
39136         this.activated = true;
39137         for (var i =0; i < this.toolbars.length;i++) {
39138             this.toolbars[i].onFirstFocus();
39139         }
39140        
39141         if(Roo.isGecko){ // prevent silly gecko errors
39142             this.win.focus();
39143             var s = this.win.getSelection();
39144             if(!s.focusNode || s.focusNode.nodeType != 3){
39145                 var r = s.getRangeAt(0);
39146                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39147                 r.collapse(true);
39148                 this.deferFocus();
39149             }
39150             try{
39151                 this.execCmd('useCSS', true);
39152                 this.execCmd('styleWithCSS', false);
39153             }catch(e){}
39154         }
39155         this.fireEvent('activate', this);
39156     },
39157
39158     // private
39159     adjustFont: function(btn){
39160         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39161         //if(Roo.isSafari){ // safari
39162         //    adjust *= 2;
39163        // }
39164         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39165         if(Roo.isSafari){ // safari
39166             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39167             v =  (v < 10) ? 10 : v;
39168             v =  (v > 48) ? 48 : v;
39169             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39170             
39171         }
39172         
39173         
39174         v = Math.max(1, v+adjust);
39175         
39176         this.execCmd('FontSize', v  );
39177     },
39178
39179     onEditorEvent : function(e){
39180         this.fireEvent('editorevent', this, e);
39181       //  this.updateToolbar();
39182         this.syncValue();
39183     },
39184
39185     insertTag : function(tg)
39186     {
39187         // could be a bit smarter... -> wrap the current selected tRoo..
39188         
39189         this.execCmd("formatblock",   tg);
39190         
39191     },
39192     
39193     insertText : function(txt)
39194     {
39195         
39196         
39197         range = this.createRange();
39198         range.deleteContents();
39199                //alert(Sender.getAttribute('label'));
39200                
39201         range.insertNode(this.doc.createTextNode(txt));
39202     } ,
39203     
39204     // private
39205     relayBtnCmd : function(btn){
39206         this.relayCmd(btn.cmd);
39207     },
39208
39209     /**
39210      * Executes a Midas editor command on the editor document and performs necessary focus and
39211      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39212      * @param {String} cmd The Midas command
39213      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39214      */
39215     relayCmd : function(cmd, value){
39216         this.win.focus();
39217         this.execCmd(cmd, value);
39218         this.fireEvent('editorevent', this);
39219         //this.updateToolbar();
39220         this.deferFocus();
39221     },
39222
39223     /**
39224      * Executes a Midas editor command directly on the editor document.
39225      * For visual commands, you should use {@link #relayCmd} instead.
39226      * <b>This should only be called after the editor is initialized.</b>
39227      * @param {String} cmd The Midas command
39228      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39229      */
39230     execCmd : function(cmd, value){
39231         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39232         this.syncValue();
39233     },
39234
39235    
39236     /**
39237      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39238      * to insert tRoo.
39239      * @param {String} text
39240      */
39241     insertAtCursor : function(text){
39242         if(!this.activated){
39243             return;
39244         }
39245         if(Roo.isIE){
39246             this.win.focus();
39247             var r = this.doc.selection.createRange();
39248             if(r){
39249                 r.collapse(true);
39250                 r.pasteHTML(text);
39251                 this.syncValue();
39252                 this.deferFocus();
39253             }
39254         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39255             this.win.focus();
39256             this.execCmd('InsertHTML', text);
39257             this.deferFocus();
39258         }
39259     },
39260  // private
39261     mozKeyPress : function(e){
39262         if(e.ctrlKey){
39263             var c = e.getCharCode(), cmd;
39264           
39265             if(c > 0){
39266                 c = String.fromCharCode(c).toLowerCase();
39267                 switch(c){
39268                     case 'b':
39269                         cmd = 'bold';
39270                     break;
39271                     case 'i':
39272                         cmd = 'italic';
39273                     break;
39274                     case 'u':
39275                         cmd = 'underline';
39276                     case 'v':
39277                         this.cleanUpPaste.defer(100, this);
39278                         return;
39279                     break;
39280                 }
39281                 if(cmd){
39282                     this.win.focus();
39283                     this.execCmd(cmd);
39284                     this.deferFocus();
39285                     e.preventDefault();
39286                 }
39287                 
39288             }
39289         }
39290     },
39291
39292     // private
39293     fixKeys : function(){ // load time branching for fastest keydown performance
39294         if(Roo.isIE){
39295             return function(e){
39296                 var k = e.getKey(), r;
39297                 if(k == e.TAB){
39298                     e.stopEvent();
39299                     r = this.doc.selection.createRange();
39300                     if(r){
39301                         r.collapse(true);
39302                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39303                         this.deferFocus();
39304                     }
39305                     return;
39306                 }
39307                 
39308                 if(k == e.ENTER){
39309                     r = this.doc.selection.createRange();
39310                     if(r){
39311                         var target = r.parentElement();
39312                         if(!target || target.tagName.toLowerCase() != 'li'){
39313                             e.stopEvent();
39314                             r.pasteHTML('<br />');
39315                             r.collapse(false);
39316                             r.select();
39317                         }
39318                     }
39319                 }
39320                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39321                     this.cleanUpPaste.defer(100, this);
39322                     return;
39323                 }
39324                 
39325                 
39326             };
39327         }else if(Roo.isOpera){
39328             return function(e){
39329                 var k = e.getKey();
39330                 if(k == e.TAB){
39331                     e.stopEvent();
39332                     this.win.focus();
39333                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39334                     this.deferFocus();
39335                 }
39336                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39337                     this.cleanUpPaste.defer(100, this);
39338                     return;
39339                 }
39340                 
39341             };
39342         }else if(Roo.isSafari){
39343             return function(e){
39344                 var k = e.getKey();
39345                 
39346                 if(k == e.TAB){
39347                     e.stopEvent();
39348                     this.execCmd('InsertText','\t');
39349                     this.deferFocus();
39350                     return;
39351                 }
39352                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39353                     this.cleanUpPaste.defer(100, this);
39354                     return;
39355                 }
39356                 
39357              };
39358         }
39359     }(),
39360     
39361     getAllAncestors: function()
39362     {
39363         var p = this.getSelectedNode();
39364         var a = [];
39365         if (!p) {
39366             a.push(p); // push blank onto stack..
39367             p = this.getParentElement();
39368         }
39369         
39370         
39371         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39372             a.push(p);
39373             p = p.parentNode;
39374         }
39375         a.push(this.doc.body);
39376         return a;
39377     },
39378     lastSel : false,
39379     lastSelNode : false,
39380     
39381     
39382     getSelection : function() 
39383     {
39384         this.assignDocWin();
39385         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39386     },
39387     
39388     getSelectedNode: function() 
39389     {
39390         // this may only work on Gecko!!!
39391         
39392         // should we cache this!!!!
39393         
39394         
39395         
39396          
39397         var range = this.createRange(this.getSelection()).cloneRange();
39398         
39399         if (Roo.isIE) {
39400             var parent = range.parentElement();
39401             while (true) {
39402                 var testRange = range.duplicate();
39403                 testRange.moveToElementText(parent);
39404                 if (testRange.inRange(range)) {
39405                     break;
39406                 }
39407                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39408                     break;
39409                 }
39410                 parent = parent.parentElement;
39411             }
39412             return parent;
39413         }
39414         
39415         // is ancestor a text element.
39416         var ac =  range.commonAncestorContainer;
39417         if (ac.nodeType == 3) {
39418             ac = ac.parentNode;
39419         }
39420         
39421         var ar = ac.childNodes;
39422          
39423         var nodes = [];
39424         var other_nodes = [];
39425         var has_other_nodes = false;
39426         for (var i=0;i<ar.length;i++) {
39427             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39428                 continue;
39429             }
39430             // fullly contained node.
39431             
39432             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39433                 nodes.push(ar[i]);
39434                 continue;
39435             }
39436             
39437             // probably selected..
39438             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39439                 other_nodes.push(ar[i]);
39440                 continue;
39441             }
39442             // outer..
39443             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39444                 continue;
39445             }
39446             
39447             
39448             has_other_nodes = true;
39449         }
39450         if (!nodes.length && other_nodes.length) {
39451             nodes= other_nodes;
39452         }
39453         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39454             return false;
39455         }
39456         
39457         return nodes[0];
39458     },
39459     createRange: function(sel)
39460     {
39461         // this has strange effects when using with 
39462         // top toolbar - not sure if it's a great idea.
39463         //this.editor.contentWindow.focus();
39464         if (typeof sel != "undefined") {
39465             try {
39466                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39467             } catch(e) {
39468                 return this.doc.createRange();
39469             }
39470         } else {
39471             return this.doc.createRange();
39472         }
39473     },
39474     getParentElement: function()
39475     {
39476         
39477         this.assignDocWin();
39478         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39479         
39480         var range = this.createRange(sel);
39481          
39482         try {
39483             var p = range.commonAncestorContainer;
39484             while (p.nodeType == 3) { // text node
39485                 p = p.parentNode;
39486             }
39487             return p;
39488         } catch (e) {
39489             return null;
39490         }
39491     
39492     },
39493     /***
39494      *
39495      * Range intersection.. the hard stuff...
39496      *  '-1' = before
39497      *  '0' = hits..
39498      *  '1' = after.
39499      *         [ -- selected range --- ]
39500      *   [fail]                        [fail]
39501      *
39502      *    basically..
39503      *      if end is before start or  hits it. fail.
39504      *      if start is after end or hits it fail.
39505      *
39506      *   if either hits (but other is outside. - then it's not 
39507      *   
39508      *    
39509      **/
39510     
39511     
39512     // @see http://www.thismuchiknow.co.uk/?p=64.
39513     rangeIntersectsNode : function(range, node)
39514     {
39515         var nodeRange = node.ownerDocument.createRange();
39516         try {
39517             nodeRange.selectNode(node);
39518         } catch (e) {
39519             nodeRange.selectNodeContents(node);
39520         }
39521     
39522         var rangeStartRange = range.cloneRange();
39523         rangeStartRange.collapse(true);
39524     
39525         var rangeEndRange = range.cloneRange();
39526         rangeEndRange.collapse(false);
39527     
39528         var nodeStartRange = nodeRange.cloneRange();
39529         nodeStartRange.collapse(true);
39530     
39531         var nodeEndRange = nodeRange.cloneRange();
39532         nodeEndRange.collapse(false);
39533     
39534         return rangeStartRange.compareBoundaryPoints(
39535                  Range.START_TO_START, nodeEndRange) == -1 &&
39536                rangeEndRange.compareBoundaryPoints(
39537                  Range.START_TO_START, nodeStartRange) == 1;
39538         
39539          
39540     },
39541     rangeCompareNode : function(range, node)
39542     {
39543         var nodeRange = node.ownerDocument.createRange();
39544         try {
39545             nodeRange.selectNode(node);
39546         } catch (e) {
39547             nodeRange.selectNodeContents(node);
39548         }
39549         
39550         
39551         range.collapse(true);
39552     
39553         nodeRange.collapse(true);
39554      
39555         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39556         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39557          
39558         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39559         
39560         var nodeIsBefore   =  ss == 1;
39561         var nodeIsAfter    = ee == -1;
39562         
39563         if (nodeIsBefore && nodeIsAfter)
39564             return 0; // outer
39565         if (!nodeIsBefore && nodeIsAfter)
39566             return 1; //right trailed.
39567         
39568         if (nodeIsBefore && !nodeIsAfter)
39569             return 2;  // left trailed.
39570         // fully contined.
39571         return 3;
39572     },
39573
39574     // private? - in a new class?
39575     cleanUpPaste :  function()
39576     {
39577         // cleans up the whole document..
39578       //  console.log('cleanuppaste');
39579         this.cleanUpChildren(this.doc.body);
39580         
39581         
39582     },
39583     cleanUpChildren : function (n)
39584     {
39585         if (!n.childNodes.length) {
39586             return;
39587         }
39588         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39589            this.cleanUpChild(n.childNodes[i]);
39590         }
39591     },
39592     
39593     
39594         
39595     
39596     cleanUpChild : function (node)
39597     {
39598         //console.log(node);
39599         if (node.nodeName == "#text") {
39600             // clean up silly Windows -- stuff?
39601             return; 
39602         }
39603         if (node.nodeName == "#comment") {
39604             node.parentNode.removeChild(node);
39605             // clean up silly Windows -- stuff?
39606             return; 
39607         }
39608         
39609         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39610             // remove node.
39611             node.parentNode.removeChild(node);
39612             return;
39613             
39614         }
39615         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39616             this.cleanUpChildren(node);
39617             // inserts everything just before this node...
39618             while (node.childNodes.length) {
39619                 var cn = node.childNodes[0];
39620                 node.removeChild(cn);
39621                 node.parentNode.insertBefore(cn, node);
39622             }
39623             node.parentNode.removeChild(node);
39624             return;
39625         }
39626         
39627         if (!node.attributes || !node.attributes.length) {
39628             this.cleanUpChildren(node);
39629             return;
39630         }
39631         
39632         function cleanAttr(n,v)
39633         {
39634             
39635             if (v.match(/^\./) || v.match(/^\//)) {
39636                 return;
39637             }
39638             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39639                 return;
39640             }
39641             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39642             node.removeAttribute(n);
39643             
39644         }
39645         
39646         function cleanStyle(n,v)
39647         {
39648             if (v.match(/expression/)) { //XSS?? should we even bother..
39649                 node.removeAttribute(n);
39650                 return;
39651             }
39652             
39653             
39654             var parts = v.split(/;/);
39655             Roo.each(parts, function(p) {
39656                 p = p.replace(/\s+/g,'');
39657                 if (!p.length) {
39658                     return true;
39659                 }
39660                 var l = p.split(':').shift().replace(/\s+/g,'');
39661                 
39662                 // only allow 'c whitelisted system attributes'
39663                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39664                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39665                     node.removeAttribute(n);
39666                     return false;
39667                 }
39668                 return true;
39669             });
39670             
39671             
39672         }
39673         
39674         
39675         for (var i = node.attributes.length-1; i > -1 ; i--) {
39676             var a = node.attributes[i];
39677             //console.log(a);
39678             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39679                 node.removeAttribute(a.name);
39680                 return;
39681             }
39682             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39683                 cleanAttr(a.name,a.value); // fixme..
39684                 return;
39685             }
39686             if (a.name == 'style') {
39687                 cleanStyle(a.name,a.value);
39688             }
39689             /// clean up MS crap..
39690             if (a.name == 'class') {
39691                 if (a.value.match(/^Mso/)) {
39692                     node.className = '';
39693                 }
39694             }
39695             
39696             // style cleanup!?
39697             // class cleanup?
39698             
39699         }
39700         
39701         
39702         this.cleanUpChildren(node);
39703         
39704         
39705     }
39706     
39707     
39708     // hide stuff that is not compatible
39709     /**
39710      * @event blur
39711      * @hide
39712      */
39713     /**
39714      * @event change
39715      * @hide
39716      */
39717     /**
39718      * @event focus
39719      * @hide
39720      */
39721     /**
39722      * @event specialkey
39723      * @hide
39724      */
39725     /**
39726      * @cfg {String} fieldClass @hide
39727      */
39728     /**
39729      * @cfg {String} focusClass @hide
39730      */
39731     /**
39732      * @cfg {String} autoCreate @hide
39733      */
39734     /**
39735      * @cfg {String} inputType @hide
39736      */
39737     /**
39738      * @cfg {String} invalidClass @hide
39739      */
39740     /**
39741      * @cfg {String} invalidText @hide
39742      */
39743     /**
39744      * @cfg {String} msgFx @hide
39745      */
39746     /**
39747      * @cfg {String} validateOnBlur @hide
39748      */
39749 });
39750
39751 Roo.form.HtmlEditor.white = [
39752         'area', 'br', 'img', 'input', 'hr', 'wbr',
39753         
39754        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39755        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39756        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39757        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39758        'table',   'ul',         'xmp', 
39759        
39760        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39761       'thead',   'tr', 
39762      
39763       'dir', 'menu', 'ol', 'ul', 'dl',
39764        
39765       'embed',  'object'
39766 ];
39767
39768
39769 Roo.form.HtmlEditor.black = [
39770     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39771         'applet', // 
39772         'base',   'basefont', 'bgsound', 'blink',  'body', 
39773         'frame',  'frameset', 'head',    'html',   'ilayer', 
39774         'iframe', 'layer',  'link',     'meta',    'object',   
39775         'script', 'style' ,'title',  'xml' // clean later..
39776 ];
39777 Roo.form.HtmlEditor.clean = [
39778     'script', 'style', 'title', 'xml'
39779 ];
39780 Roo.form.HtmlEditor.remove = [
39781     'font'
39782 ];
39783 // attributes..
39784
39785 Roo.form.HtmlEditor.ablack = [
39786     'on'
39787 ];
39788     
39789 Roo.form.HtmlEditor.aclean = [ 
39790     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39791 ];
39792
39793 // protocols..
39794 Roo.form.HtmlEditor.pwhite= [
39795         'http',  'https',  'mailto'
39796 ];
39797
39798 // white listed style attributes.
39799 Roo.form.HtmlEditor.cwhite= [
39800         'text-align',
39801         'font-size'
39802 ];
39803
39804 // <script type="text/javascript">
39805 /*
39806  * Based on
39807  * Ext JS Library 1.1.1
39808  * Copyright(c) 2006-2007, Ext JS, LLC.
39809  *  
39810  
39811  */
39812
39813 /**
39814  * @class Roo.form.HtmlEditorToolbar1
39815  * Basic Toolbar
39816  * 
39817  * Usage:
39818  *
39819  new Roo.form.HtmlEditor({
39820     ....
39821     toolbars : [
39822         new Roo.form.HtmlEditorToolbar1({
39823             disable : { fonts: 1 , format: 1, ..., ... , ...],
39824             btns : [ .... ]
39825         })
39826     }
39827      
39828  * 
39829  * @cfg {Object} disable List of elements to disable..
39830  * @cfg {Array} btns List of additional buttons.
39831  * 
39832  * 
39833  * NEEDS Extra CSS? 
39834  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39835  */
39836  
39837 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39838 {
39839     
39840     Roo.apply(this, config);
39841     
39842     // default disabled, based on 'good practice'..
39843     this.disable = this.disable || {};
39844     Roo.applyIf(this.disable, {
39845         fontSize : true,
39846         colors : true,
39847         specialElements : true
39848     });
39849     
39850     
39851     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39852     // dont call parent... till later.
39853 }
39854
39855 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39856     
39857     tb: false,
39858     
39859     rendered: false,
39860     
39861     editor : false,
39862     /**
39863      * @cfg {Object} disable  List of toolbar elements to disable
39864          
39865      */
39866     disable : false,
39867       /**
39868      * @cfg {Array} fontFamilies An array of available font families
39869      */
39870     fontFamilies : [
39871         'Arial',
39872         'Courier New',
39873         'Tahoma',
39874         'Times New Roman',
39875         'Verdana'
39876     ],
39877     
39878     specialChars : [
39879            "&#169;",
39880           "&#174;",     
39881           "&#8482;",    
39882           "&#163;" ,    
39883          // "&#8212;",    
39884           "&#8230;",    
39885           "&#247;" ,    
39886         //  "&#225;" ,     ?? a acute?
39887            "&#8364;"    , //Euro
39888        //   "&#8220;"    ,
39889         //  "&#8221;"    ,
39890         //  "&#8226;"    ,
39891           "&#176;"  //   , // degrees
39892
39893          // "&#233;"     , // e ecute
39894          // "&#250;"     , // u ecute?
39895     ],
39896     
39897     specialElements : [
39898         {
39899             text: "Insert Table",
39900             xtype: 'MenuItem',
39901             xns : Roo.Menu,
39902             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39903                 
39904         },
39905         {    
39906             text: "Insert Image",
39907             xtype: 'MenuItem',
39908             xns : Roo.Menu,
39909             ihtml : '<img src="about:blank"/>'
39910             
39911         }
39912         
39913          
39914     ],
39915     
39916     
39917     inputElements : [ 
39918             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39919             "input:submit", "input:button", "select", "textarea", "label" ],
39920     formats : [
39921         ["p"] ,  
39922         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39923         ["pre"],[ "code"], 
39924         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39925     ],
39926      /**
39927      * @cfg {String} defaultFont default font to use.
39928      */
39929     defaultFont: 'tahoma',
39930    
39931     fontSelect : false,
39932     
39933     
39934     formatCombo : false,
39935     
39936     init : function(editor)
39937     {
39938         this.editor = editor;
39939         
39940         
39941         var fid = editor.frameId;
39942         var etb = this;
39943         function btn(id, toggle, handler){
39944             var xid = fid + '-'+ id ;
39945             return {
39946                 id : xid,
39947                 cmd : id,
39948                 cls : 'x-btn-icon x-edit-'+id,
39949                 enableToggle:toggle !== false,
39950                 scope: editor, // was editor...
39951                 handler:handler||editor.relayBtnCmd,
39952                 clickEvent:'mousedown',
39953                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39954                 tabIndex:-1
39955             };
39956         }
39957         
39958         
39959         
39960         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39961         this.tb = tb;
39962          // stop form submits
39963         tb.el.on('click', function(e){
39964             e.preventDefault(); // what does this do?
39965         });
39966
39967         if(!this.disable.font && !Roo.isSafari){
39968             /* why no safari for fonts
39969             editor.fontSelect = tb.el.createChild({
39970                 tag:'select',
39971                 tabIndex: -1,
39972                 cls:'x-font-select',
39973                 html: editor.createFontOptions()
39974             });
39975             editor.fontSelect.on('change', function(){
39976                 var font = editor.fontSelect.dom.value;
39977                 editor.relayCmd('fontname', font);
39978                 editor.deferFocus();
39979             }, editor);
39980             tb.add(
39981                 editor.fontSelect.dom,
39982                 '-'
39983             );
39984             */
39985         };
39986         if(!this.disable.formats){
39987             this.formatCombo = new Roo.form.ComboBox({
39988                 store: new Roo.data.SimpleStore({
39989                     id : 'tag',
39990                     fields: ['tag'],
39991                     data : this.formats // from states.js
39992                 }),
39993                 blockFocus : true,
39994                 //autoCreate : {tag: "div",  size: "20"},
39995                 displayField:'tag',
39996                 typeAhead: false,
39997                 mode: 'local',
39998                 editable : false,
39999                 triggerAction: 'all',
40000                 emptyText:'Add tag',
40001                 selectOnFocus:true,
40002                 width:135,
40003                 listeners : {
40004                     'select': function(c, r, i) {
40005                         editor.insertTag(r.get('tag'));
40006                         editor.focus();
40007                     }
40008                 }
40009
40010             });
40011             tb.addField(this.formatCombo);
40012             
40013         }
40014         
40015         if(!this.disable.format){
40016             tb.add(
40017                 btn('bold'),
40018                 btn('italic'),
40019                 btn('underline')
40020             );
40021         };
40022         if(!this.disable.fontSize){
40023             tb.add(
40024                 '-',
40025                 
40026                 
40027                 btn('increasefontsize', false, editor.adjustFont),
40028                 btn('decreasefontsize', false, editor.adjustFont)
40029             );
40030         };
40031         
40032         
40033         if(!this.disable.colors){
40034             tb.add(
40035                 '-', {
40036                     id:editor.frameId +'-forecolor',
40037                     cls:'x-btn-icon x-edit-forecolor',
40038                     clickEvent:'mousedown',
40039                     tooltip: this.buttonTips['forecolor'] || undefined,
40040                     tabIndex:-1,
40041                     menu : new Roo.menu.ColorMenu({
40042                         allowReselect: true,
40043                         focus: Roo.emptyFn,
40044                         value:'000000',
40045                         plain:true,
40046                         selectHandler: function(cp, color){
40047                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40048                             editor.deferFocus();
40049                         },
40050                         scope: editor,
40051                         clickEvent:'mousedown'
40052                     })
40053                 }, {
40054                     id:editor.frameId +'backcolor',
40055                     cls:'x-btn-icon x-edit-backcolor',
40056                     clickEvent:'mousedown',
40057                     tooltip: this.buttonTips['backcolor'] || undefined,
40058                     tabIndex:-1,
40059                     menu : new Roo.menu.ColorMenu({
40060                         focus: Roo.emptyFn,
40061                         value:'FFFFFF',
40062                         plain:true,
40063                         allowReselect: true,
40064                         selectHandler: function(cp, color){
40065                             if(Roo.isGecko){
40066                                 editor.execCmd('useCSS', false);
40067                                 editor.execCmd('hilitecolor', color);
40068                                 editor.execCmd('useCSS', true);
40069                                 editor.deferFocus();
40070                             }else{
40071                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40072                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40073                                 editor.deferFocus();
40074                             }
40075                         },
40076                         scope:editor,
40077                         clickEvent:'mousedown'
40078                     })
40079                 }
40080             );
40081         };
40082         // now add all the items...
40083         
40084
40085         if(!this.disable.alignments){
40086             tb.add(
40087                 '-',
40088                 btn('justifyleft'),
40089                 btn('justifycenter'),
40090                 btn('justifyright')
40091             );
40092         };
40093
40094         //if(!Roo.isSafari){
40095             if(!this.disable.links){
40096                 tb.add(
40097                     '-',
40098                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40099                 );
40100             };
40101
40102             if(!this.disable.lists){
40103                 tb.add(
40104                     '-',
40105                     btn('insertorderedlist'),
40106                     btn('insertunorderedlist')
40107                 );
40108             }
40109             if(!this.disable.sourceEdit){
40110                 tb.add(
40111                     '-',
40112                     btn('sourceedit', true, function(btn){
40113                         this.toggleSourceEdit(btn.pressed);
40114                     })
40115                 );
40116             }
40117         //}
40118         
40119         var smenu = { };
40120         // special menu.. - needs to be tidied up..
40121         if (!this.disable.special) {
40122             smenu = {
40123                 text: "&#169;",
40124                 cls: 'x-edit-none',
40125                 
40126                 menu : {
40127                     items : []
40128                 }
40129             };
40130             for (var i =0; i < this.specialChars.length; i++) {
40131                 smenu.menu.items.push({
40132                     
40133                     html: this.specialChars[i],
40134                     handler: function(a,b) {
40135                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40136                         
40137                     },
40138                     tabIndex:-1
40139                 });
40140             }
40141             
40142             
40143             tb.add(smenu);
40144             
40145             
40146         }
40147          
40148         if (!this.disable.specialElements) {
40149             var semenu = {
40150                 text: "Other;",
40151                 cls: 'x-edit-none',
40152                 menu : {
40153                     items : []
40154                 }
40155             };
40156             for (var i =0; i < this.specialElements.length; i++) {
40157                 semenu.menu.items.push(
40158                     Roo.apply({ 
40159                         handler: function(a,b) {
40160                             editor.insertAtCursor(this.ihtml);
40161                         }
40162                     }, this.specialElements[i])
40163                 );
40164                     
40165             }
40166             
40167             tb.add(semenu);
40168             
40169             
40170         }
40171          
40172         
40173         if (this.btns) {
40174             for(var i =0; i< this.btns.length;i++) {
40175                 var b = this.btns[i];
40176                 b.cls =  'x-edit-none';
40177                 b.scope = editor;
40178                 tb.add(b);
40179             }
40180         
40181         }
40182         
40183         
40184         
40185         // disable everything...
40186         
40187         this.tb.items.each(function(item){
40188            if(item.id != editor.frameId+ '-sourceedit'){
40189                 item.disable();
40190             }
40191         });
40192         this.rendered = true;
40193         
40194         // the all the btns;
40195         editor.on('editorevent', this.updateToolbar, this);
40196         // other toolbars need to implement this..
40197         //editor.on('editmodechange', this.updateToolbar, this);
40198     },
40199     
40200     
40201     
40202     /**
40203      * Protected method that will not generally be called directly. It triggers
40204      * a toolbar update by reading the markup state of the current selection in the editor.
40205      */
40206     updateToolbar: function(){
40207
40208         if(!this.editor.activated){
40209             this.editor.onFirstFocus();
40210             return;
40211         }
40212
40213         var btns = this.tb.items.map, 
40214             doc = this.editor.doc,
40215             frameId = this.editor.frameId;
40216
40217         if(!this.disable.font && !Roo.isSafari){
40218             /*
40219             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40220             if(name != this.fontSelect.dom.value){
40221                 this.fontSelect.dom.value = name;
40222             }
40223             */
40224         }
40225         if(!this.disable.format){
40226             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40227             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40228             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40229         }
40230         if(!this.disable.alignments){
40231             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40232             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40233             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40234         }
40235         if(!Roo.isSafari && !this.disable.lists){
40236             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40237             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40238         }
40239         
40240         var ans = this.editor.getAllAncestors();
40241         if (this.formatCombo) {
40242             
40243             
40244             var store = this.formatCombo.store;
40245             this.formatCombo.setValue("");
40246             for (var i =0; i < ans.length;i++) {
40247                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40248                     // select it..
40249                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40250                     break;
40251                 }
40252             }
40253         }
40254         
40255         
40256         
40257         // hides menus... - so this cant be on a menu...
40258         Roo.menu.MenuMgr.hideAll();
40259
40260         //this.editorsyncValue();
40261     },
40262    
40263     
40264     createFontOptions : function(){
40265         var buf = [], fs = this.fontFamilies, ff, lc;
40266         for(var i = 0, len = fs.length; i< len; i++){
40267             ff = fs[i];
40268             lc = ff.toLowerCase();
40269             buf.push(
40270                 '<option value="',lc,'" style="font-family:',ff,';"',
40271                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40272                     ff,
40273                 '</option>'
40274             );
40275         }
40276         return buf.join('');
40277     },
40278     
40279     toggleSourceEdit : function(sourceEditMode){
40280         if(sourceEditMode === undefined){
40281             sourceEditMode = !this.sourceEditMode;
40282         }
40283         this.sourceEditMode = sourceEditMode === true;
40284         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40285         // just toggle the button?
40286         if(btn.pressed !== this.editor.sourceEditMode){
40287             btn.toggle(this.editor.sourceEditMode);
40288             return;
40289         }
40290         
40291         if(this.sourceEditMode){
40292             this.tb.items.each(function(item){
40293                 if(item.cmd != 'sourceedit'){
40294                     item.disable();
40295                 }
40296             });
40297           
40298         }else{
40299             if(this.initialized){
40300                 this.tb.items.each(function(item){
40301                     item.enable();
40302                 });
40303             }
40304             
40305         }
40306         // tell the editor that it's been pressed..
40307         this.editor.toggleSourceEdit(sourceEditMode);
40308        
40309     },
40310      /**
40311      * Object collection of toolbar tooltips for the buttons in the editor. The key
40312      * is the command id associated with that button and the value is a valid QuickTips object.
40313      * For example:
40314 <pre><code>
40315 {
40316     bold : {
40317         title: 'Bold (Ctrl+B)',
40318         text: 'Make the selected text bold.',
40319         cls: 'x-html-editor-tip'
40320     },
40321     italic : {
40322         title: 'Italic (Ctrl+I)',
40323         text: 'Make the selected text italic.',
40324         cls: 'x-html-editor-tip'
40325     },
40326     ...
40327 </code></pre>
40328     * @type Object
40329      */
40330     buttonTips : {
40331         bold : {
40332             title: 'Bold (Ctrl+B)',
40333             text: 'Make the selected text bold.',
40334             cls: 'x-html-editor-tip'
40335         },
40336         italic : {
40337             title: 'Italic (Ctrl+I)',
40338             text: 'Make the selected text italic.',
40339             cls: 'x-html-editor-tip'
40340         },
40341         underline : {
40342             title: 'Underline (Ctrl+U)',
40343             text: 'Underline the selected text.',
40344             cls: 'x-html-editor-tip'
40345         },
40346         increasefontsize : {
40347             title: 'Grow Text',
40348             text: 'Increase the font size.',
40349             cls: 'x-html-editor-tip'
40350         },
40351         decreasefontsize : {
40352             title: 'Shrink Text',
40353             text: 'Decrease the font size.',
40354             cls: 'x-html-editor-tip'
40355         },
40356         backcolor : {
40357             title: 'Text Highlight Color',
40358             text: 'Change the background color of the selected text.',
40359             cls: 'x-html-editor-tip'
40360         },
40361         forecolor : {
40362             title: 'Font Color',
40363             text: 'Change the color of the selected text.',
40364             cls: 'x-html-editor-tip'
40365         },
40366         justifyleft : {
40367             title: 'Align Text Left',
40368             text: 'Align text to the left.',
40369             cls: 'x-html-editor-tip'
40370         },
40371         justifycenter : {
40372             title: 'Center Text',
40373             text: 'Center text in the editor.',
40374             cls: 'x-html-editor-tip'
40375         },
40376         justifyright : {
40377             title: 'Align Text Right',
40378             text: 'Align text to the right.',
40379             cls: 'x-html-editor-tip'
40380         },
40381         insertunorderedlist : {
40382             title: 'Bullet List',
40383             text: 'Start a bulleted list.',
40384             cls: 'x-html-editor-tip'
40385         },
40386         insertorderedlist : {
40387             title: 'Numbered List',
40388             text: 'Start a numbered list.',
40389             cls: 'x-html-editor-tip'
40390         },
40391         createlink : {
40392             title: 'Hyperlink',
40393             text: 'Make the selected text a hyperlink.',
40394             cls: 'x-html-editor-tip'
40395         },
40396         sourceedit : {
40397             title: 'Source Edit',
40398             text: 'Switch to source editing mode.',
40399             cls: 'x-html-editor-tip'
40400         }
40401     },
40402     // private
40403     onDestroy : function(){
40404         if(this.rendered){
40405             
40406             this.tb.items.each(function(item){
40407                 if(item.menu){
40408                     item.menu.removeAll();
40409                     if(item.menu.el){
40410                         item.menu.el.destroy();
40411                     }
40412                 }
40413                 item.destroy();
40414             });
40415              
40416         }
40417     },
40418     onFirstFocus: function() {
40419         this.tb.items.each(function(item){
40420            item.enable();
40421         });
40422     }
40423 });
40424
40425
40426
40427
40428 // <script type="text/javascript">
40429 /*
40430  * Based on
40431  * Ext JS Library 1.1.1
40432  * Copyright(c) 2006-2007, Ext JS, LLC.
40433  *  
40434  
40435  */
40436
40437  
40438 /**
40439  * @class Roo.form.HtmlEditor.ToolbarContext
40440  * Context Toolbar
40441  * 
40442  * Usage:
40443  *
40444  new Roo.form.HtmlEditor({
40445     ....
40446     toolbars : [
40447         { xtype: 'ToolbarStandard', styles : {} }
40448         { xtype: 'ToolbarContext', disable : {} }
40449     ]
40450 })
40451
40452      
40453  * 
40454  * @config : {Object} disable List of elements to disable.. (not done yet.)
40455  * @config : {Object} styles  Map of styles available.
40456  * 
40457  */
40458
40459 Roo.form.HtmlEditor.ToolbarContext = function(config)
40460 {
40461     
40462     Roo.apply(this, config);
40463     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40464     // dont call parent... till later.
40465     this.styles = this.styles || {};
40466 }
40467 Roo.form.HtmlEditor.ToolbarContext.types = {
40468     'IMG' : {
40469         width : {
40470             title: "Width",
40471             width: 40
40472         },
40473         height:  {
40474             title: "Height",
40475             width: 40
40476         },
40477         align: {
40478             title: "Align",
40479             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40480             width : 80
40481             
40482         },
40483         border: {
40484             title: "Border",
40485             width: 40
40486         },
40487         alt: {
40488             title: "Alt",
40489             width: 120
40490         },
40491         src : {
40492             title: "Src",
40493             width: 220
40494         }
40495         
40496     },
40497     'A' : {
40498         name : {
40499             title: "Name",
40500             width: 50
40501         },
40502         href:  {
40503             title: "Href",
40504             width: 220
40505         } // border?
40506         
40507     },
40508     'TABLE' : {
40509         rows : {
40510             title: "Rows",
40511             width: 20
40512         },
40513         cols : {
40514             title: "Cols",
40515             width: 20
40516         },
40517         width : {
40518             title: "Width",
40519             width: 40
40520         },
40521         height : {
40522             title: "Height",
40523             width: 40
40524         },
40525         border : {
40526             title: "Border",
40527             width: 20
40528         }
40529     },
40530     'TD' : {
40531         width : {
40532             title: "Width",
40533             width: 40
40534         },
40535         height : {
40536             title: "Height",
40537             width: 40
40538         },   
40539         align: {
40540             title: "Align",
40541             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40542             width: 80
40543         },
40544         valign: {
40545             title: "Valign",
40546             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40547             width: 80
40548         },
40549         colspan: {
40550             title: "Colspan",
40551             width: 20
40552             
40553         }
40554     },
40555     'INPUT' : {
40556         name : {
40557             title: "name",
40558             width: 120
40559         },
40560         value : {
40561             title: "Value",
40562             width: 120
40563         },
40564         width : {
40565             title: "Width",
40566             width: 40
40567         }
40568     },
40569     'LABEL' : {
40570         'for' : {
40571             title: "For",
40572             width: 120
40573         }
40574     },
40575     'TEXTAREA' : {
40576           name : {
40577             title: "name",
40578             width: 120
40579         },
40580         rows : {
40581             title: "Rows",
40582             width: 20
40583         },
40584         cols : {
40585             title: "Cols",
40586             width: 20
40587         }
40588     },
40589     'SELECT' : {
40590         name : {
40591             title: "name",
40592             width: 120
40593         },
40594         selectoptions : {
40595             title: "Options",
40596             width: 200
40597         }
40598     },
40599     
40600     // should we really allow this??
40601     // should this just be 
40602     'BODY' : {
40603         title : {
40604             title: "title",
40605             width: 200,
40606             disabled : true
40607         }
40608     },
40609     '*' : {
40610         // empty..
40611     }
40612 };
40613
40614
40615
40616 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40617     
40618     tb: false,
40619     
40620     rendered: false,
40621     
40622     editor : false,
40623     /**
40624      * @cfg {Object} disable  List of toolbar elements to disable
40625          
40626      */
40627     disable : false,
40628     /**
40629      * @cfg {Object} styles List of styles 
40630      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40631      *
40632      * These must be defined in the page, so they get rendered correctly..
40633      * .headline { }
40634      * TD.underline { }
40635      * 
40636      */
40637     styles : false,
40638     
40639     
40640     
40641     toolbars : false,
40642     
40643     init : function(editor)
40644     {
40645         this.editor = editor;
40646         
40647         
40648         var fid = editor.frameId;
40649         var etb = this;
40650         function btn(id, toggle, handler){
40651             var xid = fid + '-'+ id ;
40652             return {
40653                 id : xid,
40654                 cmd : id,
40655                 cls : 'x-btn-icon x-edit-'+id,
40656                 enableToggle:toggle !== false,
40657                 scope: editor, // was editor...
40658                 handler:handler||editor.relayBtnCmd,
40659                 clickEvent:'mousedown',
40660                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40661                 tabIndex:-1
40662             };
40663         }
40664         // create a new element.
40665         var wdiv = editor.wrap.createChild({
40666                 tag: 'div'
40667             }, editor.wrap.dom.firstChild.nextSibling, true);
40668         
40669         // can we do this more than once??
40670         
40671          // stop form submits
40672       
40673  
40674         // disable everything...
40675         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40676         this.toolbars = {};
40677            
40678         for (var i in  ty) {
40679           
40680             this.toolbars[i] = this.buildToolbar(ty[i],i);
40681         }
40682         this.tb = this.toolbars.BODY;
40683         this.tb.el.show();
40684         this.buildFooter();
40685         this.footer.show();
40686          
40687         this.rendered = true;
40688         
40689         // the all the btns;
40690         editor.on('editorevent', this.updateToolbar, this);
40691         // other toolbars need to implement this..
40692         //editor.on('editmodechange', this.updateToolbar, this);
40693     },
40694     
40695     
40696     
40697     /**
40698      * Protected method that will not generally be called directly. It triggers
40699      * a toolbar update by reading the markup state of the current selection in the editor.
40700      */
40701     updateToolbar: function(ignore_a,ignore_b,sel){
40702
40703         
40704         if(!this.editor.activated){
40705              this.editor.onFirstFocus();
40706             return;
40707         }
40708         var updateFooter = sel ? false : true;
40709         
40710         
40711         var ans = this.editor.getAllAncestors();
40712         
40713         // pick
40714         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40715         
40716         if (!sel) { 
40717             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40718             sel = sel ? sel : this.editor.doc.body;
40719             sel = sel.tagName.length ? sel : this.editor.doc.body;
40720             
40721         }
40722         // pick a menu that exists..
40723         var tn = sel.tagName.toUpperCase();
40724         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40725         
40726         tn = sel.tagName.toUpperCase();
40727         
40728         var lastSel = this.tb.selectedNode
40729         
40730         this.tb.selectedNode = sel;
40731         
40732         // if current menu does not match..
40733         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40734                 
40735             this.tb.el.hide();
40736             ///console.log("show: " + tn);
40737             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40738             this.tb.el.show();
40739             // update name
40740             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40741             
40742             
40743             // update attributes
40744             if (this.tb.fields) {
40745                 this.tb.fields.each(function(e) {
40746                    e.setValue(sel.getAttribute(e.name));
40747                 });
40748             }
40749             
40750             // update styles
40751             var st = this.tb.fields.item(0);
40752             st.store.removeAll();
40753             var cn = sel.className.split(/\s+/);
40754             
40755             var avs = [];
40756             if (this.styles['*']) {
40757                 
40758                 Roo.each(this.styles['*'], function(v) {
40759                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40760                 });
40761             }
40762             if (this.styles[tn]) { 
40763                 Roo.each(this.styles[tn], function(v) {
40764                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40765                 });
40766             }
40767             
40768             st.store.loadData(avs);
40769             st.collapse();
40770             st.setValue(cn);
40771             
40772             // flag our selected Node.
40773             this.tb.selectedNode = sel;
40774            
40775            
40776             Roo.menu.MenuMgr.hideAll();
40777
40778         }
40779         
40780         if (!updateFooter) {
40781             return;
40782         }
40783         // update the footer
40784         //
40785         var html = '';
40786         
40787         this.footerEls = ans.reverse();
40788         Roo.each(this.footerEls, function(a,i) {
40789             if (!a) { return; }
40790             html += html.length ? ' &gt; '  :  '';
40791             
40792             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40793             
40794         });
40795        
40796         // 
40797         var sz = this.footDisp.up('td').getSize();
40798         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40799         this.footDisp.dom.style.marginLeft = '5px';
40800         
40801         this.footDisp.dom.style.overflow = 'hidden';
40802         
40803         this.footDisp.dom.innerHTML = html;
40804             
40805         //this.editorsyncValue();
40806     },
40807    
40808        
40809     // private
40810     onDestroy : function(){
40811         if(this.rendered){
40812             
40813             this.tb.items.each(function(item){
40814                 if(item.menu){
40815                     item.menu.removeAll();
40816                     if(item.menu.el){
40817                         item.menu.el.destroy();
40818                     }
40819                 }
40820                 item.destroy();
40821             });
40822              
40823         }
40824     },
40825     onFirstFocus: function() {
40826         // need to do this for all the toolbars..
40827         this.tb.items.each(function(item){
40828            item.enable();
40829         });
40830     },
40831     buildToolbar: function(tlist, nm)
40832     {
40833         var editor = this.editor;
40834          // create a new element.
40835         var wdiv = editor.wrap.createChild({
40836                 tag: 'div'
40837             }, editor.wrap.dom.firstChild.nextSibling, true);
40838         
40839        
40840         var tb = new Roo.Toolbar(wdiv);
40841         // add the name..
40842         
40843         tb.add(nm+ ":&nbsp;");
40844         
40845         // styles...
40846         if (this.styles) {
40847             
40848             // this needs a multi-select checkbox...
40849             tb.addField( new Roo.form.ComboBox({
40850                 store: new Roo.data.SimpleStore({
40851                     id : 'val',
40852                     fields: ['val', 'selected'],
40853                     data : [] 
40854                 }),
40855                 name : 'className',
40856                 displayField:'val',
40857                 typeAhead: false,
40858                 mode: 'local',
40859                 editable : false,
40860                 triggerAction: 'all',
40861                 emptyText:'Select Style',
40862                 selectOnFocus:true,
40863                 width: 130,
40864                 listeners : {
40865                     'select': function(c, r, i) {
40866                         // initial support only for on class per el..
40867                         tb.selectedNode.className =  r ? r.get('val') : '';
40868                     }
40869                 }
40870     
40871             }));
40872         }
40873             
40874         
40875         
40876         for (var i in tlist) {
40877             
40878             var item = tlist[i];
40879             tb.add(item.title + ":&nbsp;");
40880             
40881             
40882             
40883             
40884             if (item.opts) {
40885                 // opts == pulldown..
40886                 tb.addField( new Roo.form.ComboBox({
40887                     store: new Roo.data.SimpleStore({
40888                         id : 'val',
40889                         fields: ['val'],
40890                         data : item.opts  
40891                     }),
40892                     name : i,
40893                     displayField:'val',
40894                     typeAhead: false,
40895                     mode: 'local',
40896                     editable : false,
40897                     triggerAction: 'all',
40898                     emptyText:'Select',
40899                     selectOnFocus:true,
40900                     width: item.width ? item.width  : 130,
40901                     listeners : {
40902                         'select': function(c, r, i) {
40903                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40904                         }
40905                     }
40906
40907                 }));
40908                 continue;
40909                     
40910                  
40911                 
40912                 tb.addField( new Roo.form.TextField({
40913                     name: i,
40914                     width: 100,
40915                     //allowBlank:false,
40916                     value: ''
40917                 }));
40918                 continue;
40919             }
40920             tb.addField( new Roo.form.TextField({
40921                 name: i,
40922                 width: item.width,
40923                 //allowBlank:true,
40924                 value: '',
40925                 listeners: {
40926                     'change' : function(f, nv, ov) {
40927                         tb.selectedNode.setAttribute(f.name, nv);
40928                     }
40929                 }
40930             }));
40931              
40932         }
40933         tb.el.on('click', function(e){
40934             e.preventDefault(); // what does this do?
40935         });
40936         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40937         tb.el.hide();
40938         tb.name = nm;
40939         // dont need to disable them... as they will get hidden
40940         return tb;
40941          
40942         
40943     },
40944     buildFooter : function()
40945     {
40946         
40947         var fel = this.editor.wrap.createChild();
40948         this.footer = new Roo.Toolbar(fel);
40949         // toolbar has scrolly on left / right?
40950         var footDisp= new Roo.Toolbar.Fill();
40951         var _t = this;
40952         this.footer.add(
40953             {
40954                 text : '&lt;',
40955                 xtype: 'Button',
40956                 handler : function() {
40957                     _t.footDisp.scrollTo('left',0,true)
40958                 }
40959             }
40960         );
40961         this.footer.add( footDisp );
40962         this.footer.add( 
40963             {
40964                 text : '&gt;',
40965                 xtype: 'Button',
40966                 handler : function() {
40967                     // no animation..
40968                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40969                 }
40970             }
40971         );
40972         var fel = Roo.get(footDisp.el);
40973         fel.addClass('x-editor-context');
40974         this.footDispWrap = fel; 
40975         this.footDispWrap.overflow  = 'hidden';
40976         
40977         this.footDisp = fel.createChild();
40978         this.footDispWrap.on('click', this.onContextClick, this)
40979         
40980         
40981     },
40982     onContextClick : function (ev,dom)
40983     {
40984         ev.preventDefault();
40985         var  cn = dom.className;
40986         Roo.log(cn);
40987         if (!cn.match(/x-ed-loc-/)) {
40988             return;
40989         }
40990         var n = cn.split('-').pop();
40991         var ans = this.footerEls;
40992         var sel = ans[n];
40993         
40994          // pick
40995         var range = this.editor.createRange();
40996         
40997         range.selectNodeContents(sel);
40998         //range.selectNode(sel);
40999         
41000         
41001         var selection = this.editor.getSelection();
41002         selection.removeAllRanges();
41003         selection.addRange(range);
41004         
41005         
41006         
41007         this.updateToolbar(null, null, sel);
41008         
41009         
41010     }
41011     
41012     
41013     
41014     
41015     
41016 });
41017
41018
41019
41020
41021
41022 /*
41023  * Based on:
41024  * Ext JS Library 1.1.1
41025  * Copyright(c) 2006-2007, Ext JS, LLC.
41026  *
41027  * Originally Released Under LGPL - original licence link has changed is not relivant.
41028  *
41029  * Fork - LGPL
41030  * <script type="text/javascript">
41031  */
41032  
41033 /**
41034  * @class Roo.form.BasicForm
41035  * @extends Roo.util.Observable
41036  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41037  * @constructor
41038  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41039  * @param {Object} config Configuration options
41040  */
41041 Roo.form.BasicForm = function(el, config){
41042     this.allItems = [];
41043     this.childForms = [];
41044     Roo.apply(this, config);
41045     /*
41046      * The Roo.form.Field items in this form.
41047      * @type MixedCollection
41048      */
41049      
41050      
41051     this.items = new Roo.util.MixedCollection(false, function(o){
41052         return o.id || (o.id = Roo.id());
41053     });
41054     this.addEvents({
41055         /**
41056          * @event beforeaction
41057          * Fires before any action is performed. Return false to cancel the action.
41058          * @param {Form} this
41059          * @param {Action} action The action to be performed
41060          */
41061         beforeaction: true,
41062         /**
41063          * @event actionfailed
41064          * Fires when an action fails.
41065          * @param {Form} this
41066          * @param {Action} action The action that failed
41067          */
41068         actionfailed : true,
41069         /**
41070          * @event actioncomplete
41071          * Fires when an action is completed.
41072          * @param {Form} this
41073          * @param {Action} action The action that completed
41074          */
41075         actioncomplete : true
41076     });
41077     if(el){
41078         this.initEl(el);
41079     }
41080     Roo.form.BasicForm.superclass.constructor.call(this);
41081 };
41082
41083 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41084     /**
41085      * @cfg {String} method
41086      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41087      */
41088     /**
41089      * @cfg {DataReader} reader
41090      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41091      * This is optional as there is built-in support for processing JSON.
41092      */
41093     /**
41094      * @cfg {DataReader} errorReader
41095      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41096      * This is completely optional as there is built-in support for processing JSON.
41097      */
41098     /**
41099      * @cfg {String} url
41100      * The URL to use for form actions if one isn't supplied in the action options.
41101      */
41102     /**
41103      * @cfg {Boolean} fileUpload
41104      * Set to true if this form is a file upload.
41105      */
41106      
41107     /**
41108      * @cfg {Object} baseParams
41109      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41110      */
41111      /**
41112      
41113     /**
41114      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41115      */
41116     timeout: 30,
41117
41118     // private
41119     activeAction : null,
41120
41121     /**
41122      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41123      * or setValues() data instead of when the form was first created.
41124      */
41125     trackResetOnLoad : false,
41126     
41127     
41128     /**
41129      * childForms - used for multi-tab forms
41130      * @type {Array}
41131      */
41132     childForms : false,
41133     
41134     /**
41135      * allItems - full list of fields.
41136      * @type {Array}
41137      */
41138     allItems : false,
41139     
41140     /**
41141      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41142      * element by passing it or its id or mask the form itself by passing in true.
41143      * @type Mixed
41144      */
41145     waitMsgTarget : false,
41146
41147     // private
41148     initEl : function(el){
41149         this.el = Roo.get(el);
41150         this.id = this.el.id || Roo.id();
41151         this.el.on('submit', this.onSubmit, this);
41152         this.el.addClass('x-form');
41153     },
41154
41155     // private
41156     onSubmit : function(e){
41157         e.stopEvent();
41158     },
41159
41160     /**
41161      * Returns true if client-side validation on the form is successful.
41162      * @return Boolean
41163      */
41164     isValid : function(){
41165         var valid = true;
41166         this.items.each(function(f){
41167            if(!f.validate()){
41168                valid = false;
41169            }
41170         });
41171         return valid;
41172     },
41173
41174     /**
41175      * Returns true if any fields in this form have changed since their original load.
41176      * @return Boolean
41177      */
41178     isDirty : function(){
41179         var dirty = false;
41180         this.items.each(function(f){
41181            if(f.isDirty()){
41182                dirty = true;
41183                return false;
41184            }
41185         });
41186         return dirty;
41187     },
41188
41189     /**
41190      * Performs a predefined action (submit or load) or custom actions you define on this form.
41191      * @param {String} actionName The name of the action type
41192      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41193      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41194      * accept other config options):
41195      * <pre>
41196 Property          Type             Description
41197 ----------------  ---------------  ----------------------------------------------------------------------------------
41198 url               String           The url for the action (defaults to the form's url)
41199 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41200 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41201 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41202                                    validate the form on the client (defaults to false)
41203      * </pre>
41204      * @return {BasicForm} this
41205      */
41206     doAction : function(action, options){
41207         if(typeof action == 'string'){
41208             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41209         }
41210         if(this.fireEvent('beforeaction', this, action) !== false){
41211             this.beforeAction(action);
41212             action.run.defer(100, action);
41213         }
41214         return this;
41215     },
41216
41217     /**
41218      * Shortcut to do a submit action.
41219      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41220      * @return {BasicForm} this
41221      */
41222     submit : function(options){
41223         this.doAction('submit', options);
41224         return this;
41225     },
41226
41227     /**
41228      * Shortcut to do a load action.
41229      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41230      * @return {BasicForm} this
41231      */
41232     load : function(options){
41233         this.doAction('load', options);
41234         return this;
41235     },
41236
41237     /**
41238      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41239      * @param {Record} record The record to edit
41240      * @return {BasicForm} this
41241      */
41242     updateRecord : function(record){
41243         record.beginEdit();
41244         var fs = record.fields;
41245         fs.each(function(f){
41246             var field = this.findField(f.name);
41247             if(field){
41248                 record.set(f.name, field.getValue());
41249             }
41250         }, this);
41251         record.endEdit();
41252         return this;
41253     },
41254
41255     /**
41256      * Loads an Roo.data.Record into this form.
41257      * @param {Record} record The record to load
41258      * @return {BasicForm} this
41259      */
41260     loadRecord : function(record){
41261         this.setValues(record.data);
41262         return this;
41263     },
41264
41265     // private
41266     beforeAction : function(action){
41267         var o = action.options;
41268         
41269        
41270         if(this.waitMsgTarget === true){
41271             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41272         }else if(this.waitMsgTarget){
41273             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41274             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41275         }else {
41276             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41277         }
41278          
41279     },
41280
41281     // private
41282     afterAction : function(action, success){
41283         this.activeAction = null;
41284         var o = action.options;
41285         
41286         if(this.waitMsgTarget === true){
41287             this.el.unmask();
41288         }else if(this.waitMsgTarget){
41289             this.waitMsgTarget.unmask();
41290         }else{
41291             Roo.MessageBox.updateProgress(1);
41292             Roo.MessageBox.hide();
41293         }
41294          
41295         if(success){
41296             if(o.reset){
41297                 this.reset();
41298             }
41299             Roo.callback(o.success, o.scope, [this, action]);
41300             this.fireEvent('actioncomplete', this, action);
41301             
41302         }else{
41303             Roo.callback(o.failure, o.scope, [this, action]);
41304             // show an error message if no failed handler is set..
41305             if (!this.hasListener('actionfailed')) {
41306                 Roo.MessageBox.alert("Error",
41307                     typeof(action.result.errorMsg) != 'undefined' ?
41308                         action.result.errorMsg :
41309                         "Saving Failed, please check your entries"
41310                 );
41311             }
41312             
41313             this.fireEvent('actionfailed', this, action);
41314         }
41315         
41316     },
41317
41318     /**
41319      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41320      * @param {String} id The value to search for
41321      * @return Field
41322      */
41323     findField : function(id){
41324         var field = this.items.get(id);
41325         if(!field){
41326             this.items.each(function(f){
41327                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41328                     field = f;
41329                     return false;
41330                 }
41331             });
41332         }
41333         return field || null;
41334     },
41335
41336     /**
41337      * Add a secondary form to this one, 
41338      * Used to provide tabbed forms. One form is primary, with hidden values 
41339      * which mirror the elements from the other forms.
41340      * 
41341      * @param {Roo.form.Form} form to add.
41342      * 
41343      */
41344     addForm : function(form)
41345     {
41346        
41347         if (this.childForms.indexOf(form) > -1) {
41348             // already added..
41349             return;
41350         }
41351         this.childForms.push(form);
41352         var n = '';
41353         Roo.each(form.allItems, function (fe) {
41354             
41355             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41356             if (this.findField(n)) { // already added..
41357                 return;
41358             }
41359             var add = new Roo.form.Hidden({
41360                 name : n
41361             });
41362             add.render(this.el);
41363             
41364             this.add( add );
41365         }, this);
41366         
41367     },
41368     /**
41369      * Mark fields in this form invalid in bulk.
41370      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41371      * @return {BasicForm} this
41372      */
41373     markInvalid : function(errors){
41374         if(errors instanceof Array){
41375             for(var i = 0, len = errors.length; i < len; i++){
41376                 var fieldError = errors[i];
41377                 var f = this.findField(fieldError.id);
41378                 if(f){
41379                     f.markInvalid(fieldError.msg);
41380                 }
41381             }
41382         }else{
41383             var field, id;
41384             for(id in errors){
41385                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41386                     field.markInvalid(errors[id]);
41387                 }
41388             }
41389         }
41390         Roo.each(this.childForms || [], function (f) {
41391             f.markInvalid(errors);
41392         });
41393         
41394         return this;
41395     },
41396
41397     /**
41398      * Set values for fields in this form in bulk.
41399      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41400      * @return {BasicForm} this
41401      */
41402     setValues : function(values){
41403         if(values instanceof Array){ // array of objects
41404             for(var i = 0, len = values.length; i < len; i++){
41405                 var v = values[i];
41406                 var f = this.findField(v.id);
41407                 if(f){
41408                     f.setValue(v.value);
41409                     if(this.trackResetOnLoad){
41410                         f.originalValue = f.getValue();
41411                     }
41412                 }
41413             }
41414         }else{ // object hash
41415             var field, id;
41416             for(id in values){
41417                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41418                     
41419                     if (field.setFromData && 
41420                         field.valueField && 
41421                         field.displayField &&
41422                         // combos' with local stores can 
41423                         // be queried via setValue()
41424                         // to set their value..
41425                         (field.store && !field.store.isLocal)
41426                         ) {
41427                         // it's a combo
41428                         var sd = { };
41429                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41430                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41431                         field.setFromData(sd);
41432                         
41433                     } else {
41434                         field.setValue(values[id]);
41435                     }
41436                     
41437                     
41438                     if(this.trackResetOnLoad){
41439                         field.originalValue = field.getValue();
41440                     }
41441                 }
41442             }
41443         }
41444          
41445         Roo.each(this.childForms || [], function (f) {
41446             f.setValues(values);
41447         });
41448                 
41449         return this;
41450     },
41451
41452     /**
41453      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41454      * they are returned as an array.
41455      * @param {Boolean} asString
41456      * @return {Object}
41457      */
41458     getValues : function(asString){
41459         if (this.childForms) {
41460             // copy values from the child forms
41461             Roo.each(this.childForms, function (f) {
41462                 this.setValues(f.getValues());
41463             }, this);
41464         }
41465         
41466         
41467         
41468         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41469         if(asString === true){
41470             return fs;
41471         }
41472         return Roo.urlDecode(fs);
41473     },
41474     
41475     /**
41476      * Returns the fields in this form as an object with key/value pairs. 
41477      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41478      * @return {Object}
41479      */
41480     getFieldValues : function(with_hidden)
41481     {
41482         if (this.childForms) {
41483             // copy values from the child forms
41484             // should this call getFieldValues - probably not as we do not currently copy
41485             // hidden fields when we generate..
41486             Roo.each(this.childForms, function (f) {
41487                 this.setValues(f.getValues());
41488             }, this);
41489         }
41490         
41491         var ret = {};
41492         this.items.each(function(f){
41493             if (!f.getName()) {
41494                 return;
41495             }
41496             var v = f.getValue();
41497             // not sure if this supported any more..
41498             if ((typeof(v) == 'object') && f.getRawValue) {
41499                 v = f.getRawValue() ; // dates..
41500             }
41501             // combo boxes where name != hiddenName...
41502             if (f.name != f.getName()) {
41503                 ret[f.name] = f.getRawValue();
41504             }
41505             ret[f.getName()] = v;
41506         });
41507         
41508         return ret;
41509     },
41510
41511     /**
41512      * Clears all invalid messages in this form.
41513      * @return {BasicForm} this
41514      */
41515     clearInvalid : function(){
41516         this.items.each(function(f){
41517            f.clearInvalid();
41518         });
41519         
41520         Roo.each(this.childForms || [], function (f) {
41521             f.clearInvalid();
41522         });
41523         
41524         
41525         return this;
41526     },
41527
41528     /**
41529      * Resets this form.
41530      * @return {BasicForm} this
41531      */
41532     reset : function(){
41533         this.items.each(function(f){
41534             f.reset();
41535         });
41536         
41537         Roo.each(this.childForms || [], function (f) {
41538             f.reset();
41539         });
41540        
41541         
41542         return this;
41543     },
41544
41545     /**
41546      * Add Roo.form components to this form.
41547      * @param {Field} field1
41548      * @param {Field} field2 (optional)
41549      * @param {Field} etc (optional)
41550      * @return {BasicForm} this
41551      */
41552     add : function(){
41553         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41554         return this;
41555     },
41556
41557
41558     /**
41559      * Removes a field from the items collection (does NOT remove its markup).
41560      * @param {Field} field
41561      * @return {BasicForm} this
41562      */
41563     remove : function(field){
41564         this.items.remove(field);
41565         return this;
41566     },
41567
41568     /**
41569      * Looks at the fields in this form, checks them for an id attribute,
41570      * and calls applyTo on the existing dom element with that id.
41571      * @return {BasicForm} this
41572      */
41573     render : function(){
41574         this.items.each(function(f){
41575             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41576                 f.applyTo(f.id);
41577             }
41578         });
41579         return this;
41580     },
41581
41582     /**
41583      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41584      * @param {Object} values
41585      * @return {BasicForm} this
41586      */
41587     applyToFields : function(o){
41588         this.items.each(function(f){
41589            Roo.apply(f, o);
41590         });
41591         return this;
41592     },
41593
41594     /**
41595      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41596      * @param {Object} values
41597      * @return {BasicForm} this
41598      */
41599     applyIfToFields : function(o){
41600         this.items.each(function(f){
41601            Roo.applyIf(f, o);
41602         });
41603         return this;
41604     }
41605 });
41606
41607 // back compat
41608 Roo.BasicForm = Roo.form.BasicForm;/*
41609  * Based on:
41610  * Ext JS Library 1.1.1
41611  * Copyright(c) 2006-2007, Ext JS, LLC.
41612  *
41613  * Originally Released Under LGPL - original licence link has changed is not relivant.
41614  *
41615  * Fork - LGPL
41616  * <script type="text/javascript">
41617  */
41618
41619 /**
41620  * @class Roo.form.Form
41621  * @extends Roo.form.BasicForm
41622  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41623  * @constructor
41624  * @param {Object} config Configuration options
41625  */
41626 Roo.form.Form = function(config){
41627     var xitems =  [];
41628     if (config.items) {
41629         xitems = config.items;
41630         delete config.items;
41631     }
41632    
41633     
41634     Roo.form.Form.superclass.constructor.call(this, null, config);
41635     this.url = this.url || this.action;
41636     if(!this.root){
41637         this.root = new Roo.form.Layout(Roo.applyIf({
41638             id: Roo.id()
41639         }, config));
41640     }
41641     this.active = this.root;
41642     /**
41643      * Array of all the buttons that have been added to this form via {@link addButton}
41644      * @type Array
41645      */
41646     this.buttons = [];
41647     this.allItems = [];
41648     this.addEvents({
41649         /**
41650          * @event clientvalidation
41651          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41652          * @param {Form} this
41653          * @param {Boolean} valid true if the form has passed client-side validation
41654          */
41655         clientvalidation: true,
41656         /**
41657          * @event rendered
41658          * Fires when the form is rendered
41659          * @param {Roo.form.Form} form
41660          */
41661         rendered : true
41662     });
41663     
41664     if (this.progressUrl) {
41665             // push a hidden field onto the list of fields..
41666             this.addxtype( {
41667                     xns: Roo.form, 
41668                     xtype : 'Hidden', 
41669                     name : 'UPLOAD_IDENTIFIER' 
41670             });
41671         }
41672         
41673     
41674     Roo.each(xitems, this.addxtype, this);
41675     
41676     
41677     
41678 };
41679
41680 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41681     /**
41682      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41683      */
41684     /**
41685      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41686      */
41687     /**
41688      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41689      */
41690     buttonAlign:'center',
41691
41692     /**
41693      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41694      */
41695     minButtonWidth:75,
41696
41697     /**
41698      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41699      * This property cascades to child containers if not set.
41700      */
41701     labelAlign:'left',
41702
41703     /**
41704      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41705      * fires a looping event with that state. This is required to bind buttons to the valid
41706      * state using the config value formBind:true on the button.
41707      */
41708     monitorValid : false,
41709
41710     /**
41711      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41712      */
41713     monitorPoll : 200,
41714     
41715     /**
41716      * @cfg {String} progressUrl - Url to return progress data 
41717      */
41718     
41719     progressUrl : false,
41720   
41721     /**
41722      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41723      * fields are added and the column is closed. If no fields are passed the column remains open
41724      * until end() is called.
41725      * @param {Object} config The config to pass to the column
41726      * @param {Field} field1 (optional)
41727      * @param {Field} field2 (optional)
41728      * @param {Field} etc (optional)
41729      * @return Column The column container object
41730      */
41731     column : function(c){
41732         var col = new Roo.form.Column(c);
41733         this.start(col);
41734         if(arguments.length > 1){ // duplicate code required because of Opera
41735             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41736             this.end();
41737         }
41738         return col;
41739     },
41740
41741     /**
41742      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41743      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41744      * until end() is called.
41745      * @param {Object} config The config to pass to the fieldset
41746      * @param {Field} field1 (optional)
41747      * @param {Field} field2 (optional)
41748      * @param {Field} etc (optional)
41749      * @return FieldSet The fieldset container object
41750      */
41751     fieldset : function(c){
41752         var fs = new Roo.form.FieldSet(c);
41753         this.start(fs);
41754         if(arguments.length > 1){ // duplicate code required because of Opera
41755             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41756             this.end();
41757         }
41758         return fs;
41759     },
41760
41761     /**
41762      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41763      * fields are added and the container is closed. If no fields are passed the container remains open
41764      * until end() is called.
41765      * @param {Object} config The config to pass to the Layout
41766      * @param {Field} field1 (optional)
41767      * @param {Field} field2 (optional)
41768      * @param {Field} etc (optional)
41769      * @return Layout The container object
41770      */
41771     container : function(c){
41772         var l = new Roo.form.Layout(c);
41773         this.start(l);
41774         if(arguments.length > 1){ // duplicate code required because of Opera
41775             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41776             this.end();
41777         }
41778         return l;
41779     },
41780
41781     /**
41782      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41783      * @param {Object} container A Roo.form.Layout or subclass of Layout
41784      * @return {Form} this
41785      */
41786     start : function(c){
41787         // cascade label info
41788         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41789         this.active.stack.push(c);
41790         c.ownerCt = this.active;
41791         this.active = c;
41792         return this;
41793     },
41794
41795     /**
41796      * Closes the current open container
41797      * @return {Form} this
41798      */
41799     end : function(){
41800         if(this.active == this.root){
41801             return this;
41802         }
41803         this.active = this.active.ownerCt;
41804         return this;
41805     },
41806
41807     /**
41808      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41809      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41810      * as the label of the field.
41811      * @param {Field} field1
41812      * @param {Field} field2 (optional)
41813      * @param {Field} etc. (optional)
41814      * @return {Form} this
41815      */
41816     add : function(){
41817         this.active.stack.push.apply(this.active.stack, arguments);
41818         this.allItems.push.apply(this.allItems,arguments);
41819         var r = [];
41820         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41821             if(a[i].isFormField){
41822                 r.push(a[i]);
41823             }
41824         }
41825         if(r.length > 0){
41826             Roo.form.Form.superclass.add.apply(this, r);
41827         }
41828         return this;
41829     },
41830     
41831
41832     
41833     
41834     
41835      /**
41836      * Find any element that has been added to a form, using it's ID or name
41837      * This can include framesets, columns etc. along with regular fields..
41838      * @param {String} id - id or name to find.
41839      
41840      * @return {Element} e - or false if nothing found.
41841      */
41842     findbyId : function(id)
41843     {
41844         var ret = false;
41845         if (!id) {
41846             return ret;
41847         }
41848         Roo.each(this.allItems, function(f){
41849             if (f.id == id || f.name == id ){
41850                 ret = f;
41851                 return false;
41852             }
41853         });
41854         return ret;
41855     },
41856
41857     
41858     
41859     /**
41860      * Render this form into the passed container. This should only be called once!
41861      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41862      * @return {Form} this
41863      */
41864     render : function(ct)
41865     {
41866         
41867         
41868         
41869         ct = Roo.get(ct);
41870         var o = this.autoCreate || {
41871             tag: 'form',
41872             method : this.method || 'POST',
41873             id : this.id || Roo.id()
41874         };
41875         this.initEl(ct.createChild(o));
41876
41877         this.root.render(this.el);
41878         
41879        
41880              
41881         this.items.each(function(f){
41882             f.render('x-form-el-'+f.id);
41883         });
41884
41885         if(this.buttons.length > 0){
41886             // tables are required to maintain order and for correct IE layout
41887             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41888                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41889                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41890             }}, null, true);
41891             var tr = tb.getElementsByTagName('tr')[0];
41892             for(var i = 0, len = this.buttons.length; i < len; i++) {
41893                 var b = this.buttons[i];
41894                 var td = document.createElement('td');
41895                 td.className = 'x-form-btn-td';
41896                 b.render(tr.appendChild(td));
41897             }
41898         }
41899         if(this.monitorValid){ // initialize after render
41900             this.startMonitoring();
41901         }
41902         this.fireEvent('rendered', this);
41903         return this;
41904     },
41905
41906     /**
41907      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41908      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41909      * object or a valid Roo.DomHelper element config
41910      * @param {Function} handler The function called when the button is clicked
41911      * @param {Object} scope (optional) The scope of the handler function
41912      * @return {Roo.Button}
41913      */
41914     addButton : function(config, handler, scope){
41915         var bc = {
41916             handler: handler,
41917             scope: scope,
41918             minWidth: this.minButtonWidth,
41919             hideParent:true
41920         };
41921         if(typeof config == "string"){
41922             bc.text = config;
41923         }else{
41924             Roo.apply(bc, config);
41925         }
41926         var btn = new Roo.Button(null, bc);
41927         this.buttons.push(btn);
41928         return btn;
41929     },
41930
41931      /**
41932      * Adds a series of form elements (using the xtype property as the factory method.
41933      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41934      * @param {Object} config 
41935      */
41936     
41937     addxtype : function()
41938     {
41939         var ar = Array.prototype.slice.call(arguments, 0);
41940         var ret = false;
41941         for(var i = 0; i < ar.length; i++) {
41942             if (!ar[i]) {
41943                 continue; // skip -- if this happends something invalid got sent, we 
41944                 // should ignore it, as basically that interface element will not show up
41945                 // and that should be pretty obvious!!
41946             }
41947             
41948             if (Roo.form[ar[i].xtype]) {
41949                 ar[i].form = this;
41950                 var fe = Roo.factory(ar[i], Roo.form);
41951                 if (!ret) {
41952                     ret = fe;
41953                 }
41954                 fe.form = this;
41955                 if (fe.store) {
41956                     fe.store.form = this;
41957                 }
41958                 if (fe.isLayout) {  
41959                          
41960                     this.start(fe);
41961                     this.allItems.push(fe);
41962                     if (fe.items && fe.addxtype) {
41963                         fe.addxtype.apply(fe, fe.items);
41964                         delete fe.items;
41965                     }
41966                      this.end();
41967                     continue;
41968                 }
41969                 
41970                 
41971                  
41972                 this.add(fe);
41973               //  console.log('adding ' + ar[i].xtype);
41974             }
41975             if (ar[i].xtype == 'Button') {  
41976                 //console.log('adding button');
41977                 //console.log(ar[i]);
41978                 this.addButton(ar[i]);
41979                 this.allItems.push(fe);
41980                 continue;
41981             }
41982             
41983             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41984                 alert('end is not supported on xtype any more, use items');
41985             //    this.end();
41986             //    //console.log('adding end');
41987             }
41988             
41989         }
41990         return ret;
41991     },
41992     
41993     /**
41994      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41995      * option "monitorValid"
41996      */
41997     startMonitoring : function(){
41998         if(!this.bound){
41999             this.bound = true;
42000             Roo.TaskMgr.start({
42001                 run : this.bindHandler,
42002                 interval : this.monitorPoll || 200,
42003                 scope: this
42004             });
42005         }
42006     },
42007
42008     /**
42009      * Stops monitoring of the valid state of this form
42010      */
42011     stopMonitoring : function(){
42012         this.bound = false;
42013     },
42014
42015     // private
42016     bindHandler : function(){
42017         if(!this.bound){
42018             return false; // stops binding
42019         }
42020         var valid = true;
42021         this.items.each(function(f){
42022             if(!f.isValid(true)){
42023                 valid = false;
42024                 return false;
42025             }
42026         });
42027         for(var i = 0, len = this.buttons.length; i < len; i++){
42028             var btn = this.buttons[i];
42029             if(btn.formBind === true && btn.disabled === valid){
42030                 btn.setDisabled(!valid);
42031             }
42032         }
42033         this.fireEvent('clientvalidation', this, valid);
42034     }
42035     
42036     
42037     
42038     
42039     
42040     
42041     
42042     
42043 });
42044
42045
42046 // back compat
42047 Roo.Form = Roo.form.Form;
42048 /*
42049  * Based on:
42050  * Ext JS Library 1.1.1
42051  * Copyright(c) 2006-2007, Ext JS, LLC.
42052  *
42053  * Originally Released Under LGPL - original licence link has changed is not relivant.
42054  *
42055  * Fork - LGPL
42056  * <script type="text/javascript">
42057  */
42058  
42059  /**
42060  * @class Roo.form.Action
42061  * Internal Class used to handle form actions
42062  * @constructor
42063  * @param {Roo.form.BasicForm} el The form element or its id
42064  * @param {Object} config Configuration options
42065  */
42066  
42067  
42068 // define the action interface
42069 Roo.form.Action = function(form, options){
42070     this.form = form;
42071     this.options = options || {};
42072 };
42073 /**
42074  * Client Validation Failed
42075  * @const 
42076  */
42077 Roo.form.Action.CLIENT_INVALID = 'client';
42078 /**
42079  * Server Validation Failed
42080  * @const 
42081  */
42082  Roo.form.Action.SERVER_INVALID = 'server';
42083  /**
42084  * Connect to Server Failed
42085  * @const 
42086  */
42087 Roo.form.Action.CONNECT_FAILURE = 'connect';
42088 /**
42089  * Reading Data from Server Failed
42090  * @const 
42091  */
42092 Roo.form.Action.LOAD_FAILURE = 'load';
42093
42094 Roo.form.Action.prototype = {
42095     type : 'default',
42096     failureType : undefined,
42097     response : undefined,
42098     result : undefined,
42099
42100     // interface method
42101     run : function(options){
42102
42103     },
42104
42105     // interface method
42106     success : function(response){
42107
42108     },
42109
42110     // interface method
42111     handleResponse : function(response){
42112
42113     },
42114
42115     // default connection failure
42116     failure : function(response){
42117         
42118         this.response = response;
42119         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42120         this.form.afterAction(this, false);
42121     },
42122
42123     processResponse : function(response){
42124         this.response = response;
42125         if(!response.responseText){
42126             return true;
42127         }
42128         this.result = this.handleResponse(response);
42129         return this.result;
42130     },
42131
42132     // utility functions used internally
42133     getUrl : function(appendParams){
42134         var url = this.options.url || this.form.url || this.form.el.dom.action;
42135         if(appendParams){
42136             var p = this.getParams();
42137             if(p){
42138                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42139             }
42140         }
42141         return url;
42142     },
42143
42144     getMethod : function(){
42145         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42146     },
42147
42148     getParams : function(){
42149         var bp = this.form.baseParams;
42150         var p = this.options.params;
42151         if(p){
42152             if(typeof p == "object"){
42153                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42154             }else if(typeof p == 'string' && bp){
42155                 p += '&' + Roo.urlEncode(bp);
42156             }
42157         }else if(bp){
42158             p = Roo.urlEncode(bp);
42159         }
42160         return p;
42161     },
42162
42163     createCallback : function(){
42164         return {
42165             success: this.success,
42166             failure: this.failure,
42167             scope: this,
42168             timeout: (this.form.timeout*1000),
42169             upload: this.form.fileUpload ? this.success : undefined
42170         };
42171     }
42172 };
42173
42174 Roo.form.Action.Submit = function(form, options){
42175     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42176 };
42177
42178 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42179     type : 'submit',
42180
42181     haveProgress : false,
42182     uploadComplete : false,
42183     
42184     // uploadProgress indicator.
42185     uploadProgress : function()
42186     {
42187         if (!this.form.progressUrl) {
42188             return;
42189         }
42190         
42191         if (!this.haveProgress) {
42192             Roo.MessageBox.progress("Uploading", "Uploading");
42193         }
42194         if (this.uploadComplete) {
42195            Roo.MessageBox.hide();
42196            return;
42197         }
42198         
42199         this.haveProgress = true;
42200    
42201         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42202         
42203         var c = new Roo.data.Connection();
42204         c.request({
42205             url : this.form.progressUrl,
42206             params: {
42207                 id : uid
42208             },
42209             method: 'GET',
42210             success : function(req){
42211                //console.log(data);
42212                 var rdata = false;
42213                 var edata;
42214                 try  {
42215                    rdata = Roo.decode(req.responseText)
42216                 } catch (e) {
42217                     Roo.log("Invalid data from server..");
42218                     Roo.log(edata);
42219                     return;
42220                 }
42221                 if (!rdata || !rdata.success) {
42222                     Roo.log(rdata);
42223                     return;
42224                 }
42225                 var data = rdata.data;
42226                 
42227                 if (this.uploadComplete) {
42228                    Roo.MessageBox.hide();
42229                    return;
42230                 }
42231                    
42232                 if (data){
42233                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42234                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42235                     );
42236                 }
42237                 this.uploadProgress.defer(2000,this);
42238             },
42239        
42240             failure: function(data) {
42241                 Roo.log('progress url failed ');
42242                 Roo.log(data);
42243             },
42244             scope : this
42245         });
42246            
42247     },
42248     
42249     
42250     run : function()
42251     {
42252         // run get Values on the form, so it syncs any secondary forms.
42253         this.form.getValues();
42254         
42255         var o = this.options;
42256         var method = this.getMethod();
42257         var isPost = method == 'POST';
42258         if(o.clientValidation === false || this.form.isValid()){
42259             
42260             if (this.form.progressUrl) {
42261                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42262                     (new Date() * 1) + '' + Math.random());
42263                     
42264             } 
42265             
42266             
42267             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42268                 form:this.form.el.dom,
42269                 url:this.getUrl(!isPost),
42270                 method: method,
42271                 params:isPost ? this.getParams() : null,
42272                 isUpload: this.form.fileUpload
42273             }));
42274             
42275             this.uploadProgress();
42276
42277         }else if (o.clientValidation !== false){ // client validation failed
42278             this.failureType = Roo.form.Action.CLIENT_INVALID;
42279             this.form.afterAction(this, false);
42280         }
42281     },
42282
42283     success : function(response)
42284     {
42285         this.uploadComplete= true;
42286         if (this.haveProgress) {
42287             Roo.MessageBox.hide();
42288         }
42289         
42290         
42291         var result = this.processResponse(response);
42292         if(result === true || result.success){
42293             this.form.afterAction(this, true);
42294             return;
42295         }
42296         if(result.errors){
42297             this.form.markInvalid(result.errors);
42298             this.failureType = Roo.form.Action.SERVER_INVALID;
42299         }
42300         this.form.afterAction(this, false);
42301     },
42302     failure : function(response)
42303     {
42304         this.uploadComplete= true;
42305         if (this.haveProgress) {
42306             Roo.MessageBox.hide();
42307         }
42308         
42309         this.response = response;
42310         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42311         this.form.afterAction(this, false);
42312     },
42313     
42314     handleResponse : function(response){
42315         if(this.form.errorReader){
42316             var rs = this.form.errorReader.read(response);
42317             var errors = [];
42318             if(rs.records){
42319                 for(var i = 0, len = rs.records.length; i < len; i++) {
42320                     var r = rs.records[i];
42321                     errors[i] = r.data;
42322                 }
42323             }
42324             if(errors.length < 1){
42325                 errors = null;
42326             }
42327             return {
42328                 success : rs.success,
42329                 errors : errors
42330             };
42331         }
42332         var ret = false;
42333         try {
42334             ret = Roo.decode(response.responseText);
42335         } catch (e) {
42336             ret = {
42337                 success: false,
42338                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42339                 errors : []
42340             };
42341         }
42342         return ret;
42343         
42344     }
42345 });
42346
42347
42348 Roo.form.Action.Load = function(form, options){
42349     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42350     this.reader = this.form.reader;
42351 };
42352
42353 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42354     type : 'load',
42355
42356     run : function(){
42357         
42358         Roo.Ajax.request(Roo.apply(
42359                 this.createCallback(), {
42360                     method:this.getMethod(),
42361                     url:this.getUrl(false),
42362                     params:this.getParams()
42363         }));
42364     },
42365
42366     success : function(response){
42367         
42368         var result = this.processResponse(response);
42369         if(result === true || !result.success || !result.data){
42370             this.failureType = Roo.form.Action.LOAD_FAILURE;
42371             this.form.afterAction(this, false);
42372             return;
42373         }
42374         this.form.clearInvalid();
42375         this.form.setValues(result.data);
42376         this.form.afterAction(this, true);
42377     },
42378
42379     handleResponse : function(response){
42380         if(this.form.reader){
42381             var rs = this.form.reader.read(response);
42382             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42383             return {
42384                 success : rs.success,
42385                 data : data
42386             };
42387         }
42388         return Roo.decode(response.responseText);
42389     }
42390 });
42391
42392 Roo.form.Action.ACTION_TYPES = {
42393     'load' : Roo.form.Action.Load,
42394     'submit' : Roo.form.Action.Submit
42395 };/*
42396  * Based on:
42397  * Ext JS Library 1.1.1
42398  * Copyright(c) 2006-2007, Ext JS, LLC.
42399  *
42400  * Originally Released Under LGPL - original licence link has changed is not relivant.
42401  *
42402  * Fork - LGPL
42403  * <script type="text/javascript">
42404  */
42405  
42406 /**
42407  * @class Roo.form.Layout
42408  * @extends Roo.Component
42409  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42410  * @constructor
42411  * @param {Object} config Configuration options
42412  */
42413 Roo.form.Layout = function(config){
42414     var xitems = [];
42415     if (config.items) {
42416         xitems = config.items;
42417         delete config.items;
42418     }
42419     Roo.form.Layout.superclass.constructor.call(this, config);
42420     this.stack = [];
42421     Roo.each(xitems, this.addxtype, this);
42422      
42423 };
42424
42425 Roo.extend(Roo.form.Layout, Roo.Component, {
42426     /**
42427      * @cfg {String/Object} autoCreate
42428      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42429      */
42430     /**
42431      * @cfg {String/Object/Function} style
42432      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42433      * a function which returns such a specification.
42434      */
42435     /**
42436      * @cfg {String} labelAlign
42437      * Valid values are "left," "top" and "right" (defaults to "left")
42438      */
42439     /**
42440      * @cfg {Number} labelWidth
42441      * Fixed width in pixels of all field labels (defaults to undefined)
42442      */
42443     /**
42444      * @cfg {Boolean} clear
42445      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42446      */
42447     clear : true,
42448     /**
42449      * @cfg {String} labelSeparator
42450      * The separator to use after field labels (defaults to ':')
42451      */
42452     labelSeparator : ':',
42453     /**
42454      * @cfg {Boolean} hideLabels
42455      * True to suppress the display of field labels in this layout (defaults to false)
42456      */
42457     hideLabels : false,
42458
42459     // private
42460     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42461     
42462     isLayout : true,
42463     
42464     // private
42465     onRender : function(ct, position){
42466         if(this.el){ // from markup
42467             this.el = Roo.get(this.el);
42468         }else {  // generate
42469             var cfg = this.getAutoCreate();
42470             this.el = ct.createChild(cfg, position);
42471         }
42472         if(this.style){
42473             this.el.applyStyles(this.style);
42474         }
42475         if(this.labelAlign){
42476             this.el.addClass('x-form-label-'+this.labelAlign);
42477         }
42478         if(this.hideLabels){
42479             this.labelStyle = "display:none";
42480             this.elementStyle = "padding-left:0;";
42481         }else{
42482             if(typeof this.labelWidth == 'number'){
42483                 this.labelStyle = "width:"+this.labelWidth+"px;";
42484                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42485             }
42486             if(this.labelAlign == 'top'){
42487                 this.labelStyle = "width:auto;";
42488                 this.elementStyle = "padding-left:0;";
42489             }
42490         }
42491         var stack = this.stack;
42492         var slen = stack.length;
42493         if(slen > 0){
42494             if(!this.fieldTpl){
42495                 var t = new Roo.Template(
42496                     '<div class="x-form-item {5}">',
42497                         '<label for="{0}" style="{2}">{1}{4}</label>',
42498                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42499                         '</div>',
42500                     '</div><div class="x-form-clear-left"></div>'
42501                 );
42502                 t.disableFormats = true;
42503                 t.compile();
42504                 Roo.form.Layout.prototype.fieldTpl = t;
42505             }
42506             for(var i = 0; i < slen; i++) {
42507                 if(stack[i].isFormField){
42508                     this.renderField(stack[i]);
42509                 }else{
42510                     this.renderComponent(stack[i]);
42511                 }
42512             }
42513         }
42514         if(this.clear){
42515             this.el.createChild({cls:'x-form-clear'});
42516         }
42517     },
42518
42519     // private
42520     renderField : function(f){
42521         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42522                f.id, //0
42523                f.fieldLabel, //1
42524                f.labelStyle||this.labelStyle||'', //2
42525                this.elementStyle||'', //3
42526                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42527                f.itemCls||this.itemCls||''  //5
42528        ], true).getPrevSibling());
42529     },
42530
42531     // private
42532     renderComponent : function(c){
42533         c.render(c.isLayout ? this.el : this.el.createChild());    
42534     },
42535     /**
42536      * Adds a object form elements (using the xtype property as the factory method.)
42537      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42538      * @param {Object} config 
42539      */
42540     addxtype : function(o)
42541     {
42542         // create the lement.
42543         o.form = this.form;
42544         var fe = Roo.factory(o, Roo.form);
42545         this.form.allItems.push(fe);
42546         this.stack.push(fe);
42547         
42548         if (fe.isFormField) {
42549             this.form.items.add(fe);
42550         }
42551          
42552         return fe;
42553     }
42554 });
42555
42556 /**
42557  * @class Roo.form.Column
42558  * @extends Roo.form.Layout
42559  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42560  * @constructor
42561  * @param {Object} config Configuration options
42562  */
42563 Roo.form.Column = function(config){
42564     Roo.form.Column.superclass.constructor.call(this, config);
42565 };
42566
42567 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42568     /**
42569      * @cfg {Number/String} width
42570      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42571      */
42572     /**
42573      * @cfg {String/Object} autoCreate
42574      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42575      */
42576
42577     // private
42578     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42579
42580     // private
42581     onRender : function(ct, position){
42582         Roo.form.Column.superclass.onRender.call(this, ct, position);
42583         if(this.width){
42584             this.el.setWidth(this.width);
42585         }
42586     }
42587 });
42588
42589
42590 /**
42591  * @class Roo.form.Row
42592  * @extends Roo.form.Layout
42593  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42594  * @constructor
42595  * @param {Object} config Configuration options
42596  */
42597
42598  
42599 Roo.form.Row = function(config){
42600     Roo.form.Row.superclass.constructor.call(this, config);
42601 };
42602  
42603 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42604       /**
42605      * @cfg {Number/String} width
42606      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42607      */
42608     /**
42609      * @cfg {Number/String} height
42610      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42611      */
42612     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42613     
42614     padWidth : 20,
42615     // private
42616     onRender : function(ct, position){
42617         //console.log('row render');
42618         if(!this.rowTpl){
42619             var t = new Roo.Template(
42620                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42621                     '<label for="{0}" style="{2}">{1}{4}</label>',
42622                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42623                     '</div>',
42624                 '</div>'
42625             );
42626             t.disableFormats = true;
42627             t.compile();
42628             Roo.form.Layout.prototype.rowTpl = t;
42629         }
42630         this.fieldTpl = this.rowTpl;
42631         
42632         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42633         var labelWidth = 100;
42634         
42635         if ((this.labelAlign != 'top')) {
42636             if (typeof this.labelWidth == 'number') {
42637                 labelWidth = this.labelWidth
42638             }
42639             this.padWidth =  20 + labelWidth;
42640             
42641         }
42642         
42643         Roo.form.Column.superclass.onRender.call(this, ct, position);
42644         if(this.width){
42645             this.el.setWidth(this.width);
42646         }
42647         if(this.height){
42648             this.el.setHeight(this.height);
42649         }
42650     },
42651     
42652     // private
42653     renderField : function(f){
42654         f.fieldEl = this.fieldTpl.append(this.el, [
42655                f.id, f.fieldLabel,
42656                f.labelStyle||this.labelStyle||'',
42657                this.elementStyle||'',
42658                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42659                f.itemCls||this.itemCls||'',
42660                f.width ? f.width + this.padWidth : 160 + this.padWidth
42661        ],true);
42662     }
42663 });
42664  
42665
42666 /**
42667  * @class Roo.form.FieldSet
42668  * @extends Roo.form.Layout
42669  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42670  * @constructor
42671  * @param {Object} config Configuration options
42672  */
42673 Roo.form.FieldSet = function(config){
42674     Roo.form.FieldSet.superclass.constructor.call(this, config);
42675 };
42676
42677 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42678     /**
42679      * @cfg {String} legend
42680      * The text to display as the legend for the FieldSet (defaults to '')
42681      */
42682     /**
42683      * @cfg {String/Object} autoCreate
42684      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42685      */
42686
42687     // private
42688     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42689
42690     // private
42691     onRender : function(ct, position){
42692         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42693         if(this.legend){
42694             this.setLegend(this.legend);
42695         }
42696     },
42697
42698     // private
42699     setLegend : function(text){
42700         if(this.rendered){
42701             this.el.child('legend').update(text);
42702         }
42703     }
42704 });/*
42705  * Based on:
42706  * Ext JS Library 1.1.1
42707  * Copyright(c) 2006-2007, Ext JS, LLC.
42708  *
42709  * Originally Released Under LGPL - original licence link has changed is not relivant.
42710  *
42711  * Fork - LGPL
42712  * <script type="text/javascript">
42713  */
42714 /**
42715  * @class Roo.form.VTypes
42716  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42717  * @singleton
42718  */
42719 Roo.form.VTypes = function(){
42720     // closure these in so they are only created once.
42721     var alpha = /^[a-zA-Z_]+$/;
42722     var alphanum = /^[a-zA-Z0-9_]+$/;
42723     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42724     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42725
42726     // All these messages and functions are configurable
42727     return {
42728         /**
42729          * The function used to validate email addresses
42730          * @param {String} value The email address
42731          */
42732         'email' : function(v){
42733             return email.test(v);
42734         },
42735         /**
42736          * The error text to display when the email validation function returns false
42737          * @type String
42738          */
42739         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42740         /**
42741          * The keystroke filter mask to be applied on email input
42742          * @type RegExp
42743          */
42744         'emailMask' : /[a-z0-9_\.\-@]/i,
42745
42746         /**
42747          * The function used to validate URLs
42748          * @param {String} value The URL
42749          */
42750         'url' : function(v){
42751             return url.test(v);
42752         },
42753         /**
42754          * The error text to display when the url validation function returns false
42755          * @type String
42756          */
42757         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42758         
42759         /**
42760          * The function used to validate alpha values
42761          * @param {String} value The value
42762          */
42763         'alpha' : function(v){
42764             return alpha.test(v);
42765         },
42766         /**
42767          * The error text to display when the alpha validation function returns false
42768          * @type String
42769          */
42770         'alphaText' : 'This field should only contain letters and _',
42771         /**
42772          * The keystroke filter mask to be applied on alpha input
42773          * @type RegExp
42774          */
42775         'alphaMask' : /[a-z_]/i,
42776
42777         /**
42778          * The function used to validate alphanumeric values
42779          * @param {String} value The value
42780          */
42781         'alphanum' : function(v){
42782             return alphanum.test(v);
42783         },
42784         /**
42785          * The error text to display when the alphanumeric validation function returns false
42786          * @type String
42787          */
42788         'alphanumText' : 'This field should only contain letters, numbers and _',
42789         /**
42790          * The keystroke filter mask to be applied on alphanumeric input
42791          * @type RegExp
42792          */
42793         'alphanumMask' : /[a-z0-9_]/i
42794     };
42795 }();//<script type="text/javascript">
42796
42797 /**
42798  * @class Roo.form.FCKeditor
42799  * @extends Roo.form.TextArea
42800  * Wrapper around the FCKEditor http://www.fckeditor.net
42801  * @constructor
42802  * Creates a new FCKeditor
42803  * @param {Object} config Configuration options
42804  */
42805 Roo.form.FCKeditor = function(config){
42806     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42807     this.addEvents({
42808          /**
42809          * @event editorinit
42810          * Fired when the editor is initialized - you can add extra handlers here..
42811          * @param {FCKeditor} this
42812          * @param {Object} the FCK object.
42813          */
42814         editorinit : true
42815     });
42816     
42817     
42818 };
42819 Roo.form.FCKeditor.editors = { };
42820 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42821 {
42822     //defaultAutoCreate : {
42823     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42824     //},
42825     // private
42826     /**
42827      * @cfg {Object} fck options - see fck manual for details.
42828      */
42829     fckconfig : false,
42830     
42831     /**
42832      * @cfg {Object} fck toolbar set (Basic or Default)
42833      */
42834     toolbarSet : 'Basic',
42835     /**
42836      * @cfg {Object} fck BasePath
42837      */ 
42838     basePath : '/fckeditor/',
42839     
42840     
42841     frame : false,
42842     
42843     value : '',
42844     
42845    
42846     onRender : function(ct, position)
42847     {
42848         if(!this.el){
42849             this.defaultAutoCreate = {
42850                 tag: "textarea",
42851                 style:"width:300px;height:60px;",
42852                 autocomplete: "off"
42853             };
42854         }
42855         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42856         /*
42857         if(this.grow){
42858             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42859             if(this.preventScrollbars){
42860                 this.el.setStyle("overflow", "hidden");
42861             }
42862             this.el.setHeight(this.growMin);
42863         }
42864         */
42865         //console.log('onrender' + this.getId() );
42866         Roo.form.FCKeditor.editors[this.getId()] = this;
42867          
42868
42869         this.replaceTextarea() ;
42870         
42871     },
42872     
42873     getEditor : function() {
42874         return this.fckEditor;
42875     },
42876     /**
42877      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42878      * @param {Mixed} value The value to set
42879      */
42880     
42881     
42882     setValue : function(value)
42883     {
42884         //console.log('setValue: ' + value);
42885         
42886         if(typeof(value) == 'undefined') { // not sure why this is happending...
42887             return;
42888         }
42889         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42890         
42891         //if(!this.el || !this.getEditor()) {
42892         //    this.value = value;
42893             //this.setValue.defer(100,this,[value]);    
42894         //    return;
42895         //} 
42896         
42897         if(!this.getEditor()) {
42898             return;
42899         }
42900         
42901         this.getEditor().SetData(value);
42902         
42903         //
42904
42905     },
42906
42907     /**
42908      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42909      * @return {Mixed} value The field value
42910      */
42911     getValue : function()
42912     {
42913         
42914         if (this.frame && this.frame.dom.style.display == 'none') {
42915             return Roo.form.FCKeditor.superclass.getValue.call(this);
42916         }
42917         
42918         if(!this.el || !this.getEditor()) {
42919            
42920            // this.getValue.defer(100,this); 
42921             return this.value;
42922         }
42923        
42924         
42925         var value=this.getEditor().GetData();
42926         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42927         return Roo.form.FCKeditor.superclass.getValue.call(this);
42928         
42929
42930     },
42931
42932     /**
42933      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42934      * @return {Mixed} value The field value
42935      */
42936     getRawValue : function()
42937     {
42938         if (this.frame && this.frame.dom.style.display == 'none') {
42939             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42940         }
42941         
42942         if(!this.el || !this.getEditor()) {
42943             //this.getRawValue.defer(100,this); 
42944             return this.value;
42945             return;
42946         }
42947         
42948         
42949         
42950         var value=this.getEditor().GetData();
42951         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42952         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42953          
42954     },
42955     
42956     setSize : function(w,h) {
42957         
42958         
42959         
42960         //if (this.frame && this.frame.dom.style.display == 'none') {
42961         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42962         //    return;
42963         //}
42964         //if(!this.el || !this.getEditor()) {
42965         //    this.setSize.defer(100,this, [w,h]); 
42966         //    return;
42967         //}
42968         
42969         
42970         
42971         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42972         
42973         this.frame.dom.setAttribute('width', w);
42974         this.frame.dom.setAttribute('height', h);
42975         this.frame.setSize(w,h);
42976         
42977     },
42978     
42979     toggleSourceEdit : function(value) {
42980         
42981       
42982          
42983         this.el.dom.style.display = value ? '' : 'none';
42984         this.frame.dom.style.display = value ?  'none' : '';
42985         
42986     },
42987     
42988     
42989     focus: function(tag)
42990     {
42991         if (this.frame.dom.style.display == 'none') {
42992             return Roo.form.FCKeditor.superclass.focus.call(this);
42993         }
42994         if(!this.el || !this.getEditor()) {
42995             this.focus.defer(100,this, [tag]); 
42996             return;
42997         }
42998         
42999         
43000         
43001         
43002         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43003         this.getEditor().Focus();
43004         if (tgs.length) {
43005             if (!this.getEditor().Selection.GetSelection()) {
43006                 this.focus.defer(100,this, [tag]); 
43007                 return;
43008             }
43009             
43010             
43011             var r = this.getEditor().EditorDocument.createRange();
43012             r.setStart(tgs[0],0);
43013             r.setEnd(tgs[0],0);
43014             this.getEditor().Selection.GetSelection().removeAllRanges();
43015             this.getEditor().Selection.GetSelection().addRange(r);
43016             this.getEditor().Focus();
43017         }
43018         
43019     },
43020     
43021     
43022     
43023     replaceTextarea : function()
43024     {
43025         if ( document.getElementById( this.getId() + '___Frame' ) )
43026             return ;
43027         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43028         //{
43029             // We must check the elements firstly using the Id and then the name.
43030         var oTextarea = document.getElementById( this.getId() );
43031         
43032         var colElementsByName = document.getElementsByName( this.getId() ) ;
43033          
43034         oTextarea.style.display = 'none' ;
43035
43036         if ( oTextarea.tabIndex ) {            
43037             this.TabIndex = oTextarea.tabIndex ;
43038         }
43039         
43040         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43041         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43042         this.frame = Roo.get(this.getId() + '___Frame')
43043     },
43044     
43045     _getConfigHtml : function()
43046     {
43047         var sConfig = '' ;
43048
43049         for ( var o in this.fckconfig ) {
43050             sConfig += sConfig.length > 0  ? '&amp;' : '';
43051             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43052         }
43053
43054         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43055     },
43056     
43057     
43058     _getIFrameHtml : function()
43059     {
43060         var sFile = 'fckeditor.html' ;
43061         /* no idea what this is about..
43062         try
43063         {
43064             if ( (/fcksource=true/i).test( window.top.location.search ) )
43065                 sFile = 'fckeditor.original.html' ;
43066         }
43067         catch (e) { 
43068         */
43069
43070         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43071         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43072         
43073         
43074         var html = '<iframe id="' + this.getId() +
43075             '___Frame" src="' + sLink +
43076             '" width="' + this.width +
43077             '" height="' + this.height + '"' +
43078             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43079             ' frameborder="0" scrolling="no"></iframe>' ;
43080
43081         return html ;
43082     },
43083     
43084     _insertHtmlBefore : function( html, element )
43085     {
43086         if ( element.insertAdjacentHTML )       {
43087             // IE
43088             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43089         } else { // Gecko
43090             var oRange = document.createRange() ;
43091             oRange.setStartBefore( element ) ;
43092             var oFragment = oRange.createContextualFragment( html );
43093             element.parentNode.insertBefore( oFragment, element ) ;
43094         }
43095     }
43096     
43097     
43098   
43099     
43100     
43101     
43102     
43103
43104 });
43105
43106 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43107
43108 function FCKeditor_OnComplete(editorInstance){
43109     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43110     f.fckEditor = editorInstance;
43111     //console.log("loaded");
43112     f.fireEvent('editorinit', f, editorInstance);
43113
43114   
43115
43116  
43117
43118
43119
43120
43121
43122
43123
43124
43125
43126
43127
43128
43129
43130
43131
43132 //<script type="text/javascript">
43133 /**
43134  * @class Roo.form.GridField
43135  * @extends Roo.form.Field
43136  * Embed a grid (or editable grid into a form)
43137  * STATUS ALPHA
43138  * 
43139  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43140  * it needs 
43141  * xgrid.store = Roo.data.Store
43142  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43143  * xgrid.store.reader = Roo.data.JsonReader 
43144  * 
43145  * 
43146  * @constructor
43147  * Creates a new GridField
43148  * @param {Object} config Configuration options
43149  */
43150 Roo.form.GridField = function(config){
43151     Roo.form.GridField.superclass.constructor.call(this, config);
43152      
43153 };
43154
43155 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43156     /**
43157      * @cfg {Number} width  - used to restrict width of grid..
43158      */
43159     width : 100,
43160     /**
43161      * @cfg {Number} height - used to restrict height of grid..
43162      */
43163     height : 50,
43164      /**
43165      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43166          * 
43167          *}
43168      */
43169     xgrid : false, 
43170     /**
43171      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43172      * {tag: "input", type: "checkbox", autocomplete: "off"})
43173      */
43174    // defaultAutoCreate : { tag: 'div' },
43175     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43176     /**
43177      * @cfg {String} addTitle Text to include for adding a title.
43178      */
43179     addTitle : false,
43180     //
43181     onResize : function(){
43182         Roo.form.Field.superclass.onResize.apply(this, arguments);
43183     },
43184
43185     initEvents : function(){
43186         // Roo.form.Checkbox.superclass.initEvents.call(this);
43187         // has no events...
43188        
43189     },
43190
43191
43192     getResizeEl : function(){
43193         return this.wrap;
43194     },
43195
43196     getPositionEl : function(){
43197         return this.wrap;
43198     },
43199
43200     // private
43201     onRender : function(ct, position){
43202         
43203         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43204         var style = this.style;
43205         delete this.style;
43206         
43207         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43208         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43209         this.viewEl = this.wrap.createChild({ tag: 'div' });
43210         if (style) {
43211             this.viewEl.applyStyles(style);
43212         }
43213         if (this.width) {
43214             this.viewEl.setWidth(this.width);
43215         }
43216         if (this.height) {
43217             this.viewEl.setHeight(this.height);
43218         }
43219         //if(this.inputValue !== undefined){
43220         //this.setValue(this.value);
43221         
43222         
43223         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43224         
43225         
43226         this.grid.render();
43227         this.grid.getDataSource().on('remove', this.refreshValue, this);
43228         this.grid.getDataSource().on('update', this.refreshValue, this);
43229         this.grid.on('afteredit', this.refreshValue, this);
43230  
43231     },
43232      
43233     
43234     /**
43235      * Sets the value of the item. 
43236      * @param {String} either an object  or a string..
43237      */
43238     setValue : function(v){
43239         //this.value = v;
43240         v = v || []; // empty set..
43241         // this does not seem smart - it really only affects memoryproxy grids..
43242         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43243             var ds = this.grid.getDataSource();
43244             // assumes a json reader..
43245             var data = {}
43246             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43247             ds.loadData( data);
43248         }
43249         // clear selection so it does not get stale.
43250         if (this.grid.sm) { 
43251             this.grid.sm.clearSelections();
43252         }
43253         
43254         Roo.form.GridField.superclass.setValue.call(this, v);
43255         this.refreshValue();
43256         // should load data in the grid really....
43257     },
43258     
43259     // private
43260     refreshValue: function() {
43261          var val = [];
43262         this.grid.getDataSource().each(function(r) {
43263             val.push(r.data);
43264         });
43265         this.el.dom.value = Roo.encode(val);
43266     }
43267     
43268      
43269     
43270     
43271 });/*
43272  * Based on:
43273  * Ext JS Library 1.1.1
43274  * Copyright(c) 2006-2007, Ext JS, LLC.
43275  *
43276  * Originally Released Under LGPL - original licence link has changed is not relivant.
43277  *
43278  * Fork - LGPL
43279  * <script type="text/javascript">
43280  */
43281 /**
43282  * @class Roo.form.DisplayField
43283  * @extends Roo.form.Field
43284  * A generic Field to display non-editable data.
43285  * @constructor
43286  * Creates a new Display Field item.
43287  * @param {Object} config Configuration options
43288  */
43289 Roo.form.DisplayField = function(config){
43290     Roo.form.DisplayField.superclass.constructor.call(this, config);
43291     
43292 };
43293
43294 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43295     inputType:      'hidden',
43296     allowBlank:     true,
43297     readOnly:         true,
43298     
43299  
43300     /**
43301      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43302      */
43303     focusClass : undefined,
43304     /**
43305      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43306      */
43307     fieldClass: 'x-form-field',
43308     
43309      /**
43310      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43311      */
43312     valueRenderer: undefined,
43313     
43314     width: 100,
43315     /**
43316      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43317      * {tag: "input", type: "checkbox", autocomplete: "off"})
43318      */
43319      
43320  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43321
43322     onResize : function(){
43323         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43324         
43325     },
43326
43327     initEvents : function(){
43328         // Roo.form.Checkbox.superclass.initEvents.call(this);
43329         // has no events...
43330        
43331     },
43332
43333
43334     getResizeEl : function(){
43335         return this.wrap;
43336     },
43337
43338     getPositionEl : function(){
43339         return this.wrap;
43340     },
43341
43342     // private
43343     onRender : function(ct, position){
43344         
43345         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43346         //if(this.inputValue !== undefined){
43347         this.wrap = this.el.wrap();
43348         
43349         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43350         
43351         if (this.bodyStyle) {
43352             this.viewEl.applyStyles(this.bodyStyle);
43353         }
43354         //this.viewEl.setStyle('padding', '2px');
43355         
43356         this.setValue(this.value);
43357         
43358     },
43359 /*
43360     // private
43361     initValue : Roo.emptyFn,
43362
43363   */
43364
43365         // private
43366     onClick : function(){
43367         
43368     },
43369
43370     /**
43371      * Sets the checked state of the checkbox.
43372      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43373      */
43374     setValue : function(v){
43375         this.value = v;
43376         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43377         // this might be called before we have a dom element..
43378         if (!this.viewEl) {
43379             return;
43380         }
43381         this.viewEl.dom.innerHTML = html;
43382         Roo.form.DisplayField.superclass.setValue.call(this, v);
43383
43384     }
43385 });/*
43386  * 
43387  * Licence- LGPL
43388  * 
43389  */
43390
43391 /**
43392  * @class Roo.form.DayPicker
43393  * @extends Roo.form.Field
43394  * A Day picker show [M] [T] [W] ....
43395  * @constructor
43396  * Creates a new Day Picker
43397  * @param {Object} config Configuration options
43398  */
43399 Roo.form.DayPicker= function(config){
43400     Roo.form.DayPicker.superclass.constructor.call(this, config);
43401      
43402 };
43403
43404 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43405     /**
43406      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43407      */
43408     focusClass : undefined,
43409     /**
43410      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43411      */
43412     fieldClass: "x-form-field",
43413    
43414     /**
43415      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43416      * {tag: "input", type: "checkbox", autocomplete: "off"})
43417      */
43418     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43419     
43420    
43421     actionMode : 'viewEl', 
43422     //
43423     // private
43424  
43425     inputType : 'hidden',
43426     
43427      
43428     inputElement: false, // real input element?
43429     basedOn: false, // ????
43430     
43431     isFormField: true, // not sure where this is needed!!!!
43432
43433     onResize : function(){
43434         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43435         if(!this.boxLabel){
43436             this.el.alignTo(this.wrap, 'c-c');
43437         }
43438     },
43439
43440     initEvents : function(){
43441         Roo.form.Checkbox.superclass.initEvents.call(this);
43442         this.el.on("click", this.onClick,  this);
43443         this.el.on("change", this.onClick,  this);
43444     },
43445
43446
43447     getResizeEl : function(){
43448         return this.wrap;
43449     },
43450
43451     getPositionEl : function(){
43452         return this.wrap;
43453     },
43454
43455     
43456     // private
43457     onRender : function(ct, position){
43458         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43459        
43460         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43461         
43462         var r1 = '<table><tr>';
43463         var r2 = '<tr class="x-form-daypick-icons">';
43464         for (var i=0; i < 7; i++) {
43465             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43466             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43467         }
43468         
43469         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43470         viewEl.select('img').on('click', this.onClick, this);
43471         this.viewEl = viewEl;   
43472         
43473         
43474         // this will not work on Chrome!!!
43475         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43476         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43477         
43478         
43479           
43480
43481     },
43482
43483     // private
43484     initValue : Roo.emptyFn,
43485
43486     /**
43487      * Returns the checked state of the checkbox.
43488      * @return {Boolean} True if checked, else false
43489      */
43490     getValue : function(){
43491         return this.el.dom.value;
43492         
43493     },
43494
43495         // private
43496     onClick : function(e){ 
43497         //this.setChecked(!this.checked);
43498         Roo.get(e.target).toggleClass('x-menu-item-checked');
43499         this.refreshValue();
43500         //if(this.el.dom.checked != this.checked){
43501         //    this.setValue(this.el.dom.checked);
43502        // }
43503     },
43504     
43505     // private
43506     refreshValue : function()
43507     {
43508         var val = '';
43509         this.viewEl.select('img',true).each(function(e,i,n)  {
43510             val += e.is(".x-menu-item-checked") ? String(n) : '';
43511         });
43512         this.setValue(val, true);
43513     },
43514
43515     /**
43516      * Sets the checked state of the checkbox.
43517      * On is always based on a string comparison between inputValue and the param.
43518      * @param {Boolean/String} value - the value to set 
43519      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43520      */
43521     setValue : function(v,suppressEvent){
43522         if (!this.el.dom) {
43523             return;
43524         }
43525         var old = this.el.dom.value ;
43526         this.el.dom.value = v;
43527         if (suppressEvent) {
43528             return ;
43529         }
43530          
43531         // update display..
43532         this.viewEl.select('img',true).each(function(e,i,n)  {
43533             
43534             var on = e.is(".x-menu-item-checked");
43535             var newv = v.indexOf(String(n)) > -1;
43536             if (on != newv) {
43537                 e.toggleClass('x-menu-item-checked');
43538             }
43539             
43540         });
43541         
43542         
43543         this.fireEvent('change', this, v, old);
43544         
43545         
43546     },
43547    
43548     // handle setting of hidden value by some other method!!?!?
43549     setFromHidden: function()
43550     {
43551         if(!this.el){
43552             return;
43553         }
43554         //console.log("SET FROM HIDDEN");
43555         //alert('setFrom hidden');
43556         this.setValue(this.el.dom.value);
43557     },
43558     
43559     onDestroy : function()
43560     {
43561         if(this.viewEl){
43562             Roo.get(this.viewEl).remove();
43563         }
43564          
43565         Roo.form.DayPicker.superclass.onDestroy.call(this);
43566     }
43567
43568 });/*
43569  * RooJS Library 1.1.1
43570  * Copyright(c) 2008-2011  Alan Knowles
43571  *
43572  * License - LGPL
43573  */
43574  
43575
43576 /**
43577  * @class Roo.form.ComboCheck
43578  * @extends Roo.form.ComboBox
43579  * A combobox for multiple select items.
43580  *
43581  * FIXME - could do with a reset button..
43582  * 
43583  * @constructor
43584  * Create a new ComboCheck
43585  * @param {Object} config Configuration options
43586  */
43587 Roo.form.ComboCheck = function(config){
43588     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43589     // should verify some data...
43590     // like
43591     // hiddenName = required..
43592     // displayField = required
43593     // valudField == required
43594     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43595     var _t = this;
43596     Roo.each(req, function(e) {
43597         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43598             throw "Roo.form.ComboCheck : missing value for: " + e;
43599         }
43600     });
43601     
43602     
43603 };
43604
43605 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43606      
43607      
43608     editable : false,
43609      
43610     selectedClass: 'x-menu-item-checked', 
43611     
43612     // private
43613     onRender : function(ct, position){
43614         var _t = this;
43615         
43616         
43617         
43618         if(!this.tpl){
43619             var cls = 'x-combo-list';
43620
43621             
43622             this.tpl =  new Roo.Template({
43623                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43624                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43625                    '<span>{' + this.displayField + '}</span>' +
43626                     '</div>' 
43627                 
43628             });
43629         }
43630  
43631         
43632         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43633         this.view.singleSelect = false;
43634         this.view.multiSelect = true;
43635         this.view.toggleSelect = true;
43636         this.pageTb.add(new Roo.Toolbar.Fill(), {
43637             
43638             text: 'Done',
43639             handler: function()
43640             {
43641                 _t.collapse();
43642             }
43643         });
43644     },
43645     
43646     onViewOver : function(e, t){
43647         // do nothing...
43648         return;
43649         
43650     },
43651     
43652     onViewClick : function(doFocus,index){
43653         return;
43654         
43655     },
43656     select: function () {
43657         //Roo.log("SELECT CALLED");
43658     },
43659      
43660     selectByValue : function(xv, scrollIntoView){
43661         var ar = this.getValueArray();
43662         var sels = [];
43663         
43664         Roo.each(ar, function(v) {
43665             if(v === undefined || v === null){
43666                 return;
43667             }
43668             var r = this.findRecord(this.valueField, v);
43669             if(r){
43670                 sels.push(this.store.indexOf(r))
43671                 
43672             }
43673         },this);
43674         this.view.select(sels);
43675         return false;
43676     },
43677     
43678     
43679     
43680     onSelect : function(record, index){
43681        // Roo.log("onselect Called");
43682        // this is only called by the clear button now..
43683         this.view.clearSelections();
43684         this.setValue('[]');
43685         if (this.value != this.valueBefore) {
43686             this.fireEvent('change', this, this.value, this.valueBefore);
43687         }
43688     },
43689     getValueArray : function()
43690     {
43691         var ar = [] ;
43692         
43693         try {
43694             Roo.log(this.value);
43695             var ar = Roo.decode(this.value);
43696             return  ar instanceof Array ? ar : []; //?? valid?
43697             
43698         } catch(e) {
43699             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43700             return [];
43701         }
43702          
43703     },
43704     expand : function ()
43705     {
43706         Roo.form.ComboCheck.superclass.expand.call(this);
43707         this.valueBefore = this.value;
43708         
43709
43710     },
43711     
43712     collapse : function(){
43713         Roo.form.ComboCheck.superclass.collapse.call(this);
43714         var sl = this.view.getSelectedIndexes();
43715         var st = this.store;
43716         var nv = [];
43717         var tv = [];
43718         var r;
43719         Roo.each(sl, function(i) {
43720             r = st.getAt(i);
43721             nv.push(r.get(this.valueField));
43722         },this);
43723         this.setValue(Roo.encode(nv));
43724         if (this.value != this.valueBefore) {
43725
43726             this.fireEvent('change', this, this.value, this.valueBefore);
43727         }
43728         
43729     },
43730     
43731     setValue : function(v){
43732         // Roo.log(v);
43733         this.value = v;
43734         
43735         var vals = this.getValueArray();
43736         var tv = [];
43737         Roo.each(vals, function(k) {
43738             var r = this.findRecord(this.valueField, k);
43739             if(r){
43740                 tv.push(r.data[this.displayField]);
43741             }else if(this.valueNotFoundText !== undefined){
43742                 tv.push( this.valueNotFoundText );
43743             }
43744         },this);
43745        // Roo.log(tv);
43746         
43747         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43748         this.hiddenField.value = v;
43749         this.value = v;
43750     }
43751     
43752 });//<script type="text/javasscript">
43753  
43754
43755 /**
43756  * @class Roo.DDView
43757  * A DnD enabled version of Roo.View.
43758  * @param {Element/String} container The Element in which to create the View.
43759  * @param {String} tpl The template string used to create the markup for each element of the View
43760  * @param {Object} config The configuration properties. These include all the config options of
43761  * {@link Roo.View} plus some specific to this class.<br>
43762  * <p>
43763  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43764  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43765  * <p>
43766  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43767 .x-view-drag-insert-above {
43768         border-top:1px dotted #3366cc;
43769 }
43770 .x-view-drag-insert-below {
43771         border-bottom:1px dotted #3366cc;
43772 }
43773 </code></pre>
43774  * 
43775  */
43776  
43777 Roo.DDView = function(container, tpl, config) {
43778     Roo.DDView.superclass.constructor.apply(this, arguments);
43779     this.getEl().setStyle("outline", "0px none");
43780     this.getEl().unselectable();
43781     if (this.dragGroup) {
43782                 this.setDraggable(this.dragGroup.split(","));
43783     }
43784     if (this.dropGroup) {
43785                 this.setDroppable(this.dropGroup.split(","));
43786     }
43787     if (this.deletable) {
43788         this.setDeletable();
43789     }
43790     this.isDirtyFlag = false;
43791         this.addEvents({
43792                 "drop" : true
43793         });
43794 };
43795
43796 Roo.extend(Roo.DDView, Roo.View, {
43797 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43798 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43799 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43800 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43801
43802         isFormField: true,
43803
43804         reset: Roo.emptyFn,
43805         
43806         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43807
43808         validate: function() {
43809                 return true;
43810         },
43811         
43812         destroy: function() {
43813                 this.purgeListeners();
43814                 this.getEl.removeAllListeners();
43815                 this.getEl().remove();
43816                 if (this.dragZone) {
43817                         if (this.dragZone.destroy) {
43818                                 this.dragZone.destroy();
43819                         }
43820                 }
43821                 if (this.dropZone) {
43822                         if (this.dropZone.destroy) {
43823                                 this.dropZone.destroy();
43824                         }
43825                 }
43826         },
43827
43828 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43829         getName: function() {
43830                 return this.name;
43831         },
43832
43833 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43834         setValue: function(v) {
43835                 if (!this.store) {
43836                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43837                 }
43838                 var data = {};
43839                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43840                 this.store.proxy = new Roo.data.MemoryProxy(data);
43841                 this.store.load();
43842         },
43843
43844 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43845         getValue: function() {
43846                 var result = '(';
43847                 this.store.each(function(rec) {
43848                         result += rec.id + ',';
43849                 });
43850                 return result.substr(0, result.length - 1) + ')';
43851         },
43852         
43853         getIds: function() {
43854                 var i = 0, result = new Array(this.store.getCount());
43855                 this.store.each(function(rec) {
43856                         result[i++] = rec.id;
43857                 });
43858                 return result;
43859         },
43860         
43861         isDirty: function() {
43862                 return this.isDirtyFlag;
43863         },
43864
43865 /**
43866  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43867  *      whole Element becomes the target, and this causes the drop gesture to append.
43868  */
43869     getTargetFromEvent : function(e) {
43870                 var target = e.getTarget();
43871                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43872                 target = target.parentNode;
43873                 }
43874                 if (!target) {
43875                         target = this.el.dom.lastChild || this.el.dom;
43876                 }
43877                 return target;
43878     },
43879
43880 /**
43881  *      Create the drag data which consists of an object which has the property "ddel" as
43882  *      the drag proxy element. 
43883  */
43884     getDragData : function(e) {
43885         var target = this.findItemFromChild(e.getTarget());
43886                 if(target) {
43887                         this.handleSelection(e);
43888                         var selNodes = this.getSelectedNodes();
43889             var dragData = {
43890                 source: this,
43891                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43892                 nodes: selNodes,
43893                 records: []
43894                         };
43895                         var selectedIndices = this.getSelectedIndexes();
43896                         for (var i = 0; i < selectedIndices.length; i++) {
43897                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43898                         }
43899                         if (selNodes.length == 1) {
43900                                 dragData.ddel = target.cloneNode(true); // the div element
43901                         } else {
43902                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43903                                 div.className = 'multi-proxy';
43904                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43905                                         div.appendChild(selNodes[i].cloneNode(true));
43906                                 }
43907                                 dragData.ddel = div;
43908                         }
43909             //console.log(dragData)
43910             //console.log(dragData.ddel.innerHTML)
43911                         return dragData;
43912                 }
43913         //console.log('nodragData')
43914                 return false;
43915     },
43916     
43917 /**     Specify to which ddGroup items in this DDView may be dragged. */
43918     setDraggable: function(ddGroup) {
43919         if (ddGroup instanceof Array) {
43920                 Roo.each(ddGroup, this.setDraggable, this);
43921                 return;
43922         }
43923         if (this.dragZone) {
43924                 this.dragZone.addToGroup(ddGroup);
43925         } else {
43926                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43927                                 containerScroll: true,
43928                                 ddGroup: ddGroup 
43929
43930                         });
43931 //                      Draggability implies selection. DragZone's mousedown selects the element.
43932                         if (!this.multiSelect) { this.singleSelect = true; }
43933
43934 //                      Wire the DragZone's handlers up to methods in *this*
43935                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43936                 }
43937     },
43938
43939 /**     Specify from which ddGroup this DDView accepts drops. */
43940     setDroppable: function(ddGroup) {
43941         if (ddGroup instanceof Array) {
43942                 Roo.each(ddGroup, this.setDroppable, this);
43943                 return;
43944         }
43945         if (this.dropZone) {
43946                 this.dropZone.addToGroup(ddGroup);
43947         } else {
43948                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43949                                 containerScroll: true,
43950                                 ddGroup: ddGroup
43951                         });
43952
43953 //                      Wire the DropZone's handlers up to methods in *this*
43954                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43955                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43956                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43957                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43958                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43959                 }
43960     },
43961
43962 /**     Decide whether to drop above or below a View node. */
43963     getDropPoint : function(e, n, dd){
43964         if (n == this.el.dom) { return "above"; }
43965                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43966                 var c = t + (b - t) / 2;
43967                 var y = Roo.lib.Event.getPageY(e);
43968                 if(y <= c) {
43969                         return "above";
43970                 }else{
43971                         return "below";
43972                 }
43973     },
43974
43975     onNodeEnter : function(n, dd, e, data){
43976                 return false;
43977     },
43978     
43979     onNodeOver : function(n, dd, e, data){
43980                 var pt = this.getDropPoint(e, n, dd);
43981                 // set the insert point style on the target node
43982                 var dragElClass = this.dropNotAllowed;
43983                 if (pt) {
43984                         var targetElClass;
43985                         if (pt == "above"){
43986                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43987                                 targetElClass = "x-view-drag-insert-above";
43988                         } else {
43989                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43990                                 targetElClass = "x-view-drag-insert-below";
43991                         }
43992                         if (this.lastInsertClass != targetElClass){
43993                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43994                                 this.lastInsertClass = targetElClass;
43995                         }
43996                 }
43997                 return dragElClass;
43998         },
43999
44000     onNodeOut : function(n, dd, e, data){
44001                 this.removeDropIndicators(n);
44002     },
44003
44004     onNodeDrop : function(n, dd, e, data){
44005         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44006                 return false;
44007         }
44008         var pt = this.getDropPoint(e, n, dd);
44009                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44010                 if (pt == "below") { insertAt++; }
44011                 for (var i = 0; i < data.records.length; i++) {
44012                         var r = data.records[i];
44013                         var dup = this.store.getById(r.id);
44014                         if (dup && (dd != this.dragZone)) {
44015                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44016                         } else {
44017                                 if (data.copy) {
44018                                         this.store.insert(insertAt++, r.copy());
44019                                 } else {
44020                                         data.source.isDirtyFlag = true;
44021                                         r.store.remove(r);
44022                                         this.store.insert(insertAt++, r);
44023                                 }
44024                                 this.isDirtyFlag = true;
44025                         }
44026                 }
44027                 this.dragZone.cachedTarget = null;
44028                 return true;
44029     },
44030
44031     removeDropIndicators : function(n){
44032                 if(n){
44033                         Roo.fly(n).removeClass([
44034                                 "x-view-drag-insert-above",
44035                                 "x-view-drag-insert-below"]);
44036                         this.lastInsertClass = "_noclass";
44037                 }
44038     },
44039
44040 /**
44041  *      Utility method. Add a delete option to the DDView's context menu.
44042  *      @param {String} imageUrl The URL of the "delete" icon image.
44043  */
44044         setDeletable: function(imageUrl) {
44045                 if (!this.singleSelect && !this.multiSelect) {
44046                         this.singleSelect = true;
44047                 }
44048                 var c = this.getContextMenu();
44049                 this.contextMenu.on("itemclick", function(item) {
44050                         switch (item.id) {
44051                                 case "delete":
44052                                         this.remove(this.getSelectedIndexes());
44053                                         break;
44054                         }
44055                 }, this);
44056                 this.contextMenu.add({
44057                         icon: imageUrl,
44058                         id: "delete",
44059                         text: 'Delete'
44060                 });
44061         },
44062         
44063 /**     Return the context menu for this DDView. */
44064         getContextMenu: function() {
44065                 if (!this.contextMenu) {
44066 //                      Create the View's context menu
44067                         this.contextMenu = new Roo.menu.Menu({
44068                                 id: this.id + "-contextmenu"
44069                         });
44070                         this.el.on("contextmenu", this.showContextMenu, this);
44071                 }
44072                 return this.contextMenu;
44073         },
44074         
44075         disableContextMenu: function() {
44076                 if (this.contextMenu) {
44077                         this.el.un("contextmenu", this.showContextMenu, this);
44078                 }
44079         },
44080
44081         showContextMenu: function(e, item) {
44082         item = this.findItemFromChild(e.getTarget());
44083                 if (item) {
44084                         e.stopEvent();
44085                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44086                         this.contextMenu.showAt(e.getXY());
44087             }
44088     },
44089
44090 /**
44091  *      Remove {@link Roo.data.Record}s at the specified indices.
44092  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44093  */
44094     remove: function(selectedIndices) {
44095                 selectedIndices = [].concat(selectedIndices);
44096                 for (var i = 0; i < selectedIndices.length; i++) {
44097                         var rec = this.store.getAt(selectedIndices[i]);
44098                         this.store.remove(rec);
44099                 }
44100     },
44101
44102 /**
44103  *      Double click fires the event, but also, if this is draggable, and there is only one other
44104  *      related DropZone, it transfers the selected node.
44105  */
44106     onDblClick : function(e){
44107         var item = this.findItemFromChild(e.getTarget());
44108         if(item){
44109             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44110                 return false;
44111             }
44112             if (this.dragGroup) {
44113                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44114                     while (targets.indexOf(this.dropZone) > -1) {
44115                             targets.remove(this.dropZone);
44116                                 }
44117                     if (targets.length == 1) {
44118                                         this.dragZone.cachedTarget = null;
44119                         var el = Roo.get(targets[0].getEl());
44120                         var box = el.getBox(true);
44121                         targets[0].onNodeDrop(el.dom, {
44122                                 target: el.dom,
44123                                 xy: [box.x, box.y + box.height - 1]
44124                         }, null, this.getDragData(e));
44125                     }
44126                 }
44127         }
44128     },
44129     
44130     handleSelection: function(e) {
44131                 this.dragZone.cachedTarget = null;
44132         var item = this.findItemFromChild(e.getTarget());
44133         if (!item) {
44134                 this.clearSelections(true);
44135                 return;
44136         }
44137                 if (item && (this.multiSelect || this.singleSelect)){
44138                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44139                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44140                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44141                                 this.unselect(item);
44142                         } else {
44143                                 this.select(item, this.multiSelect && e.ctrlKey);
44144                                 this.lastSelection = item;
44145                         }
44146                 }
44147     },
44148
44149     onItemClick : function(item, index, e){
44150                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44151                         return false;
44152                 }
44153                 return true;
44154     },
44155
44156     unselect : function(nodeInfo, suppressEvent){
44157                 var node = this.getNode(nodeInfo);
44158                 if(node && this.isSelected(node)){
44159                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44160                                 Roo.fly(node).removeClass(this.selectedClass);
44161                                 this.selections.remove(node);
44162                                 if(!suppressEvent){
44163                                         this.fireEvent("selectionchange", this, this.selections);
44164                                 }
44165                         }
44166                 }
44167     }
44168 });
44169 /*
44170  * Based on:
44171  * Ext JS Library 1.1.1
44172  * Copyright(c) 2006-2007, Ext JS, LLC.
44173  *
44174  * Originally Released Under LGPL - original licence link has changed is not relivant.
44175  *
44176  * Fork - LGPL
44177  * <script type="text/javascript">
44178  */
44179  
44180 /**
44181  * @class Roo.LayoutManager
44182  * @extends Roo.util.Observable
44183  * Base class for layout managers.
44184  */
44185 Roo.LayoutManager = function(container, config){
44186     Roo.LayoutManager.superclass.constructor.call(this);
44187     this.el = Roo.get(container);
44188     // ie scrollbar fix
44189     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44190         document.body.scroll = "no";
44191     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44192         this.el.position('relative');
44193     }
44194     this.id = this.el.id;
44195     this.el.addClass("x-layout-container");
44196     /** false to disable window resize monitoring @type Boolean */
44197     this.monitorWindowResize = true;
44198     this.regions = {};
44199     this.addEvents({
44200         /**
44201          * @event layout
44202          * Fires when a layout is performed. 
44203          * @param {Roo.LayoutManager} this
44204          */
44205         "layout" : true,
44206         /**
44207          * @event regionresized
44208          * Fires when the user resizes a region. 
44209          * @param {Roo.LayoutRegion} region The resized region
44210          * @param {Number} newSize The new size (width for east/west, height for north/south)
44211          */
44212         "regionresized" : true,
44213         /**
44214          * @event regioncollapsed
44215          * Fires when a region is collapsed. 
44216          * @param {Roo.LayoutRegion} region The collapsed region
44217          */
44218         "regioncollapsed" : true,
44219         /**
44220          * @event regionexpanded
44221          * Fires when a region is expanded.  
44222          * @param {Roo.LayoutRegion} region The expanded region
44223          */
44224         "regionexpanded" : true
44225     });
44226     this.updating = false;
44227     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44228 };
44229
44230 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44231     /**
44232      * Returns true if this layout is currently being updated
44233      * @return {Boolean}
44234      */
44235     isUpdating : function(){
44236         return this.updating; 
44237     },
44238     
44239     /**
44240      * Suspend the LayoutManager from doing auto-layouts while
44241      * making multiple add or remove calls
44242      */
44243     beginUpdate : function(){
44244         this.updating = true;    
44245     },
44246     
44247     /**
44248      * Restore auto-layouts and optionally disable the manager from performing a layout
44249      * @param {Boolean} noLayout true to disable a layout update 
44250      */
44251     endUpdate : function(noLayout){
44252         this.updating = false;
44253         if(!noLayout){
44254             this.layout();
44255         }    
44256     },
44257     
44258     layout: function(){
44259         
44260     },
44261     
44262     onRegionResized : function(region, newSize){
44263         this.fireEvent("regionresized", region, newSize);
44264         this.layout();
44265     },
44266     
44267     onRegionCollapsed : function(region){
44268         this.fireEvent("regioncollapsed", region);
44269     },
44270     
44271     onRegionExpanded : function(region){
44272         this.fireEvent("regionexpanded", region);
44273     },
44274         
44275     /**
44276      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44277      * performs box-model adjustments.
44278      * @return {Object} The size as an object {width: (the width), height: (the height)}
44279      */
44280     getViewSize : function(){
44281         var size;
44282         if(this.el.dom != document.body){
44283             size = this.el.getSize();
44284         }else{
44285             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44286         }
44287         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44288         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44289         return size;
44290     },
44291     
44292     /**
44293      * Returns the Element this layout is bound to.
44294      * @return {Roo.Element}
44295      */
44296     getEl : function(){
44297         return this.el;
44298     },
44299     
44300     /**
44301      * Returns the specified region.
44302      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44303      * @return {Roo.LayoutRegion}
44304      */
44305     getRegion : function(target){
44306         return this.regions[target.toLowerCase()];
44307     },
44308     
44309     onWindowResize : function(){
44310         if(this.monitorWindowResize){
44311             this.layout();
44312         }
44313     }
44314 });/*
44315  * Based on:
44316  * Ext JS Library 1.1.1
44317  * Copyright(c) 2006-2007, Ext JS, LLC.
44318  *
44319  * Originally Released Under LGPL - original licence link has changed is not relivant.
44320  *
44321  * Fork - LGPL
44322  * <script type="text/javascript">
44323  */
44324 /**
44325  * @class Roo.BorderLayout
44326  * @extends Roo.LayoutManager
44327  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44328  * please see: <br><br>
44329  * <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>
44330  * <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>
44331  * Example:
44332  <pre><code>
44333  var layout = new Roo.BorderLayout(document.body, {
44334     north: {
44335         initialSize: 25,
44336         titlebar: false
44337     },
44338     west: {
44339         split:true,
44340         initialSize: 200,
44341         minSize: 175,
44342         maxSize: 400,
44343         titlebar: true,
44344         collapsible: true
44345     },
44346     east: {
44347         split:true,
44348         initialSize: 202,
44349         minSize: 175,
44350         maxSize: 400,
44351         titlebar: true,
44352         collapsible: true
44353     },
44354     south: {
44355         split:true,
44356         initialSize: 100,
44357         minSize: 100,
44358         maxSize: 200,
44359         titlebar: true,
44360         collapsible: true
44361     },
44362     center: {
44363         titlebar: true,
44364         autoScroll:true,
44365         resizeTabs: true,
44366         minTabWidth: 50,
44367         preferredTabWidth: 150
44368     }
44369 });
44370
44371 // shorthand
44372 var CP = Roo.ContentPanel;
44373
44374 layout.beginUpdate();
44375 layout.add("north", new CP("north", "North"));
44376 layout.add("south", new CP("south", {title: "South", closable: true}));
44377 layout.add("west", new CP("west", {title: "West"}));
44378 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44379 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44380 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44381 layout.getRegion("center").showPanel("center1");
44382 layout.endUpdate();
44383 </code></pre>
44384
44385 <b>The container the layout is rendered into can be either the body element or any other element.
44386 If it is not the body element, the container needs to either be an absolute positioned element,
44387 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44388 the container size if it is not the body element.</b>
44389
44390 * @constructor
44391 * Create a new BorderLayout
44392 * @param {String/HTMLElement/Element} container The container this layout is bound to
44393 * @param {Object} config Configuration options
44394  */
44395 Roo.BorderLayout = function(container, config){
44396     config = config || {};
44397     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44398     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44399     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44400         var target = this.factory.validRegions[i];
44401         if(config[target]){
44402             this.addRegion(target, config[target]);
44403         }
44404     }
44405 };
44406
44407 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44408     /**
44409      * Creates and adds a new region if it doesn't already exist.
44410      * @param {String} target The target region key (north, south, east, west or center).
44411      * @param {Object} config The regions config object
44412      * @return {BorderLayoutRegion} The new region
44413      */
44414     addRegion : function(target, config){
44415         if(!this.regions[target]){
44416             var r = this.factory.create(target, this, config);
44417             this.bindRegion(target, r);
44418         }
44419         return this.regions[target];
44420     },
44421
44422     // private (kinda)
44423     bindRegion : function(name, r){
44424         this.regions[name] = r;
44425         r.on("visibilitychange", this.layout, this);
44426         r.on("paneladded", this.layout, this);
44427         r.on("panelremoved", this.layout, this);
44428         r.on("invalidated", this.layout, this);
44429         r.on("resized", this.onRegionResized, this);
44430         r.on("collapsed", this.onRegionCollapsed, this);
44431         r.on("expanded", this.onRegionExpanded, this);
44432     },
44433
44434     /**
44435      * Performs a layout update.
44436      */
44437     layout : function(){
44438         if(this.updating) return;
44439         var size = this.getViewSize();
44440         var w = size.width;
44441         var h = size.height;
44442         var centerW = w;
44443         var centerH = h;
44444         var centerY = 0;
44445         var centerX = 0;
44446         //var x = 0, y = 0;
44447
44448         var rs = this.regions;
44449         var north = rs["north"];
44450         var south = rs["south"]; 
44451         var west = rs["west"];
44452         var east = rs["east"];
44453         var center = rs["center"];
44454         //if(this.hideOnLayout){ // not supported anymore
44455             //c.el.setStyle("display", "none");
44456         //}
44457         if(north && north.isVisible()){
44458             var b = north.getBox();
44459             var m = north.getMargins();
44460             b.width = w - (m.left+m.right);
44461             b.x = m.left;
44462             b.y = m.top;
44463             centerY = b.height + b.y + m.bottom;
44464             centerH -= centerY;
44465             north.updateBox(this.safeBox(b));
44466         }
44467         if(south && south.isVisible()){
44468             var b = south.getBox();
44469             var m = south.getMargins();
44470             b.width = w - (m.left+m.right);
44471             b.x = m.left;
44472             var totalHeight = (b.height + m.top + m.bottom);
44473             b.y = h - totalHeight + m.top;
44474             centerH -= totalHeight;
44475             south.updateBox(this.safeBox(b));
44476         }
44477         if(west && west.isVisible()){
44478             var b = west.getBox();
44479             var m = west.getMargins();
44480             b.height = centerH - (m.top+m.bottom);
44481             b.x = m.left;
44482             b.y = centerY + m.top;
44483             var totalWidth = (b.width + m.left + m.right);
44484             centerX += totalWidth;
44485             centerW -= totalWidth;
44486             west.updateBox(this.safeBox(b));
44487         }
44488         if(east && east.isVisible()){
44489             var b = east.getBox();
44490             var m = east.getMargins();
44491             b.height = centerH - (m.top+m.bottom);
44492             var totalWidth = (b.width + m.left + m.right);
44493             b.x = w - totalWidth + m.left;
44494             b.y = centerY + m.top;
44495             centerW -= totalWidth;
44496             east.updateBox(this.safeBox(b));
44497         }
44498         if(center){
44499             var m = center.getMargins();
44500             var centerBox = {
44501                 x: centerX + m.left,
44502                 y: centerY + m.top,
44503                 width: centerW - (m.left+m.right),
44504                 height: centerH - (m.top+m.bottom)
44505             };
44506             //if(this.hideOnLayout){
44507                 //center.el.setStyle("display", "block");
44508             //}
44509             center.updateBox(this.safeBox(centerBox));
44510         }
44511         this.el.repaint();
44512         this.fireEvent("layout", this);
44513     },
44514
44515     // private
44516     safeBox : function(box){
44517         box.width = Math.max(0, box.width);
44518         box.height = Math.max(0, box.height);
44519         return box;
44520     },
44521
44522     /**
44523      * Adds a ContentPanel (or subclass) to this layout.
44524      * @param {String} target The target region key (north, south, east, west or center).
44525      * @param {Roo.ContentPanel} panel The panel to add
44526      * @return {Roo.ContentPanel} The added panel
44527      */
44528     add : function(target, panel){
44529          
44530         target = target.toLowerCase();
44531         return this.regions[target].add(panel);
44532     },
44533
44534     /**
44535      * Remove a ContentPanel (or subclass) to this layout.
44536      * @param {String} target The target region key (north, south, east, west or center).
44537      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44538      * @return {Roo.ContentPanel} The removed panel
44539      */
44540     remove : function(target, panel){
44541         target = target.toLowerCase();
44542         return this.regions[target].remove(panel);
44543     },
44544
44545     /**
44546      * Searches all regions for a panel with the specified id
44547      * @param {String} panelId
44548      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44549      */
44550     findPanel : function(panelId){
44551         var rs = this.regions;
44552         for(var target in rs){
44553             if(typeof rs[target] != "function"){
44554                 var p = rs[target].getPanel(panelId);
44555                 if(p){
44556                     return p;
44557                 }
44558             }
44559         }
44560         return null;
44561     },
44562
44563     /**
44564      * Searches all regions for a panel with the specified id and activates (shows) it.
44565      * @param {String/ContentPanel} panelId The panels id or the panel itself
44566      * @return {Roo.ContentPanel} The shown panel or null
44567      */
44568     showPanel : function(panelId) {
44569       var rs = this.regions;
44570       for(var target in rs){
44571          var r = rs[target];
44572          if(typeof r != "function"){
44573             if(r.hasPanel(panelId)){
44574                return r.showPanel(panelId);
44575             }
44576          }
44577       }
44578       return null;
44579    },
44580
44581    /**
44582      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44583      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44584      */
44585     restoreState : function(provider){
44586         if(!provider){
44587             provider = Roo.state.Manager;
44588         }
44589         var sm = new Roo.LayoutStateManager();
44590         sm.init(this, provider);
44591     },
44592
44593     /**
44594      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44595      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44596      * a valid ContentPanel config object.  Example:
44597      * <pre><code>
44598 // Create the main layout
44599 var layout = new Roo.BorderLayout('main-ct', {
44600     west: {
44601         split:true,
44602         minSize: 175,
44603         titlebar: true
44604     },
44605     center: {
44606         title:'Components'
44607     }
44608 }, 'main-ct');
44609
44610 // Create and add multiple ContentPanels at once via configs
44611 layout.batchAdd({
44612    west: {
44613        id: 'source-files',
44614        autoCreate:true,
44615        title:'Ext Source Files',
44616        autoScroll:true,
44617        fitToFrame:true
44618    },
44619    center : {
44620        el: cview,
44621        autoScroll:true,
44622        fitToFrame:true,
44623        toolbar: tb,
44624        resizeEl:'cbody'
44625    }
44626 });
44627 </code></pre>
44628      * @param {Object} regions An object containing ContentPanel configs by region name
44629      */
44630     batchAdd : function(regions){
44631         this.beginUpdate();
44632         for(var rname in regions){
44633             var lr = this.regions[rname];
44634             if(lr){
44635                 this.addTypedPanels(lr, regions[rname]);
44636             }
44637         }
44638         this.endUpdate();
44639     },
44640
44641     // private
44642     addTypedPanels : function(lr, ps){
44643         if(typeof ps == 'string'){
44644             lr.add(new Roo.ContentPanel(ps));
44645         }
44646         else if(ps instanceof Array){
44647             for(var i =0, len = ps.length; i < len; i++){
44648                 this.addTypedPanels(lr, ps[i]);
44649             }
44650         }
44651         else if(!ps.events){ // raw config?
44652             var el = ps.el;
44653             delete ps.el; // prevent conflict
44654             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44655         }
44656         else {  // panel object assumed!
44657             lr.add(ps);
44658         }
44659     },
44660     /**
44661      * Adds a xtype elements to the layout.
44662      * <pre><code>
44663
44664 layout.addxtype({
44665        xtype : 'ContentPanel',
44666        region: 'west',
44667        items: [ .... ]
44668    }
44669 );
44670
44671 layout.addxtype({
44672         xtype : 'NestedLayoutPanel',
44673         region: 'west',
44674         layout: {
44675            center: { },
44676            west: { }   
44677         },
44678         items : [ ... list of content panels or nested layout panels.. ]
44679    }
44680 );
44681 </code></pre>
44682      * @param {Object} cfg Xtype definition of item to add.
44683      */
44684     addxtype : function(cfg)
44685     {
44686         // basically accepts a pannel...
44687         // can accept a layout region..!?!?
44688         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44689         
44690         if (!cfg.xtype.match(/Panel$/)) {
44691             return false;
44692         }
44693         var ret = false;
44694         
44695         if (typeof(cfg.region) == 'undefined') {
44696             Roo.log("Failed to add Panel, region was not set");
44697             Roo.log(cfg);
44698             return false;
44699         }
44700         var region = cfg.region;
44701         delete cfg.region;
44702         
44703           
44704         var xitems = [];
44705         if (cfg.items) {
44706             xitems = cfg.items;
44707             delete cfg.items;
44708         }
44709         
44710         
44711         switch(cfg.xtype) 
44712         {
44713             case 'ContentPanel':  // ContentPanel (el, cfg)
44714             case 'ScrollPanel':  // ContentPanel (el, cfg)
44715                 if(cfg.autoCreate) {
44716                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44717                 } else {
44718                     var el = this.el.createChild();
44719                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44720                 }
44721                 
44722                 this.add(region, ret);
44723                 break;
44724             
44725             
44726             case 'TreePanel': // our new panel!
44727                 cfg.el = this.el.createChild();
44728                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44729                 this.add(region, ret);
44730                 break;
44731             
44732             case 'NestedLayoutPanel': 
44733                 // create a new Layout (which is  a Border Layout...
44734                 var el = this.el.createChild();
44735                 var clayout = cfg.layout;
44736                 delete cfg.layout;
44737                 clayout.items   = clayout.items  || [];
44738                 // replace this exitems with the clayout ones..
44739                 xitems = clayout.items;
44740                  
44741                 
44742                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44743                     cfg.background = false;
44744                 }
44745                 var layout = new Roo.BorderLayout(el, clayout);
44746                 
44747                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44748                 //console.log('adding nested layout panel '  + cfg.toSource());
44749                 this.add(region, ret);
44750                 
44751                 break;
44752                 
44753             case 'GridPanel': 
44754             
44755                 // needs grid and region
44756                 
44757                 //var el = this.getRegion(region).el.createChild();
44758                 var el = this.el.createChild();
44759                 // create the grid first...
44760                 
44761                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44762                 delete cfg.grid;
44763                 if (region == 'center' && this.active ) {
44764                     cfg.background = false;
44765                 }
44766                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44767                 
44768                 this.add(region, ret);
44769                 if (cfg.background) {
44770                     ret.on('activate', function(gp) {
44771                         if (!gp.grid.rendered) {
44772                             gp.grid.render();
44773                         }
44774                     });
44775                 } else {
44776                     grid.render();
44777                 }
44778                 break;
44779            
44780                
44781                 
44782                 
44783             default: 
44784                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44785                 return null;
44786              // GridPanel (grid, cfg)
44787             
44788         }
44789         this.beginUpdate();
44790         // add children..
44791         Roo.each(xitems, function(i)  {
44792             ret.addxtype(i);
44793         });
44794         this.endUpdate();
44795         return ret;
44796         
44797     }
44798 });
44799
44800 /**
44801  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44802  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44803  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44804  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44805  * <pre><code>
44806 // shorthand
44807 var CP = Roo.ContentPanel;
44808
44809 var layout = Roo.BorderLayout.create({
44810     north: {
44811         initialSize: 25,
44812         titlebar: false,
44813         panels: [new CP("north", "North")]
44814     },
44815     west: {
44816         split:true,
44817         initialSize: 200,
44818         minSize: 175,
44819         maxSize: 400,
44820         titlebar: true,
44821         collapsible: true,
44822         panels: [new CP("west", {title: "West"})]
44823     },
44824     east: {
44825         split:true,
44826         initialSize: 202,
44827         minSize: 175,
44828         maxSize: 400,
44829         titlebar: true,
44830         collapsible: true,
44831         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44832     },
44833     south: {
44834         split:true,
44835         initialSize: 100,
44836         minSize: 100,
44837         maxSize: 200,
44838         titlebar: true,
44839         collapsible: true,
44840         panels: [new CP("south", {title: "South", closable: true})]
44841     },
44842     center: {
44843         titlebar: true,
44844         autoScroll:true,
44845         resizeTabs: true,
44846         minTabWidth: 50,
44847         preferredTabWidth: 150,
44848         panels: [
44849             new CP("center1", {title: "Close Me", closable: true}),
44850             new CP("center2", {title: "Center Panel", closable: false})
44851         ]
44852     }
44853 }, document.body);
44854
44855 layout.getRegion("center").showPanel("center1");
44856 </code></pre>
44857  * @param config
44858  * @param targetEl
44859  */
44860 Roo.BorderLayout.create = function(config, targetEl){
44861     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44862     layout.beginUpdate();
44863     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44864     for(var j = 0, jlen = regions.length; j < jlen; j++){
44865         var lr = regions[j];
44866         if(layout.regions[lr] && config[lr].panels){
44867             var r = layout.regions[lr];
44868             var ps = config[lr].panels;
44869             layout.addTypedPanels(r, ps);
44870         }
44871     }
44872     layout.endUpdate();
44873     return layout;
44874 };
44875
44876 // private
44877 Roo.BorderLayout.RegionFactory = {
44878     // private
44879     validRegions : ["north","south","east","west","center"],
44880
44881     // private
44882     create : function(target, mgr, config){
44883         target = target.toLowerCase();
44884         if(config.lightweight || config.basic){
44885             return new Roo.BasicLayoutRegion(mgr, config, target);
44886         }
44887         switch(target){
44888             case "north":
44889                 return new Roo.NorthLayoutRegion(mgr, config);
44890             case "south":
44891                 return new Roo.SouthLayoutRegion(mgr, config);
44892             case "east":
44893                 return new Roo.EastLayoutRegion(mgr, config);
44894             case "west":
44895                 return new Roo.WestLayoutRegion(mgr, config);
44896             case "center":
44897                 return new Roo.CenterLayoutRegion(mgr, config);
44898         }
44899         throw 'Layout region "'+target+'" not supported.';
44900     }
44901 };/*
44902  * Based on:
44903  * Ext JS Library 1.1.1
44904  * Copyright(c) 2006-2007, Ext JS, LLC.
44905  *
44906  * Originally Released Under LGPL - original licence link has changed is not relivant.
44907  *
44908  * Fork - LGPL
44909  * <script type="text/javascript">
44910  */
44911  
44912 /**
44913  * @class Roo.BasicLayoutRegion
44914  * @extends Roo.util.Observable
44915  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44916  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44917  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44918  */
44919 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44920     this.mgr = mgr;
44921     this.position  = pos;
44922     this.events = {
44923         /**
44924          * @scope Roo.BasicLayoutRegion
44925          */
44926         
44927         /**
44928          * @event beforeremove
44929          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44930          * @param {Roo.LayoutRegion} this
44931          * @param {Roo.ContentPanel} panel The panel
44932          * @param {Object} e The cancel event object
44933          */
44934         "beforeremove" : true,
44935         /**
44936          * @event invalidated
44937          * Fires when the layout for this region is changed.
44938          * @param {Roo.LayoutRegion} this
44939          */
44940         "invalidated" : true,
44941         /**
44942          * @event visibilitychange
44943          * Fires when this region is shown or hidden 
44944          * @param {Roo.LayoutRegion} this
44945          * @param {Boolean} visibility true or false
44946          */
44947         "visibilitychange" : true,
44948         /**
44949          * @event paneladded
44950          * Fires when a panel is added. 
44951          * @param {Roo.LayoutRegion} this
44952          * @param {Roo.ContentPanel} panel The panel
44953          */
44954         "paneladded" : true,
44955         /**
44956          * @event panelremoved
44957          * Fires when a panel is removed. 
44958          * @param {Roo.LayoutRegion} this
44959          * @param {Roo.ContentPanel} panel The panel
44960          */
44961         "panelremoved" : true,
44962         /**
44963          * @event collapsed
44964          * Fires when this region is collapsed.
44965          * @param {Roo.LayoutRegion} this
44966          */
44967         "collapsed" : true,
44968         /**
44969          * @event expanded
44970          * Fires when this region is expanded.
44971          * @param {Roo.LayoutRegion} this
44972          */
44973         "expanded" : true,
44974         /**
44975          * @event slideshow
44976          * Fires when this region is slid into view.
44977          * @param {Roo.LayoutRegion} this
44978          */
44979         "slideshow" : true,
44980         /**
44981          * @event slidehide
44982          * Fires when this region slides out of view. 
44983          * @param {Roo.LayoutRegion} this
44984          */
44985         "slidehide" : true,
44986         /**
44987          * @event panelactivated
44988          * Fires when a panel is activated. 
44989          * @param {Roo.LayoutRegion} this
44990          * @param {Roo.ContentPanel} panel The activated panel
44991          */
44992         "panelactivated" : true,
44993         /**
44994          * @event resized
44995          * Fires when the user resizes this region. 
44996          * @param {Roo.LayoutRegion} this
44997          * @param {Number} newSize The new size (width for east/west, height for north/south)
44998          */
44999         "resized" : true
45000     };
45001     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45002     this.panels = new Roo.util.MixedCollection();
45003     this.panels.getKey = this.getPanelId.createDelegate(this);
45004     this.box = null;
45005     this.activePanel = null;
45006     // ensure listeners are added...
45007     
45008     if (config.listeners || config.events) {
45009         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45010             listeners : config.listeners || {},
45011             events : config.events || {}
45012         });
45013     }
45014     
45015     if(skipConfig !== true){
45016         this.applyConfig(config);
45017     }
45018 };
45019
45020 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45021     getPanelId : function(p){
45022         return p.getId();
45023     },
45024     
45025     applyConfig : function(config){
45026         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45027         this.config = config;
45028         
45029     },
45030     
45031     /**
45032      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45033      * the width, for horizontal (north, south) the height.
45034      * @param {Number} newSize The new width or height
45035      */
45036     resizeTo : function(newSize){
45037         var el = this.el ? this.el :
45038                  (this.activePanel ? this.activePanel.getEl() : null);
45039         if(el){
45040             switch(this.position){
45041                 case "east":
45042                 case "west":
45043                     el.setWidth(newSize);
45044                     this.fireEvent("resized", this, newSize);
45045                 break;
45046                 case "north":
45047                 case "south":
45048                     el.setHeight(newSize);
45049                     this.fireEvent("resized", this, newSize);
45050                 break;                
45051             }
45052         }
45053     },
45054     
45055     getBox : function(){
45056         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45057     },
45058     
45059     getMargins : function(){
45060         return this.margins;
45061     },
45062     
45063     updateBox : function(box){
45064         this.box = box;
45065         var el = this.activePanel.getEl();
45066         el.dom.style.left = box.x + "px";
45067         el.dom.style.top = box.y + "px";
45068         this.activePanel.setSize(box.width, box.height);
45069     },
45070     
45071     /**
45072      * Returns the container element for this region.
45073      * @return {Roo.Element}
45074      */
45075     getEl : function(){
45076         return this.activePanel;
45077     },
45078     
45079     /**
45080      * Returns true if this region is currently visible.
45081      * @return {Boolean}
45082      */
45083     isVisible : function(){
45084         return this.activePanel ? true : false;
45085     },
45086     
45087     setActivePanel : function(panel){
45088         panel = this.getPanel(panel);
45089         if(this.activePanel && this.activePanel != panel){
45090             this.activePanel.setActiveState(false);
45091             this.activePanel.getEl().setLeftTop(-10000,-10000);
45092         }
45093         this.activePanel = panel;
45094         panel.setActiveState(true);
45095         if(this.box){
45096             panel.setSize(this.box.width, this.box.height);
45097         }
45098         this.fireEvent("panelactivated", this, panel);
45099         this.fireEvent("invalidated");
45100     },
45101     
45102     /**
45103      * Show the specified panel.
45104      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45105      * @return {Roo.ContentPanel} The shown panel or null
45106      */
45107     showPanel : function(panel){
45108         if(panel = this.getPanel(panel)){
45109             this.setActivePanel(panel);
45110         }
45111         return panel;
45112     },
45113     
45114     /**
45115      * Get the active panel for this region.
45116      * @return {Roo.ContentPanel} The active panel or null
45117      */
45118     getActivePanel : function(){
45119         return this.activePanel;
45120     },
45121     
45122     /**
45123      * Add the passed ContentPanel(s)
45124      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45125      * @return {Roo.ContentPanel} The panel added (if only one was added)
45126      */
45127     add : function(panel){
45128         if(arguments.length > 1){
45129             for(var i = 0, len = arguments.length; i < len; i++) {
45130                 this.add(arguments[i]);
45131             }
45132             return null;
45133         }
45134         if(this.hasPanel(panel)){
45135             this.showPanel(panel);
45136             return panel;
45137         }
45138         var el = panel.getEl();
45139         if(el.dom.parentNode != this.mgr.el.dom){
45140             this.mgr.el.dom.appendChild(el.dom);
45141         }
45142         if(panel.setRegion){
45143             panel.setRegion(this);
45144         }
45145         this.panels.add(panel);
45146         el.setStyle("position", "absolute");
45147         if(!panel.background){
45148             this.setActivePanel(panel);
45149             if(this.config.initialSize && this.panels.getCount()==1){
45150                 this.resizeTo(this.config.initialSize);
45151             }
45152         }
45153         this.fireEvent("paneladded", this, panel);
45154         return panel;
45155     },
45156     
45157     /**
45158      * Returns true if the panel is in this region.
45159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45160      * @return {Boolean}
45161      */
45162     hasPanel : function(panel){
45163         if(typeof panel == "object"){ // must be panel obj
45164             panel = panel.getId();
45165         }
45166         return this.getPanel(panel) ? true : false;
45167     },
45168     
45169     /**
45170      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45171      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45172      * @param {Boolean} preservePanel Overrides the config preservePanel option
45173      * @return {Roo.ContentPanel} The panel that was removed
45174      */
45175     remove : function(panel, preservePanel){
45176         panel = this.getPanel(panel);
45177         if(!panel){
45178             return null;
45179         }
45180         var e = {};
45181         this.fireEvent("beforeremove", this, panel, e);
45182         if(e.cancel === true){
45183             return null;
45184         }
45185         var panelId = panel.getId();
45186         this.panels.removeKey(panelId);
45187         return panel;
45188     },
45189     
45190     /**
45191      * Returns the panel specified or null if it's not in this region.
45192      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45193      * @return {Roo.ContentPanel}
45194      */
45195     getPanel : function(id){
45196         if(typeof id == "object"){ // must be panel obj
45197             return id;
45198         }
45199         return this.panels.get(id);
45200     },
45201     
45202     /**
45203      * Returns this regions position (north/south/east/west/center).
45204      * @return {String} 
45205      */
45206     getPosition: function(){
45207         return this.position;    
45208     }
45209 });/*
45210  * Based on:
45211  * Ext JS Library 1.1.1
45212  * Copyright(c) 2006-2007, Ext JS, LLC.
45213  *
45214  * Originally Released Under LGPL - original licence link has changed is not relivant.
45215  *
45216  * Fork - LGPL
45217  * <script type="text/javascript">
45218  */
45219  
45220 /**
45221  * @class Roo.LayoutRegion
45222  * @extends Roo.BasicLayoutRegion
45223  * This class represents a region in a layout manager.
45224  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45225  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45226  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45227  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45228  * @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})
45229  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45230  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45231  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45232  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45233  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45234  * @cfg {String}    title           The title for the region (overrides panel titles)
45235  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45236  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45237  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45238  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45239  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45240  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45241  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45242  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45243  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45244  * @cfg {Boolean}   showPin         True to show a pin button
45245  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45246  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45247  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45248  * @cfg {Number}    width           For East/West panels
45249  * @cfg {Number}    height          For North/South panels
45250  * @cfg {Boolean}   split           To show the splitter
45251  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45252  */
45253 Roo.LayoutRegion = function(mgr, config, pos){
45254     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45255     var dh = Roo.DomHelper;
45256     /** This region's container element 
45257     * @type Roo.Element */
45258     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45259     /** This region's title element 
45260     * @type Roo.Element */
45261
45262     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45263         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45264         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45265     ]}, true);
45266     this.titleEl.enableDisplayMode();
45267     /** This region's title text element 
45268     * @type HTMLElement */
45269     this.titleTextEl = this.titleEl.dom.firstChild;
45270     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45271     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45272     this.closeBtn.enableDisplayMode();
45273     this.closeBtn.on("click", this.closeClicked, this);
45274     this.closeBtn.hide();
45275
45276     this.createBody(config);
45277     this.visible = true;
45278     this.collapsed = false;
45279
45280     if(config.hideWhenEmpty){
45281         this.hide();
45282         this.on("paneladded", this.validateVisibility, this);
45283         this.on("panelremoved", this.validateVisibility, this);
45284     }
45285     this.applyConfig(config);
45286 };
45287
45288 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45289
45290     createBody : function(){
45291         /** This region's body element 
45292         * @type Roo.Element */
45293         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45294     },
45295
45296     applyConfig : function(c){
45297         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45298             var dh = Roo.DomHelper;
45299             if(c.titlebar !== false){
45300                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45301                 this.collapseBtn.on("click", this.collapse, this);
45302                 this.collapseBtn.enableDisplayMode();
45303
45304                 if(c.showPin === true || this.showPin){
45305                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45306                     this.stickBtn.enableDisplayMode();
45307                     this.stickBtn.on("click", this.expand, this);
45308                     this.stickBtn.hide();
45309                 }
45310             }
45311             /** This region's collapsed element
45312             * @type Roo.Element */
45313             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45314                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45315             ]}, true);
45316             if(c.floatable !== false){
45317                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45318                this.collapsedEl.on("click", this.collapseClick, this);
45319             }
45320
45321             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45322                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45323                    id: "message", unselectable: "on", style:{"float":"left"}});
45324                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45325              }
45326             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45327             this.expandBtn.on("click", this.expand, this);
45328         }
45329         if(this.collapseBtn){
45330             this.collapseBtn.setVisible(c.collapsible == true);
45331         }
45332         this.cmargins = c.cmargins || this.cmargins ||
45333                          (this.position == "west" || this.position == "east" ?
45334                              {top: 0, left: 2, right:2, bottom: 0} :
45335                              {top: 2, left: 0, right:0, bottom: 2});
45336         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45337         this.bottomTabs = c.tabPosition != "top";
45338         this.autoScroll = c.autoScroll || false;
45339         if(this.autoScroll){
45340             this.bodyEl.setStyle("overflow", "auto");
45341         }else{
45342             this.bodyEl.setStyle("overflow", "hidden");
45343         }
45344         //if(c.titlebar !== false){
45345             if((!c.titlebar && !c.title) || c.titlebar === false){
45346                 this.titleEl.hide();
45347             }else{
45348                 this.titleEl.show();
45349                 if(c.title){
45350                     this.titleTextEl.innerHTML = c.title;
45351                 }
45352             }
45353         //}
45354         this.duration = c.duration || .30;
45355         this.slideDuration = c.slideDuration || .45;
45356         this.config = c;
45357         if(c.collapsed){
45358             this.collapse(true);
45359         }
45360         if(c.hidden){
45361             this.hide();
45362         }
45363     },
45364     /**
45365      * Returns true if this region is currently visible.
45366      * @return {Boolean}
45367      */
45368     isVisible : function(){
45369         return this.visible;
45370     },
45371
45372     /**
45373      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45374      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45375      */
45376     setCollapsedTitle : function(title){
45377         title = title || "&#160;";
45378         if(this.collapsedTitleTextEl){
45379             this.collapsedTitleTextEl.innerHTML = title;
45380         }
45381     },
45382
45383     getBox : function(){
45384         var b;
45385         if(!this.collapsed){
45386             b = this.el.getBox(false, true);
45387         }else{
45388             b = this.collapsedEl.getBox(false, true);
45389         }
45390         return b;
45391     },
45392
45393     getMargins : function(){
45394         return this.collapsed ? this.cmargins : this.margins;
45395     },
45396
45397     highlight : function(){
45398         this.el.addClass("x-layout-panel-dragover");
45399     },
45400
45401     unhighlight : function(){
45402         this.el.removeClass("x-layout-panel-dragover");
45403     },
45404
45405     updateBox : function(box){
45406         this.box = box;
45407         if(!this.collapsed){
45408             this.el.dom.style.left = box.x + "px";
45409             this.el.dom.style.top = box.y + "px";
45410             this.updateBody(box.width, box.height);
45411         }else{
45412             this.collapsedEl.dom.style.left = box.x + "px";
45413             this.collapsedEl.dom.style.top = box.y + "px";
45414             this.collapsedEl.setSize(box.width, box.height);
45415         }
45416         if(this.tabs){
45417             this.tabs.autoSizeTabs();
45418         }
45419     },
45420
45421     updateBody : function(w, h){
45422         if(w !== null){
45423             this.el.setWidth(w);
45424             w -= this.el.getBorderWidth("rl");
45425             if(this.config.adjustments){
45426                 w += this.config.adjustments[0];
45427             }
45428         }
45429         if(h !== null){
45430             this.el.setHeight(h);
45431             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45432             h -= this.el.getBorderWidth("tb");
45433             if(this.config.adjustments){
45434                 h += this.config.adjustments[1];
45435             }
45436             this.bodyEl.setHeight(h);
45437             if(this.tabs){
45438                 h = this.tabs.syncHeight(h);
45439             }
45440         }
45441         if(this.panelSize){
45442             w = w !== null ? w : this.panelSize.width;
45443             h = h !== null ? h : this.panelSize.height;
45444         }
45445         if(this.activePanel){
45446             var el = this.activePanel.getEl();
45447             w = w !== null ? w : el.getWidth();
45448             h = h !== null ? h : el.getHeight();
45449             this.panelSize = {width: w, height: h};
45450             this.activePanel.setSize(w, h);
45451         }
45452         if(Roo.isIE && this.tabs){
45453             this.tabs.el.repaint();
45454         }
45455     },
45456
45457     /**
45458      * Returns the container element for this region.
45459      * @return {Roo.Element}
45460      */
45461     getEl : function(){
45462         return this.el;
45463     },
45464
45465     /**
45466      * Hides this region.
45467      */
45468     hide : function(){
45469         if(!this.collapsed){
45470             this.el.dom.style.left = "-2000px";
45471             this.el.hide();
45472         }else{
45473             this.collapsedEl.dom.style.left = "-2000px";
45474             this.collapsedEl.hide();
45475         }
45476         this.visible = false;
45477         this.fireEvent("visibilitychange", this, false);
45478     },
45479
45480     /**
45481      * Shows this region if it was previously hidden.
45482      */
45483     show : function(){
45484         if(!this.collapsed){
45485             this.el.show();
45486         }else{
45487             this.collapsedEl.show();
45488         }
45489         this.visible = true;
45490         this.fireEvent("visibilitychange", this, true);
45491     },
45492
45493     closeClicked : function(){
45494         if(this.activePanel){
45495             this.remove(this.activePanel);
45496         }
45497     },
45498
45499     collapseClick : function(e){
45500         if(this.isSlid){
45501            e.stopPropagation();
45502            this.slideIn();
45503         }else{
45504            e.stopPropagation();
45505            this.slideOut();
45506         }
45507     },
45508
45509     /**
45510      * Collapses this region.
45511      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45512      */
45513     collapse : function(skipAnim){
45514         if(this.collapsed) return;
45515         this.collapsed = true;
45516         if(this.split){
45517             this.split.el.hide();
45518         }
45519         if(this.config.animate && skipAnim !== true){
45520             this.fireEvent("invalidated", this);
45521             this.animateCollapse();
45522         }else{
45523             this.el.setLocation(-20000,-20000);
45524             this.el.hide();
45525             this.collapsedEl.show();
45526             this.fireEvent("collapsed", this);
45527             this.fireEvent("invalidated", this);
45528         }
45529     },
45530
45531     animateCollapse : function(){
45532         // overridden
45533     },
45534
45535     /**
45536      * Expands this region if it was previously collapsed.
45537      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45538      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45539      */
45540     expand : function(e, skipAnim){
45541         if(e) e.stopPropagation();
45542         if(!this.collapsed || this.el.hasActiveFx()) return;
45543         if(this.isSlid){
45544             this.afterSlideIn();
45545             skipAnim = true;
45546         }
45547         this.collapsed = false;
45548         if(this.config.animate && skipAnim !== true){
45549             this.animateExpand();
45550         }else{
45551             this.el.show();
45552             if(this.split){
45553                 this.split.el.show();
45554             }
45555             this.collapsedEl.setLocation(-2000,-2000);
45556             this.collapsedEl.hide();
45557             this.fireEvent("invalidated", this);
45558             this.fireEvent("expanded", this);
45559         }
45560     },
45561
45562     animateExpand : function(){
45563         // overridden
45564     },
45565
45566     initTabs : function()
45567     {
45568         this.bodyEl.setStyle("overflow", "hidden");
45569         var ts = new Roo.TabPanel(
45570                 this.bodyEl.dom,
45571                 {
45572                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45573                     disableTooltips: this.config.disableTabTips,
45574                     toolbar : this.config.toolbar
45575                 }
45576         );
45577         if(this.config.hideTabs){
45578             ts.stripWrap.setDisplayed(false);
45579         }
45580         this.tabs = ts;
45581         ts.resizeTabs = this.config.resizeTabs === true;
45582         ts.minTabWidth = this.config.minTabWidth || 40;
45583         ts.maxTabWidth = this.config.maxTabWidth || 250;
45584         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45585         ts.monitorResize = false;
45586         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45587         ts.bodyEl.addClass('x-layout-tabs-body');
45588         this.panels.each(this.initPanelAsTab, this);
45589     },
45590
45591     initPanelAsTab : function(panel){
45592         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45593                     this.config.closeOnTab && panel.isClosable());
45594         if(panel.tabTip !== undefined){
45595             ti.setTooltip(panel.tabTip);
45596         }
45597         ti.on("activate", function(){
45598               this.setActivePanel(panel);
45599         }, this);
45600         if(this.config.closeOnTab){
45601             ti.on("beforeclose", function(t, e){
45602                 e.cancel = true;
45603                 this.remove(panel);
45604             }, this);
45605         }
45606         return ti;
45607     },
45608
45609     updatePanelTitle : function(panel, title){
45610         if(this.activePanel == panel){
45611             this.updateTitle(title);
45612         }
45613         if(this.tabs){
45614             var ti = this.tabs.getTab(panel.getEl().id);
45615             ti.setText(title);
45616             if(panel.tabTip !== undefined){
45617                 ti.setTooltip(panel.tabTip);
45618             }
45619         }
45620     },
45621
45622     updateTitle : function(title){
45623         if(this.titleTextEl && !this.config.title){
45624             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45625         }
45626     },
45627
45628     setActivePanel : function(panel){
45629         panel = this.getPanel(panel);
45630         if(this.activePanel && this.activePanel != panel){
45631             this.activePanel.setActiveState(false);
45632         }
45633         this.activePanel = panel;
45634         panel.setActiveState(true);
45635         if(this.panelSize){
45636             panel.setSize(this.panelSize.width, this.panelSize.height);
45637         }
45638         if(this.closeBtn){
45639             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45640         }
45641         this.updateTitle(panel.getTitle());
45642         if(this.tabs){
45643             this.fireEvent("invalidated", this);
45644         }
45645         this.fireEvent("panelactivated", this, panel);
45646     },
45647
45648     /**
45649      * Shows the specified panel.
45650      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45651      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45652      */
45653     showPanel : function(panel){
45654         if(panel = this.getPanel(panel)){
45655             if(this.tabs){
45656                 var tab = this.tabs.getTab(panel.getEl().id);
45657                 if(tab.isHidden()){
45658                     this.tabs.unhideTab(tab.id);
45659                 }
45660                 tab.activate();
45661             }else{
45662                 this.setActivePanel(panel);
45663             }
45664         }
45665         return panel;
45666     },
45667
45668     /**
45669      * Get the active panel for this region.
45670      * @return {Roo.ContentPanel} The active panel or null
45671      */
45672     getActivePanel : function(){
45673         return this.activePanel;
45674     },
45675
45676     validateVisibility : function(){
45677         if(this.panels.getCount() < 1){
45678             this.updateTitle("&#160;");
45679             this.closeBtn.hide();
45680             this.hide();
45681         }else{
45682             if(!this.isVisible()){
45683                 this.show();
45684             }
45685         }
45686     },
45687
45688     /**
45689      * Adds the passed ContentPanel(s) to this region.
45690      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45691      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45692      */
45693     add : function(panel){
45694         if(arguments.length > 1){
45695             for(var i = 0, len = arguments.length; i < len; i++) {
45696                 this.add(arguments[i]);
45697             }
45698             return null;
45699         }
45700         if(this.hasPanel(panel)){
45701             this.showPanel(panel);
45702             return panel;
45703         }
45704         panel.setRegion(this);
45705         this.panels.add(panel);
45706         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45707             this.bodyEl.dom.appendChild(panel.getEl().dom);
45708             if(panel.background !== true){
45709                 this.setActivePanel(panel);
45710             }
45711             this.fireEvent("paneladded", this, panel);
45712             return panel;
45713         }
45714         if(!this.tabs){
45715             this.initTabs();
45716         }else{
45717             this.initPanelAsTab(panel);
45718         }
45719         if(panel.background !== true){
45720             this.tabs.activate(panel.getEl().id);
45721         }
45722         this.fireEvent("paneladded", this, panel);
45723         return panel;
45724     },
45725
45726     /**
45727      * Hides the tab for the specified panel.
45728      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45729      */
45730     hidePanel : function(panel){
45731         if(this.tabs && (panel = this.getPanel(panel))){
45732             this.tabs.hideTab(panel.getEl().id);
45733         }
45734     },
45735
45736     /**
45737      * Unhides the tab for a previously hidden panel.
45738      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45739      */
45740     unhidePanel : function(panel){
45741         if(this.tabs && (panel = this.getPanel(panel))){
45742             this.tabs.unhideTab(panel.getEl().id);
45743         }
45744     },
45745
45746     clearPanels : function(){
45747         while(this.panels.getCount() > 0){
45748              this.remove(this.panels.first());
45749         }
45750     },
45751
45752     /**
45753      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45754      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45755      * @param {Boolean} preservePanel Overrides the config preservePanel option
45756      * @return {Roo.ContentPanel} The panel that was removed
45757      */
45758     remove : function(panel, preservePanel){
45759         panel = this.getPanel(panel);
45760         if(!panel){
45761             return null;
45762         }
45763         var e = {};
45764         this.fireEvent("beforeremove", this, panel, e);
45765         if(e.cancel === true){
45766             return null;
45767         }
45768         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45769         var panelId = panel.getId();
45770         this.panels.removeKey(panelId);
45771         if(preservePanel){
45772             document.body.appendChild(panel.getEl().dom);
45773         }
45774         if(this.tabs){
45775             this.tabs.removeTab(panel.getEl().id);
45776         }else if (!preservePanel){
45777             this.bodyEl.dom.removeChild(panel.getEl().dom);
45778         }
45779         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45780             var p = this.panels.first();
45781             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45782             tempEl.appendChild(p.getEl().dom);
45783             this.bodyEl.update("");
45784             this.bodyEl.dom.appendChild(p.getEl().dom);
45785             tempEl = null;
45786             this.updateTitle(p.getTitle());
45787             this.tabs = null;
45788             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45789             this.setActivePanel(p);
45790         }
45791         panel.setRegion(null);
45792         if(this.activePanel == panel){
45793             this.activePanel = null;
45794         }
45795         if(this.config.autoDestroy !== false && preservePanel !== true){
45796             try{panel.destroy();}catch(e){}
45797         }
45798         this.fireEvent("panelremoved", this, panel);
45799         return panel;
45800     },
45801
45802     /**
45803      * Returns the TabPanel component used by this region
45804      * @return {Roo.TabPanel}
45805      */
45806     getTabs : function(){
45807         return this.tabs;
45808     },
45809
45810     createTool : function(parentEl, className){
45811         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45812             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45813         btn.addClassOnOver("x-layout-tools-button-over");
45814         return btn;
45815     }
45816 });/*
45817  * Based on:
45818  * Ext JS Library 1.1.1
45819  * Copyright(c) 2006-2007, Ext JS, LLC.
45820  *
45821  * Originally Released Under LGPL - original licence link has changed is not relivant.
45822  *
45823  * Fork - LGPL
45824  * <script type="text/javascript">
45825  */
45826  
45827
45828
45829 /**
45830  * @class Roo.SplitLayoutRegion
45831  * @extends Roo.LayoutRegion
45832  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45833  */
45834 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45835     this.cursor = cursor;
45836     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45837 };
45838
45839 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45840     splitTip : "Drag to resize.",
45841     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45842     useSplitTips : false,
45843
45844     applyConfig : function(config){
45845         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45846         if(config.split){
45847             if(!this.split){
45848                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45849                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45850                 /** The SplitBar for this region 
45851                 * @type Roo.SplitBar */
45852                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45853                 this.split.on("moved", this.onSplitMove, this);
45854                 this.split.useShim = config.useShim === true;
45855                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45856                 if(this.useSplitTips){
45857                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45858                 }
45859                 if(config.collapsible){
45860                     this.split.el.on("dblclick", this.collapse,  this);
45861                 }
45862             }
45863             if(typeof config.minSize != "undefined"){
45864                 this.split.minSize = config.minSize;
45865             }
45866             if(typeof config.maxSize != "undefined"){
45867                 this.split.maxSize = config.maxSize;
45868             }
45869             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45870                 this.hideSplitter();
45871             }
45872         }
45873     },
45874
45875     getHMaxSize : function(){
45876          var cmax = this.config.maxSize || 10000;
45877          var center = this.mgr.getRegion("center");
45878          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45879     },
45880
45881     getVMaxSize : function(){
45882          var cmax = this.config.maxSize || 10000;
45883          var center = this.mgr.getRegion("center");
45884          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45885     },
45886
45887     onSplitMove : function(split, newSize){
45888         this.fireEvent("resized", this, newSize);
45889     },
45890     
45891     /** 
45892      * Returns the {@link Roo.SplitBar} for this region.
45893      * @return {Roo.SplitBar}
45894      */
45895     getSplitBar : function(){
45896         return this.split;
45897     },
45898     
45899     hide : function(){
45900         this.hideSplitter();
45901         Roo.SplitLayoutRegion.superclass.hide.call(this);
45902     },
45903
45904     hideSplitter : function(){
45905         if(this.split){
45906             this.split.el.setLocation(-2000,-2000);
45907             this.split.el.hide();
45908         }
45909     },
45910
45911     show : function(){
45912         if(this.split){
45913             this.split.el.show();
45914         }
45915         Roo.SplitLayoutRegion.superclass.show.call(this);
45916     },
45917     
45918     beforeSlide: function(){
45919         if(Roo.isGecko){// firefox overflow auto bug workaround
45920             this.bodyEl.clip();
45921             if(this.tabs) this.tabs.bodyEl.clip();
45922             if(this.activePanel){
45923                 this.activePanel.getEl().clip();
45924                 
45925                 if(this.activePanel.beforeSlide){
45926                     this.activePanel.beforeSlide();
45927                 }
45928             }
45929         }
45930     },
45931     
45932     afterSlide : function(){
45933         if(Roo.isGecko){// firefox overflow auto bug workaround
45934             this.bodyEl.unclip();
45935             if(this.tabs) this.tabs.bodyEl.unclip();
45936             if(this.activePanel){
45937                 this.activePanel.getEl().unclip();
45938                 if(this.activePanel.afterSlide){
45939                     this.activePanel.afterSlide();
45940                 }
45941             }
45942         }
45943     },
45944
45945     initAutoHide : function(){
45946         if(this.autoHide !== false){
45947             if(!this.autoHideHd){
45948                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45949                 this.autoHideHd = {
45950                     "mouseout": function(e){
45951                         if(!e.within(this.el, true)){
45952                             st.delay(500);
45953                         }
45954                     },
45955                     "mouseover" : function(e){
45956                         st.cancel();
45957                     },
45958                     scope : this
45959                 };
45960             }
45961             this.el.on(this.autoHideHd);
45962         }
45963     },
45964
45965     clearAutoHide : function(){
45966         if(this.autoHide !== false){
45967             this.el.un("mouseout", this.autoHideHd.mouseout);
45968             this.el.un("mouseover", this.autoHideHd.mouseover);
45969         }
45970     },
45971
45972     clearMonitor : function(){
45973         Roo.get(document).un("click", this.slideInIf, this);
45974     },
45975
45976     // these names are backwards but not changed for compat
45977     slideOut : function(){
45978         if(this.isSlid || this.el.hasActiveFx()){
45979             return;
45980         }
45981         this.isSlid = true;
45982         if(this.collapseBtn){
45983             this.collapseBtn.hide();
45984         }
45985         this.closeBtnState = this.closeBtn.getStyle('display');
45986         this.closeBtn.hide();
45987         if(this.stickBtn){
45988             this.stickBtn.show();
45989         }
45990         this.el.show();
45991         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45992         this.beforeSlide();
45993         this.el.setStyle("z-index", 10001);
45994         this.el.slideIn(this.getSlideAnchor(), {
45995             callback: function(){
45996                 this.afterSlide();
45997                 this.initAutoHide();
45998                 Roo.get(document).on("click", this.slideInIf, this);
45999                 this.fireEvent("slideshow", this);
46000             },
46001             scope: this,
46002             block: true
46003         });
46004     },
46005
46006     afterSlideIn : function(){
46007         this.clearAutoHide();
46008         this.isSlid = false;
46009         this.clearMonitor();
46010         this.el.setStyle("z-index", "");
46011         if(this.collapseBtn){
46012             this.collapseBtn.show();
46013         }
46014         this.closeBtn.setStyle('display', this.closeBtnState);
46015         if(this.stickBtn){
46016             this.stickBtn.hide();
46017         }
46018         this.fireEvent("slidehide", this);
46019     },
46020
46021     slideIn : function(cb){
46022         if(!this.isSlid || this.el.hasActiveFx()){
46023             Roo.callback(cb);
46024             return;
46025         }
46026         this.isSlid = false;
46027         this.beforeSlide();
46028         this.el.slideOut(this.getSlideAnchor(), {
46029             callback: function(){
46030                 this.el.setLeftTop(-10000, -10000);
46031                 this.afterSlide();
46032                 this.afterSlideIn();
46033                 Roo.callback(cb);
46034             },
46035             scope: this,
46036             block: true
46037         });
46038     },
46039     
46040     slideInIf : function(e){
46041         if(!e.within(this.el)){
46042             this.slideIn();
46043         }
46044     },
46045
46046     animateCollapse : function(){
46047         this.beforeSlide();
46048         this.el.setStyle("z-index", 20000);
46049         var anchor = this.getSlideAnchor();
46050         this.el.slideOut(anchor, {
46051             callback : function(){
46052                 this.el.setStyle("z-index", "");
46053                 this.collapsedEl.slideIn(anchor, {duration:.3});
46054                 this.afterSlide();
46055                 this.el.setLocation(-10000,-10000);
46056                 this.el.hide();
46057                 this.fireEvent("collapsed", this);
46058             },
46059             scope: this,
46060             block: true
46061         });
46062     },
46063
46064     animateExpand : function(){
46065         this.beforeSlide();
46066         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46067         this.el.setStyle("z-index", 20000);
46068         this.collapsedEl.hide({
46069             duration:.1
46070         });
46071         this.el.slideIn(this.getSlideAnchor(), {
46072             callback : function(){
46073                 this.el.setStyle("z-index", "");
46074                 this.afterSlide();
46075                 if(this.split){
46076                     this.split.el.show();
46077                 }
46078                 this.fireEvent("invalidated", this);
46079                 this.fireEvent("expanded", this);
46080             },
46081             scope: this,
46082             block: true
46083         });
46084     },
46085
46086     anchors : {
46087         "west" : "left",
46088         "east" : "right",
46089         "north" : "top",
46090         "south" : "bottom"
46091     },
46092
46093     sanchors : {
46094         "west" : "l",
46095         "east" : "r",
46096         "north" : "t",
46097         "south" : "b"
46098     },
46099
46100     canchors : {
46101         "west" : "tl-tr",
46102         "east" : "tr-tl",
46103         "north" : "tl-bl",
46104         "south" : "bl-tl"
46105     },
46106
46107     getAnchor : function(){
46108         return this.anchors[this.position];
46109     },
46110
46111     getCollapseAnchor : function(){
46112         return this.canchors[this.position];
46113     },
46114
46115     getSlideAnchor : function(){
46116         return this.sanchors[this.position];
46117     },
46118
46119     getAlignAdj : function(){
46120         var cm = this.cmargins;
46121         switch(this.position){
46122             case "west":
46123                 return [0, 0];
46124             break;
46125             case "east":
46126                 return [0, 0];
46127             break;
46128             case "north":
46129                 return [0, 0];
46130             break;
46131             case "south":
46132                 return [0, 0];
46133             break;
46134         }
46135     },
46136
46137     getExpandAdj : function(){
46138         var c = this.collapsedEl, cm = this.cmargins;
46139         switch(this.position){
46140             case "west":
46141                 return [-(cm.right+c.getWidth()+cm.left), 0];
46142             break;
46143             case "east":
46144                 return [cm.right+c.getWidth()+cm.left, 0];
46145             break;
46146             case "north":
46147                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46148             break;
46149             case "south":
46150                 return [0, cm.top+cm.bottom+c.getHeight()];
46151             break;
46152         }
46153     }
46154 });/*
46155  * Based on:
46156  * Ext JS Library 1.1.1
46157  * Copyright(c) 2006-2007, Ext JS, LLC.
46158  *
46159  * Originally Released Under LGPL - original licence link has changed is not relivant.
46160  *
46161  * Fork - LGPL
46162  * <script type="text/javascript">
46163  */
46164 /*
46165  * These classes are private internal classes
46166  */
46167 Roo.CenterLayoutRegion = function(mgr, config){
46168     Roo.LayoutRegion.call(this, mgr, config, "center");
46169     this.visible = true;
46170     this.minWidth = config.minWidth || 20;
46171     this.minHeight = config.minHeight || 20;
46172 };
46173
46174 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46175     hide : function(){
46176         // center panel can't be hidden
46177     },
46178     
46179     show : function(){
46180         // center panel can't be hidden
46181     },
46182     
46183     getMinWidth: function(){
46184         return this.minWidth;
46185     },
46186     
46187     getMinHeight: function(){
46188         return this.minHeight;
46189     }
46190 });
46191
46192
46193 Roo.NorthLayoutRegion = function(mgr, config){
46194     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46195     if(this.split){
46196         this.split.placement = Roo.SplitBar.TOP;
46197         this.split.orientation = Roo.SplitBar.VERTICAL;
46198         this.split.el.addClass("x-layout-split-v");
46199     }
46200     var size = config.initialSize || config.height;
46201     if(typeof size != "undefined"){
46202         this.el.setHeight(size);
46203     }
46204 };
46205 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46206     orientation: Roo.SplitBar.VERTICAL,
46207     getBox : function(){
46208         if(this.collapsed){
46209             return this.collapsedEl.getBox();
46210         }
46211         var box = this.el.getBox();
46212         if(this.split){
46213             box.height += this.split.el.getHeight();
46214         }
46215         return box;
46216     },
46217     
46218     updateBox : function(box){
46219         if(this.split && !this.collapsed){
46220             box.height -= this.split.el.getHeight();
46221             this.split.el.setLeft(box.x);
46222             this.split.el.setTop(box.y+box.height);
46223             this.split.el.setWidth(box.width);
46224         }
46225         if(this.collapsed){
46226             this.updateBody(box.width, null);
46227         }
46228         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46229     }
46230 });
46231
46232 Roo.SouthLayoutRegion = function(mgr, config){
46233     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46234     if(this.split){
46235         this.split.placement = Roo.SplitBar.BOTTOM;
46236         this.split.orientation = Roo.SplitBar.VERTICAL;
46237         this.split.el.addClass("x-layout-split-v");
46238     }
46239     var size = config.initialSize || config.height;
46240     if(typeof size != "undefined"){
46241         this.el.setHeight(size);
46242     }
46243 };
46244 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46245     orientation: Roo.SplitBar.VERTICAL,
46246     getBox : function(){
46247         if(this.collapsed){
46248             return this.collapsedEl.getBox();
46249         }
46250         var box = this.el.getBox();
46251         if(this.split){
46252             var sh = this.split.el.getHeight();
46253             box.height += sh;
46254             box.y -= sh;
46255         }
46256         return box;
46257     },
46258     
46259     updateBox : function(box){
46260         if(this.split && !this.collapsed){
46261             var sh = this.split.el.getHeight();
46262             box.height -= sh;
46263             box.y += sh;
46264             this.split.el.setLeft(box.x);
46265             this.split.el.setTop(box.y-sh);
46266             this.split.el.setWidth(box.width);
46267         }
46268         if(this.collapsed){
46269             this.updateBody(box.width, null);
46270         }
46271         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46272     }
46273 });
46274
46275 Roo.EastLayoutRegion = function(mgr, config){
46276     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46277     if(this.split){
46278         this.split.placement = Roo.SplitBar.RIGHT;
46279         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46280         this.split.el.addClass("x-layout-split-h");
46281     }
46282     var size = config.initialSize || config.width;
46283     if(typeof size != "undefined"){
46284         this.el.setWidth(size);
46285     }
46286 };
46287 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46288     orientation: Roo.SplitBar.HORIZONTAL,
46289     getBox : function(){
46290         if(this.collapsed){
46291             return this.collapsedEl.getBox();
46292         }
46293         var box = this.el.getBox();
46294         if(this.split){
46295             var sw = this.split.el.getWidth();
46296             box.width += sw;
46297             box.x -= sw;
46298         }
46299         return box;
46300     },
46301
46302     updateBox : function(box){
46303         if(this.split && !this.collapsed){
46304             var sw = this.split.el.getWidth();
46305             box.width -= sw;
46306             this.split.el.setLeft(box.x);
46307             this.split.el.setTop(box.y);
46308             this.split.el.setHeight(box.height);
46309             box.x += sw;
46310         }
46311         if(this.collapsed){
46312             this.updateBody(null, box.height);
46313         }
46314         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46315     }
46316 });
46317
46318 Roo.WestLayoutRegion = function(mgr, config){
46319     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46320     if(this.split){
46321         this.split.placement = Roo.SplitBar.LEFT;
46322         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46323         this.split.el.addClass("x-layout-split-h");
46324     }
46325     var size = config.initialSize || config.width;
46326     if(typeof size != "undefined"){
46327         this.el.setWidth(size);
46328     }
46329 };
46330 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46331     orientation: Roo.SplitBar.HORIZONTAL,
46332     getBox : function(){
46333         if(this.collapsed){
46334             return this.collapsedEl.getBox();
46335         }
46336         var box = this.el.getBox();
46337         if(this.split){
46338             box.width += this.split.el.getWidth();
46339         }
46340         return box;
46341     },
46342     
46343     updateBox : function(box){
46344         if(this.split && !this.collapsed){
46345             var sw = this.split.el.getWidth();
46346             box.width -= sw;
46347             this.split.el.setLeft(box.x+box.width);
46348             this.split.el.setTop(box.y);
46349             this.split.el.setHeight(box.height);
46350         }
46351         if(this.collapsed){
46352             this.updateBody(null, box.height);
46353         }
46354         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46355     }
46356 });
46357 /*
46358  * Based on:
46359  * Ext JS Library 1.1.1
46360  * Copyright(c) 2006-2007, Ext JS, LLC.
46361  *
46362  * Originally Released Under LGPL - original licence link has changed is not relivant.
46363  *
46364  * Fork - LGPL
46365  * <script type="text/javascript">
46366  */
46367  
46368  
46369 /*
46370  * Private internal class for reading and applying state
46371  */
46372 Roo.LayoutStateManager = function(layout){
46373      // default empty state
46374      this.state = {
46375         north: {},
46376         south: {},
46377         east: {},
46378         west: {}       
46379     };
46380 };
46381
46382 Roo.LayoutStateManager.prototype = {
46383     init : function(layout, provider){
46384         this.provider = provider;
46385         var state = provider.get(layout.id+"-layout-state");
46386         if(state){
46387             var wasUpdating = layout.isUpdating();
46388             if(!wasUpdating){
46389                 layout.beginUpdate();
46390             }
46391             for(var key in state){
46392                 if(typeof state[key] != "function"){
46393                     var rstate = state[key];
46394                     var r = layout.getRegion(key);
46395                     if(r && rstate){
46396                         if(rstate.size){
46397                             r.resizeTo(rstate.size);
46398                         }
46399                         if(rstate.collapsed == true){
46400                             r.collapse(true);
46401                         }else{
46402                             r.expand(null, true);
46403                         }
46404                     }
46405                 }
46406             }
46407             if(!wasUpdating){
46408                 layout.endUpdate();
46409             }
46410             this.state = state; 
46411         }
46412         this.layout = layout;
46413         layout.on("regionresized", this.onRegionResized, this);
46414         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46415         layout.on("regionexpanded", this.onRegionExpanded, this);
46416     },
46417     
46418     storeState : function(){
46419         this.provider.set(this.layout.id+"-layout-state", this.state);
46420     },
46421     
46422     onRegionResized : function(region, newSize){
46423         this.state[region.getPosition()].size = newSize;
46424         this.storeState();
46425     },
46426     
46427     onRegionCollapsed : function(region){
46428         this.state[region.getPosition()].collapsed = true;
46429         this.storeState();
46430     },
46431     
46432     onRegionExpanded : function(region){
46433         this.state[region.getPosition()].collapsed = false;
46434         this.storeState();
46435     }
46436 };/*
46437  * Based on:
46438  * Ext JS Library 1.1.1
46439  * Copyright(c) 2006-2007, Ext JS, LLC.
46440  *
46441  * Originally Released Under LGPL - original licence link has changed is not relivant.
46442  *
46443  * Fork - LGPL
46444  * <script type="text/javascript">
46445  */
46446 /**
46447  * @class Roo.ContentPanel
46448  * @extends Roo.util.Observable
46449  * A basic ContentPanel element.
46450  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46451  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46452  * @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
46453  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46454  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46455  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46456  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46457  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46458  * @cfg {String} title          The title for this panel
46459  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46460  * @cfg {String} url            Calls {@link #setUrl} with this value
46461  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46462  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46463  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46464  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46465
46466  * @constructor
46467  * Create a new ContentPanel.
46468  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46469  * @param {String/Object} config A string to set only the title or a config object
46470  * @param {String} content (optional) Set the HTML content for this panel
46471  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46472  */
46473 Roo.ContentPanel = function(el, config, content){
46474     
46475      
46476     /*
46477     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46478         config = el;
46479         el = Roo.id();
46480     }
46481     if (config && config.parentLayout) { 
46482         el = config.parentLayout.el.createChild(); 
46483     }
46484     */
46485     if(el.autoCreate){ // xtype is available if this is called from factory
46486         config = el;
46487         el = Roo.id();
46488     }
46489     this.el = Roo.get(el);
46490     if(!this.el && config && config.autoCreate){
46491         if(typeof config.autoCreate == "object"){
46492             if(!config.autoCreate.id){
46493                 config.autoCreate.id = config.id||el;
46494             }
46495             this.el = Roo.DomHelper.append(document.body,
46496                         config.autoCreate, true);
46497         }else{
46498             this.el = Roo.DomHelper.append(document.body,
46499                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46500         }
46501     }
46502     this.closable = false;
46503     this.loaded = false;
46504     this.active = false;
46505     if(typeof config == "string"){
46506         this.title = config;
46507     }else{
46508         Roo.apply(this, config);
46509     }
46510     
46511     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46512         this.wrapEl = this.el.wrap();
46513         this.toolbar.container = this.el.insertSibling(false, 'before');
46514         this.toolbar = new Roo.Toolbar(this.toolbar);
46515     }
46516     
46517     
46518     
46519     if(this.resizeEl){
46520         this.resizeEl = Roo.get(this.resizeEl, true);
46521     }else{
46522         this.resizeEl = this.el;
46523     }
46524     this.addEvents({
46525         /**
46526          * @event activate
46527          * Fires when this panel is activated. 
46528          * @param {Roo.ContentPanel} this
46529          */
46530         "activate" : true,
46531         /**
46532          * @event deactivate
46533          * Fires when this panel is activated. 
46534          * @param {Roo.ContentPanel} this
46535          */
46536         "deactivate" : true,
46537
46538         /**
46539          * @event resize
46540          * Fires when this panel is resized if fitToFrame is true.
46541          * @param {Roo.ContentPanel} this
46542          * @param {Number} width The width after any component adjustments
46543          * @param {Number} height The height after any component adjustments
46544          */
46545         "resize" : true,
46546         
46547          /**
46548          * @event render
46549          * Fires when this tab is created
46550          * @param {Roo.ContentPanel} this
46551          */
46552         "render" : true
46553         
46554         
46555         
46556     });
46557     if(this.autoScroll){
46558         this.resizeEl.setStyle("overflow", "auto");
46559     } else {
46560         // fix randome scrolling
46561         this.el.on('scroll', function() {
46562             Roo.log('fix random scolling');
46563             this.scrollTo('top',0); 
46564         });
46565     }
46566     content = content || this.content;
46567     if(content){
46568         this.setContent(content);
46569     }
46570     if(config && config.url){
46571         this.setUrl(this.url, this.params, this.loadOnce);
46572     }
46573     
46574     
46575     
46576     Roo.ContentPanel.superclass.constructor.call(this);
46577     
46578     this.fireEvent('render', this);
46579 };
46580
46581 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46582     tabTip:'',
46583     setRegion : function(region){
46584         this.region = region;
46585         if(region){
46586            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46587         }else{
46588            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46589         } 
46590     },
46591     
46592     /**
46593      * Returns the toolbar for this Panel if one was configured. 
46594      * @return {Roo.Toolbar} 
46595      */
46596     getToolbar : function(){
46597         return this.toolbar;
46598     },
46599     
46600     setActiveState : function(active){
46601         this.active = active;
46602         if(!active){
46603             this.fireEvent("deactivate", this);
46604         }else{
46605             this.fireEvent("activate", this);
46606         }
46607     },
46608     /**
46609      * Updates this panel's element
46610      * @param {String} content The new content
46611      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46612     */
46613     setContent : function(content, loadScripts){
46614         this.el.update(content, loadScripts);
46615     },
46616
46617     ignoreResize : function(w, h){
46618         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46619             return true;
46620         }else{
46621             this.lastSize = {width: w, height: h};
46622             return false;
46623         }
46624     },
46625     /**
46626      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46627      * @return {Roo.UpdateManager} The UpdateManager
46628      */
46629     getUpdateManager : function(){
46630         return this.el.getUpdateManager();
46631     },
46632      /**
46633      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46634      * @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:
46635 <pre><code>
46636 panel.load({
46637     url: "your-url.php",
46638     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46639     callback: yourFunction,
46640     scope: yourObject, //(optional scope)
46641     discardUrl: false,
46642     nocache: false,
46643     text: "Loading...",
46644     timeout: 30,
46645     scripts: false
46646 });
46647 </code></pre>
46648      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46649      * 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.
46650      * @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}
46651      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46652      * @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.
46653      * @return {Roo.ContentPanel} this
46654      */
46655     load : function(){
46656         var um = this.el.getUpdateManager();
46657         um.update.apply(um, arguments);
46658         return this;
46659     },
46660
46661
46662     /**
46663      * 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.
46664      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46665      * @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)
46666      * @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)
46667      * @return {Roo.UpdateManager} The UpdateManager
46668      */
46669     setUrl : function(url, params, loadOnce){
46670         if(this.refreshDelegate){
46671             this.removeListener("activate", this.refreshDelegate);
46672         }
46673         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46674         this.on("activate", this.refreshDelegate);
46675         return this.el.getUpdateManager();
46676     },
46677     
46678     _handleRefresh : function(url, params, loadOnce){
46679         if(!loadOnce || !this.loaded){
46680             var updater = this.el.getUpdateManager();
46681             updater.update(url, params, this._setLoaded.createDelegate(this));
46682         }
46683     },
46684     
46685     _setLoaded : function(){
46686         this.loaded = true;
46687     }, 
46688     
46689     /**
46690      * Returns this panel's id
46691      * @return {String} 
46692      */
46693     getId : function(){
46694         return this.el.id;
46695     },
46696     
46697     /** 
46698      * Returns this panel's element - used by regiosn to add.
46699      * @return {Roo.Element} 
46700      */
46701     getEl : function(){
46702         return this.wrapEl || this.el;
46703     },
46704     
46705     adjustForComponents : function(width, height){
46706         if(this.resizeEl != this.el){
46707             width -= this.el.getFrameWidth('lr');
46708             height -= this.el.getFrameWidth('tb');
46709         }
46710         if(this.toolbar){
46711             var te = this.toolbar.getEl();
46712             height -= te.getHeight();
46713             te.setWidth(width);
46714         }
46715         if(this.adjustments){
46716             width += this.adjustments[0];
46717             height += this.adjustments[1];
46718         }
46719         return {"width": width, "height": height};
46720     },
46721     
46722     setSize : function(width, height){
46723         if(this.fitToFrame && !this.ignoreResize(width, height)){
46724             if(this.fitContainer && this.resizeEl != this.el){
46725                 this.el.setSize(width, height);
46726             }
46727             var size = this.adjustForComponents(width, height);
46728             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46729             this.fireEvent('resize', this, size.width, size.height);
46730         }
46731     },
46732     
46733     /**
46734      * Returns this panel's title
46735      * @return {String} 
46736      */
46737     getTitle : function(){
46738         return this.title;
46739     },
46740     
46741     /**
46742      * Set this panel's title
46743      * @param {String} title
46744      */
46745     setTitle : function(title){
46746         this.title = title;
46747         if(this.region){
46748             this.region.updatePanelTitle(this, title);
46749         }
46750     },
46751     
46752     /**
46753      * Returns true is this panel was configured to be closable
46754      * @return {Boolean} 
46755      */
46756     isClosable : function(){
46757         return this.closable;
46758     },
46759     
46760     beforeSlide : function(){
46761         this.el.clip();
46762         this.resizeEl.clip();
46763     },
46764     
46765     afterSlide : function(){
46766         this.el.unclip();
46767         this.resizeEl.unclip();
46768     },
46769     
46770     /**
46771      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46772      *   Will fail silently if the {@link #setUrl} method has not been called.
46773      *   This does not activate the panel, just updates its content.
46774      */
46775     refresh : function(){
46776         if(this.refreshDelegate){
46777            this.loaded = false;
46778            this.refreshDelegate();
46779         }
46780     },
46781     
46782     /**
46783      * Destroys this panel
46784      */
46785     destroy : function(){
46786         this.el.removeAllListeners();
46787         var tempEl = document.createElement("span");
46788         tempEl.appendChild(this.el.dom);
46789         tempEl.innerHTML = "";
46790         this.el.remove();
46791         this.el = null;
46792     },
46793     
46794     /**
46795      * form - if the content panel contains a form - this is a reference to it.
46796      * @type {Roo.form.Form}
46797      */
46798     form : false,
46799     /**
46800      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46801      *    This contains a reference to it.
46802      * @type {Roo.View}
46803      */
46804     view : false,
46805     
46806       /**
46807      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46808      * <pre><code>
46809
46810 layout.addxtype({
46811        xtype : 'Form',
46812        items: [ .... ]
46813    }
46814 );
46815
46816 </code></pre>
46817      * @param {Object} cfg Xtype definition of item to add.
46818      */
46819     
46820     addxtype : function(cfg) {
46821         // add form..
46822         if (cfg.xtype.match(/^Form$/)) {
46823             var el = this.el.createChild();
46824
46825             this.form = new  Roo.form.Form(cfg);
46826             
46827             
46828             if ( this.form.allItems.length) this.form.render(el.dom);
46829             return this.form;
46830         }
46831         // should only have one of theses..
46832         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46833             // views..
46834             cfg.el = this.el.appendChild(document.createElement("div"));
46835             // factory?
46836             
46837             var ret = new Roo.factory(cfg);
46838             ret.render && ret.render(false, ''); // render blank..
46839             this.view = ret;
46840             return ret;
46841         }
46842         return false;
46843     }
46844 });
46845
46846 /**
46847  * @class Roo.GridPanel
46848  * @extends Roo.ContentPanel
46849  * @constructor
46850  * Create a new GridPanel.
46851  * @param {Roo.grid.Grid} grid The grid for this panel
46852  * @param {String/Object} config A string to set only the panel's title, or a config object
46853  */
46854 Roo.GridPanel = function(grid, config){
46855     
46856   
46857     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46858         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46859         
46860     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46861     
46862     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46863     
46864     if(this.toolbar){
46865         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46866     }
46867     // xtype created footer. - not sure if will work as we normally have to render first..
46868     if (this.footer && !this.footer.el && this.footer.xtype) {
46869         
46870         this.footer.container = this.grid.getView().getFooterPanel(true);
46871         this.footer.dataSource = this.grid.dataSource;
46872         this.footer = Roo.factory(this.footer, Roo);
46873         
46874     }
46875     
46876     grid.monitorWindowResize = false; // turn off autosizing
46877     grid.autoHeight = false;
46878     grid.autoWidth = false;
46879     this.grid = grid;
46880     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46881 };
46882
46883 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46884     getId : function(){
46885         return this.grid.id;
46886     },
46887     
46888     /**
46889      * Returns the grid for this panel
46890      * @return {Roo.grid.Grid} 
46891      */
46892     getGrid : function(){
46893         return this.grid;    
46894     },
46895     
46896     setSize : function(width, height){
46897         if(!this.ignoreResize(width, height)){
46898             var grid = this.grid;
46899             var size = this.adjustForComponents(width, height);
46900             grid.getGridEl().setSize(size.width, size.height);
46901             grid.autoSize();
46902         }
46903     },
46904     
46905     beforeSlide : function(){
46906         this.grid.getView().scroller.clip();
46907     },
46908     
46909     afterSlide : function(){
46910         this.grid.getView().scroller.unclip();
46911     },
46912     
46913     destroy : function(){
46914         this.grid.destroy();
46915         delete this.grid;
46916         Roo.GridPanel.superclass.destroy.call(this); 
46917     }
46918 });
46919
46920
46921 /**
46922  * @class Roo.NestedLayoutPanel
46923  * @extends Roo.ContentPanel
46924  * @constructor
46925  * Create a new NestedLayoutPanel.
46926  * 
46927  * 
46928  * @param {Roo.BorderLayout} layout The layout for this panel
46929  * @param {String/Object} config A string to set only the title or a config object
46930  */
46931 Roo.NestedLayoutPanel = function(layout, config)
46932 {
46933     // construct with only one argument..
46934     /* FIXME - implement nicer consturctors
46935     if (layout.layout) {
46936         config = layout;
46937         layout = config.layout;
46938         delete config.layout;
46939     }
46940     if (layout.xtype && !layout.getEl) {
46941         // then layout needs constructing..
46942         layout = Roo.factory(layout, Roo);
46943     }
46944     */
46945     
46946     
46947     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46948     
46949     layout.monitorWindowResize = false; // turn off autosizing
46950     this.layout = layout;
46951     this.layout.getEl().addClass("x-layout-nested-layout");
46952     
46953     
46954     
46955     
46956 };
46957
46958 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46959
46960     setSize : function(width, height){
46961         if(!this.ignoreResize(width, height)){
46962             var size = this.adjustForComponents(width, height);
46963             var el = this.layout.getEl();
46964             el.setSize(size.width, size.height);
46965             var touch = el.dom.offsetWidth;
46966             this.layout.layout();
46967             // ie requires a double layout on the first pass
46968             if(Roo.isIE && !this.initialized){
46969                 this.initialized = true;
46970                 this.layout.layout();
46971             }
46972         }
46973     },
46974     
46975     // activate all subpanels if not currently active..
46976     
46977     setActiveState : function(active){
46978         this.active = active;
46979         if(!active){
46980             this.fireEvent("deactivate", this);
46981             return;
46982         }
46983         
46984         this.fireEvent("activate", this);
46985         // not sure if this should happen before or after..
46986         if (!this.layout) {
46987             return; // should not happen..
46988         }
46989         var reg = false;
46990         for (var r in this.layout.regions) {
46991             reg = this.layout.getRegion(r);
46992             if (reg.getActivePanel()) {
46993                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46994                 reg.setActivePanel(reg.getActivePanel());
46995                 continue;
46996             }
46997             if (!reg.panels.length) {
46998                 continue;
46999             }
47000             reg.showPanel(reg.getPanel(0));
47001         }
47002         
47003         
47004         
47005         
47006     },
47007     
47008     /**
47009      * Returns the nested BorderLayout for this panel
47010      * @return {Roo.BorderLayout} 
47011      */
47012     getLayout : function(){
47013         return this.layout;
47014     },
47015     
47016      /**
47017      * Adds a xtype elements to the layout of the nested panel
47018      * <pre><code>
47019
47020 panel.addxtype({
47021        xtype : 'ContentPanel',
47022        region: 'west',
47023        items: [ .... ]
47024    }
47025 );
47026
47027 panel.addxtype({
47028         xtype : 'NestedLayoutPanel',
47029         region: 'west',
47030         layout: {
47031            center: { },
47032            west: { }   
47033         },
47034         items : [ ... list of content panels or nested layout panels.. ]
47035    }
47036 );
47037 </code></pre>
47038      * @param {Object} cfg Xtype definition of item to add.
47039      */
47040     addxtype : function(cfg) {
47041         return this.layout.addxtype(cfg);
47042     
47043     }
47044 });
47045
47046 Roo.ScrollPanel = function(el, config, content){
47047     config = config || {};
47048     config.fitToFrame = true;
47049     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47050     
47051     this.el.dom.style.overflow = "hidden";
47052     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47053     this.el.removeClass("x-layout-inactive-content");
47054     this.el.on("mousewheel", this.onWheel, this);
47055
47056     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47057     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47058     up.unselectable(); down.unselectable();
47059     up.on("click", this.scrollUp, this);
47060     down.on("click", this.scrollDown, this);
47061     up.addClassOnOver("x-scroller-btn-over");
47062     down.addClassOnOver("x-scroller-btn-over");
47063     up.addClassOnClick("x-scroller-btn-click");
47064     down.addClassOnClick("x-scroller-btn-click");
47065     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47066
47067     this.resizeEl = this.el;
47068     this.el = wrap; this.up = up; this.down = down;
47069 };
47070
47071 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47072     increment : 100,
47073     wheelIncrement : 5,
47074     scrollUp : function(){
47075         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47076     },
47077
47078     scrollDown : function(){
47079         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47080     },
47081
47082     afterScroll : function(){
47083         var el = this.resizeEl;
47084         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47085         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47086         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47087     },
47088
47089     setSize : function(){
47090         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47091         this.afterScroll();
47092     },
47093
47094     onWheel : function(e){
47095         var d = e.getWheelDelta();
47096         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47097         this.afterScroll();
47098         e.stopEvent();
47099     },
47100
47101     setContent : function(content, loadScripts){
47102         this.resizeEl.update(content, loadScripts);
47103     }
47104
47105 });
47106
47107
47108
47109
47110
47111
47112
47113
47114
47115 /**
47116  * @class Roo.TreePanel
47117  * @extends Roo.ContentPanel
47118  * @constructor
47119  * Create a new TreePanel. - defaults to fit/scoll contents.
47120  * @param {String/Object} config A string to set only the panel's title, or a config object
47121  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47122  */
47123 Roo.TreePanel = function(config){
47124     var el = config.el;
47125     var tree = config.tree;
47126     delete config.tree; 
47127     delete config.el; // hopefull!
47128     
47129     // wrapper for IE7 strict & safari scroll issue
47130     
47131     var treeEl = el.createChild();
47132     config.resizeEl = treeEl;
47133     
47134     
47135     
47136     Roo.TreePanel.superclass.constructor.call(this, el, config);
47137  
47138  
47139     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47140     //console.log(tree);
47141     this.on('activate', function()
47142     {
47143         if (this.tree.rendered) {
47144             return;
47145         }
47146         //console.log('render tree');
47147         this.tree.render();
47148     });
47149     
47150     this.on('resize',  function (cp, w, h) {
47151             this.tree.innerCt.setWidth(w);
47152             this.tree.innerCt.setHeight(h);
47153             this.tree.innerCt.setStyle('overflow-y', 'auto');
47154     });
47155
47156         
47157     
47158 };
47159
47160 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47161     fitToFrame : true,
47162     autoScroll : true
47163 });
47164
47165
47166
47167
47168
47169
47170
47171
47172
47173
47174
47175 /*
47176  * Based on:
47177  * Ext JS Library 1.1.1
47178  * Copyright(c) 2006-2007, Ext JS, LLC.
47179  *
47180  * Originally Released Under LGPL - original licence link has changed is not relivant.
47181  *
47182  * Fork - LGPL
47183  * <script type="text/javascript">
47184  */
47185  
47186
47187 /**
47188  * @class Roo.ReaderLayout
47189  * @extends Roo.BorderLayout
47190  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47191  * center region containing two nested regions (a top one for a list view and one for item preview below),
47192  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47193  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47194  * expedites the setup of the overall layout and regions for this common application style.
47195  * Example:
47196  <pre><code>
47197 var reader = new Roo.ReaderLayout();
47198 var CP = Roo.ContentPanel;  // shortcut for adding
47199
47200 reader.beginUpdate();
47201 reader.add("north", new CP("north", "North"));
47202 reader.add("west", new CP("west", {title: "West"}));
47203 reader.add("east", new CP("east", {title: "East"}));
47204
47205 reader.regions.listView.add(new CP("listView", "List"));
47206 reader.regions.preview.add(new CP("preview", "Preview"));
47207 reader.endUpdate();
47208 </code></pre>
47209 * @constructor
47210 * Create a new ReaderLayout
47211 * @param {Object} config Configuration options
47212 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47213 * document.body if omitted)
47214 */
47215 Roo.ReaderLayout = function(config, renderTo){
47216     var c = config || {size:{}};
47217     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47218         north: c.north !== false ? Roo.apply({
47219             split:false,
47220             initialSize: 32,
47221             titlebar: false
47222         }, c.north) : false,
47223         west: c.west !== false ? Roo.apply({
47224             split:true,
47225             initialSize: 200,
47226             minSize: 175,
47227             maxSize: 400,
47228             titlebar: true,
47229             collapsible: true,
47230             animate: true,
47231             margins:{left:5,right:0,bottom:5,top:5},
47232             cmargins:{left:5,right:5,bottom:5,top:5}
47233         }, c.west) : false,
47234         east: c.east !== false ? Roo.apply({
47235             split:true,
47236             initialSize: 200,
47237             minSize: 175,
47238             maxSize: 400,
47239             titlebar: true,
47240             collapsible: true,
47241             animate: true,
47242             margins:{left:0,right:5,bottom:5,top:5},
47243             cmargins:{left:5,right:5,bottom:5,top:5}
47244         }, c.east) : false,
47245         center: Roo.apply({
47246             tabPosition: 'top',
47247             autoScroll:false,
47248             closeOnTab: true,
47249             titlebar:false,
47250             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47251         }, c.center)
47252     });
47253
47254     this.el.addClass('x-reader');
47255
47256     this.beginUpdate();
47257
47258     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47259         south: c.preview !== false ? Roo.apply({
47260             split:true,
47261             initialSize: 200,
47262             minSize: 100,
47263             autoScroll:true,
47264             collapsible:true,
47265             titlebar: true,
47266             cmargins:{top:5,left:0, right:0, bottom:0}
47267         }, c.preview) : false,
47268         center: Roo.apply({
47269             autoScroll:false,
47270             titlebar:false,
47271             minHeight:200
47272         }, c.listView)
47273     });
47274     this.add('center', new Roo.NestedLayoutPanel(inner,
47275             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47276
47277     this.endUpdate();
47278
47279     this.regions.preview = inner.getRegion('south');
47280     this.regions.listView = inner.getRegion('center');
47281 };
47282
47283 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47284  * Based on:
47285  * Ext JS Library 1.1.1
47286  * Copyright(c) 2006-2007, Ext JS, LLC.
47287  *
47288  * Originally Released Under LGPL - original licence link has changed is not relivant.
47289  *
47290  * Fork - LGPL
47291  * <script type="text/javascript">
47292  */
47293  
47294 /**
47295  * @class Roo.grid.Grid
47296  * @extends Roo.util.Observable
47297  * This class represents the primary interface of a component based grid control.
47298  * <br><br>Usage:<pre><code>
47299  var grid = new Roo.grid.Grid("my-container-id", {
47300      ds: myDataStore,
47301      cm: myColModel,
47302      selModel: mySelectionModel,
47303      autoSizeColumns: true,
47304      monitorWindowResize: false,
47305      trackMouseOver: true
47306  });
47307  // set any options
47308  grid.render();
47309  * </code></pre>
47310  * <b>Common Problems:</b><br/>
47311  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47312  * element will correct this<br/>
47313  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47314  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47315  * are unpredictable.<br/>
47316  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47317  * grid to calculate dimensions/offsets.<br/>
47318   * @constructor
47319  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47320  * The container MUST have some type of size defined for the grid to fill. The container will be
47321  * automatically set to position relative if it isn't already.
47322  * @param {Object} config A config object that sets properties on this grid.
47323  */
47324 Roo.grid.Grid = function(container, config){
47325         // initialize the container
47326         this.container = Roo.get(container);
47327         this.container.update("");
47328         this.container.setStyle("overflow", "hidden");
47329     this.container.addClass('x-grid-container');
47330
47331     this.id = this.container.id;
47332
47333     Roo.apply(this, config);
47334     // check and correct shorthanded configs
47335     if(this.ds){
47336         this.dataSource = this.ds;
47337         delete this.ds;
47338     }
47339     if(this.cm){
47340         this.colModel = this.cm;
47341         delete this.cm;
47342     }
47343     if(this.sm){
47344         this.selModel = this.sm;
47345         delete this.sm;
47346     }
47347
47348     if (this.selModel) {
47349         this.selModel = Roo.factory(this.selModel, Roo.grid);
47350         this.sm = this.selModel;
47351         this.sm.xmodule = this.xmodule || false;
47352     }
47353     if (typeof(this.colModel.config) == 'undefined') {
47354         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47355         this.cm = this.colModel;
47356         this.cm.xmodule = this.xmodule || false;
47357     }
47358     if (this.dataSource) {
47359         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47360         this.ds = this.dataSource;
47361         this.ds.xmodule = this.xmodule || false;
47362          
47363     }
47364     
47365     
47366     
47367     if(this.width){
47368         this.container.setWidth(this.width);
47369     }
47370
47371     if(this.height){
47372         this.container.setHeight(this.height);
47373     }
47374     /** @private */
47375         this.addEvents({
47376         // raw events
47377         /**
47378          * @event click
47379          * The raw click event for the entire grid.
47380          * @param {Roo.EventObject} e
47381          */
47382         "click" : true,
47383         /**
47384          * @event dblclick
47385          * The raw dblclick event for the entire grid.
47386          * @param {Roo.EventObject} e
47387          */
47388         "dblclick" : true,
47389         /**
47390          * @event contextmenu
47391          * The raw contextmenu event for the entire grid.
47392          * @param {Roo.EventObject} e
47393          */
47394         "contextmenu" : true,
47395         /**
47396          * @event mousedown
47397          * The raw mousedown event for the entire grid.
47398          * @param {Roo.EventObject} e
47399          */
47400         "mousedown" : true,
47401         /**
47402          * @event mouseup
47403          * The raw mouseup event for the entire grid.
47404          * @param {Roo.EventObject} e
47405          */
47406         "mouseup" : true,
47407         /**
47408          * @event mouseover
47409          * The raw mouseover event for the entire grid.
47410          * @param {Roo.EventObject} e
47411          */
47412         "mouseover" : true,
47413         /**
47414          * @event mouseout
47415          * The raw mouseout event for the entire grid.
47416          * @param {Roo.EventObject} e
47417          */
47418         "mouseout" : true,
47419         /**
47420          * @event keypress
47421          * The raw keypress event for the entire grid.
47422          * @param {Roo.EventObject} e
47423          */
47424         "keypress" : true,
47425         /**
47426          * @event keydown
47427          * The raw keydown event for the entire grid.
47428          * @param {Roo.EventObject} e
47429          */
47430         "keydown" : true,
47431
47432         // custom events
47433
47434         /**
47435          * @event cellclick
47436          * Fires when a cell is clicked
47437          * @param {Grid} this
47438          * @param {Number} rowIndex
47439          * @param {Number} columnIndex
47440          * @param {Roo.EventObject} e
47441          */
47442         "cellclick" : true,
47443         /**
47444          * @event celldblclick
47445          * Fires when a cell is double clicked
47446          * @param {Grid} this
47447          * @param {Number} rowIndex
47448          * @param {Number} columnIndex
47449          * @param {Roo.EventObject} e
47450          */
47451         "celldblclick" : true,
47452         /**
47453          * @event rowclick
47454          * Fires when a row is clicked
47455          * @param {Grid} this
47456          * @param {Number} rowIndex
47457          * @param {Roo.EventObject} e
47458          */
47459         "rowclick" : true,
47460         /**
47461          * @event rowdblclick
47462          * Fires when a row is double clicked
47463          * @param {Grid} this
47464          * @param {Number} rowIndex
47465          * @param {Roo.EventObject} e
47466          */
47467         "rowdblclick" : true,
47468         /**
47469          * @event headerclick
47470          * Fires when a header is clicked
47471          * @param {Grid} this
47472          * @param {Number} columnIndex
47473          * @param {Roo.EventObject} e
47474          */
47475         "headerclick" : true,
47476         /**
47477          * @event headerdblclick
47478          * Fires when a header cell is double clicked
47479          * @param {Grid} this
47480          * @param {Number} columnIndex
47481          * @param {Roo.EventObject} e
47482          */
47483         "headerdblclick" : true,
47484         /**
47485          * @event rowcontextmenu
47486          * Fires when a row is right clicked
47487          * @param {Grid} this
47488          * @param {Number} rowIndex
47489          * @param {Roo.EventObject} e
47490          */
47491         "rowcontextmenu" : true,
47492         /**
47493          * @event cellcontextmenu
47494          * Fires when a cell is right clicked
47495          * @param {Grid} this
47496          * @param {Number} rowIndex
47497          * @param {Number} cellIndex
47498          * @param {Roo.EventObject} e
47499          */
47500          "cellcontextmenu" : true,
47501         /**
47502          * @event headercontextmenu
47503          * Fires when a header is right clicked
47504          * @param {Grid} this
47505          * @param {Number} columnIndex
47506          * @param {Roo.EventObject} e
47507          */
47508         "headercontextmenu" : true,
47509         /**
47510          * @event bodyscroll
47511          * Fires when the body element is scrolled
47512          * @param {Number} scrollLeft
47513          * @param {Number} scrollTop
47514          */
47515         "bodyscroll" : true,
47516         /**
47517          * @event columnresize
47518          * Fires when the user resizes a column
47519          * @param {Number} columnIndex
47520          * @param {Number} newSize
47521          */
47522         "columnresize" : true,
47523         /**
47524          * @event columnmove
47525          * Fires when the user moves a column
47526          * @param {Number} oldIndex
47527          * @param {Number} newIndex
47528          */
47529         "columnmove" : true,
47530         /**
47531          * @event startdrag
47532          * Fires when row(s) start being dragged
47533          * @param {Grid} this
47534          * @param {Roo.GridDD} dd The drag drop object
47535          * @param {event} e The raw browser event
47536          */
47537         "startdrag" : true,
47538         /**
47539          * @event enddrag
47540          * Fires when a drag operation is complete
47541          * @param {Grid} this
47542          * @param {Roo.GridDD} dd The drag drop object
47543          * @param {event} e The raw browser event
47544          */
47545         "enddrag" : true,
47546         /**
47547          * @event dragdrop
47548          * Fires when dragged row(s) are dropped on a valid DD target
47549          * @param {Grid} this
47550          * @param {Roo.GridDD} dd The drag drop object
47551          * @param {String} targetId The target drag drop object
47552          * @param {event} e The raw browser event
47553          */
47554         "dragdrop" : true,
47555         /**
47556          * @event dragover
47557          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47558          * @param {Grid} this
47559          * @param {Roo.GridDD} dd The drag drop object
47560          * @param {String} targetId The target drag drop object
47561          * @param {event} e The raw browser event
47562          */
47563         "dragover" : true,
47564         /**
47565          * @event dragenter
47566          *  Fires when the dragged row(s) first cross another DD target while being dragged
47567          * @param {Grid} this
47568          * @param {Roo.GridDD} dd The drag drop object
47569          * @param {String} targetId The target drag drop object
47570          * @param {event} e The raw browser event
47571          */
47572         "dragenter" : true,
47573         /**
47574          * @event dragout
47575          * Fires when the dragged row(s) leave another DD target while being dragged
47576          * @param {Grid} this
47577          * @param {Roo.GridDD} dd The drag drop object
47578          * @param {String} targetId The target drag drop object
47579          * @param {event} e The raw browser event
47580          */
47581         "dragout" : true,
47582         /**
47583          * @event rowclass
47584          * Fires when a row is rendered, so you can change add a style to it.
47585          * @param {GridView} gridview   The grid view
47586          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47587          */
47588         'rowclass' : true,
47589
47590         /**
47591          * @event render
47592          * Fires when the grid is rendered
47593          * @param {Grid} grid
47594          */
47595         'render' : true
47596     });
47597
47598     Roo.grid.Grid.superclass.constructor.call(this);
47599 };
47600 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47601     
47602     /**
47603      * @cfg {String} ddGroup - drag drop group.
47604      */
47605
47606     /**
47607      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47608      */
47609     minColumnWidth : 25,
47610
47611     /**
47612      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47613      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47614      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47615      */
47616     autoSizeColumns : false,
47617
47618     /**
47619      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47620      */
47621     autoSizeHeaders : true,
47622
47623     /**
47624      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47625      */
47626     monitorWindowResize : true,
47627
47628     /**
47629      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47630      * rows measured to get a columns size. Default is 0 (all rows).
47631      */
47632     maxRowsToMeasure : 0,
47633
47634     /**
47635      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47636      */
47637     trackMouseOver : true,
47638
47639     /**
47640     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47641     */
47642     
47643     /**
47644     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47645     */
47646     enableDragDrop : false,
47647     
47648     /**
47649     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47650     */
47651     enableColumnMove : true,
47652     
47653     /**
47654     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47655     */
47656     enableColumnHide : true,
47657     
47658     /**
47659     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47660     */
47661     enableRowHeightSync : false,
47662     
47663     /**
47664     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47665     */
47666     stripeRows : true,
47667     
47668     /**
47669     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47670     */
47671     autoHeight : false,
47672
47673     /**
47674      * @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.
47675      */
47676     autoExpandColumn : false,
47677
47678     /**
47679     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47680     * Default is 50.
47681     */
47682     autoExpandMin : 50,
47683
47684     /**
47685     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47686     */
47687     autoExpandMax : 1000,
47688
47689     /**
47690     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47691     */
47692     view : null,
47693
47694     /**
47695     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47696     */
47697     loadMask : false,
47698     /**
47699     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47700     */
47701     dropTarget: false,
47702     
47703    
47704     
47705     // private
47706     rendered : false,
47707
47708     /**
47709     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47710     * of a fixed width. Default is false.
47711     */
47712     /**
47713     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47714     */
47715     /**
47716      * Called once after all setup has been completed and the grid is ready to be rendered.
47717      * @return {Roo.grid.Grid} this
47718      */
47719     render : function()
47720     {
47721         var c = this.container;
47722         // try to detect autoHeight/width mode
47723         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47724             this.autoHeight = true;
47725         }
47726         var view = this.getView();
47727         view.init(this);
47728
47729         c.on("click", this.onClick, this);
47730         c.on("dblclick", this.onDblClick, this);
47731         c.on("contextmenu", this.onContextMenu, this);
47732         c.on("keydown", this.onKeyDown, this);
47733
47734         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47735
47736         this.getSelectionModel().init(this);
47737
47738         view.render();
47739
47740         if(this.loadMask){
47741             this.loadMask = new Roo.LoadMask(this.container,
47742                     Roo.apply({store:this.dataSource}, this.loadMask));
47743         }
47744         
47745         
47746         if (this.toolbar && this.toolbar.xtype) {
47747             this.toolbar.container = this.getView().getHeaderPanel(true);
47748             this.toolbar = new Roo.Toolbar(this.toolbar);
47749         }
47750         if (this.footer && this.footer.xtype) {
47751             this.footer.dataSource = this.getDataSource();
47752             this.footer.container = this.getView().getFooterPanel(true);
47753             this.footer = Roo.factory(this.footer, Roo);
47754         }
47755         if (this.dropTarget && this.dropTarget.xtype) {
47756             delete this.dropTarget.xtype;
47757             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47758         }
47759         
47760         
47761         this.rendered = true;
47762         this.fireEvent('render', this);
47763         return this;
47764     },
47765
47766         /**
47767          * Reconfigures the grid to use a different Store and Column Model.
47768          * The View will be bound to the new objects and refreshed.
47769          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47770          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47771          */
47772     reconfigure : function(dataSource, colModel){
47773         if(this.loadMask){
47774             this.loadMask.destroy();
47775             this.loadMask = new Roo.LoadMask(this.container,
47776                     Roo.apply({store:dataSource}, this.loadMask));
47777         }
47778         this.view.bind(dataSource, colModel);
47779         this.dataSource = dataSource;
47780         this.colModel = colModel;
47781         this.view.refresh(true);
47782     },
47783
47784     // private
47785     onKeyDown : function(e){
47786         this.fireEvent("keydown", e);
47787     },
47788
47789     /**
47790      * Destroy this grid.
47791      * @param {Boolean} removeEl True to remove the element
47792      */
47793     destroy : function(removeEl, keepListeners){
47794         if(this.loadMask){
47795             this.loadMask.destroy();
47796         }
47797         var c = this.container;
47798         c.removeAllListeners();
47799         this.view.destroy();
47800         this.colModel.purgeListeners();
47801         if(!keepListeners){
47802             this.purgeListeners();
47803         }
47804         c.update("");
47805         if(removeEl === true){
47806             c.remove();
47807         }
47808     },
47809
47810     // private
47811     processEvent : function(name, e){
47812         this.fireEvent(name, e);
47813         var t = e.getTarget();
47814         var v = this.view;
47815         var header = v.findHeaderIndex(t);
47816         if(header !== false){
47817             this.fireEvent("header" + name, this, header, e);
47818         }else{
47819             var row = v.findRowIndex(t);
47820             var cell = v.findCellIndex(t);
47821             if(row !== false){
47822                 this.fireEvent("row" + name, this, row, e);
47823                 if(cell !== false){
47824                     this.fireEvent("cell" + name, this, row, cell, e);
47825                 }
47826             }
47827         }
47828     },
47829
47830     // private
47831     onClick : function(e){
47832         this.processEvent("click", e);
47833     },
47834
47835     // private
47836     onContextMenu : function(e, t){
47837         this.processEvent("contextmenu", e);
47838     },
47839
47840     // private
47841     onDblClick : function(e){
47842         this.processEvent("dblclick", e);
47843     },
47844
47845     // private
47846     walkCells : function(row, col, step, fn, scope){
47847         var cm = this.colModel, clen = cm.getColumnCount();
47848         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47849         if(step < 0){
47850             if(col < 0){
47851                 row--;
47852                 first = false;
47853             }
47854             while(row >= 0){
47855                 if(!first){
47856                     col = clen-1;
47857                 }
47858                 first = false;
47859                 while(col >= 0){
47860                     if(fn.call(scope || this, row, col, cm) === true){
47861                         return [row, col];
47862                     }
47863                     col--;
47864                 }
47865                 row--;
47866             }
47867         } else {
47868             if(col >= clen){
47869                 row++;
47870                 first = false;
47871             }
47872             while(row < rlen){
47873                 if(!first){
47874                     col = 0;
47875                 }
47876                 first = false;
47877                 while(col < clen){
47878                     if(fn.call(scope || this, row, col, cm) === true){
47879                         return [row, col];
47880                     }
47881                     col++;
47882                 }
47883                 row++;
47884             }
47885         }
47886         return null;
47887     },
47888
47889     // private
47890     getSelections : function(){
47891         return this.selModel.getSelections();
47892     },
47893
47894     /**
47895      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47896      * but if manual update is required this method will initiate it.
47897      */
47898     autoSize : function(){
47899         if(this.rendered){
47900             this.view.layout();
47901             if(this.view.adjustForScroll){
47902                 this.view.adjustForScroll();
47903             }
47904         }
47905     },
47906
47907     /**
47908      * Returns the grid's underlying element.
47909      * @return {Element} The element
47910      */
47911     getGridEl : function(){
47912         return this.container;
47913     },
47914
47915     // private for compatibility, overridden by editor grid
47916     stopEditing : function(){},
47917
47918     /**
47919      * Returns the grid's SelectionModel.
47920      * @return {SelectionModel}
47921      */
47922     getSelectionModel : function(){
47923         if(!this.selModel){
47924             this.selModel = new Roo.grid.RowSelectionModel();
47925         }
47926         return this.selModel;
47927     },
47928
47929     /**
47930      * Returns the grid's DataSource.
47931      * @return {DataSource}
47932      */
47933     getDataSource : function(){
47934         return this.dataSource;
47935     },
47936
47937     /**
47938      * Returns the grid's ColumnModel.
47939      * @return {ColumnModel}
47940      */
47941     getColumnModel : function(){
47942         return this.colModel;
47943     },
47944
47945     /**
47946      * Returns the grid's GridView object.
47947      * @return {GridView}
47948      */
47949     getView : function(){
47950         if(!this.view){
47951             this.view = new Roo.grid.GridView(this.viewConfig);
47952         }
47953         return this.view;
47954     },
47955     /**
47956      * Called to get grid's drag proxy text, by default returns this.ddText.
47957      * @return {String}
47958      */
47959     getDragDropText : function(){
47960         var count = this.selModel.getCount();
47961         return String.format(this.ddText, count, count == 1 ? '' : 's');
47962     }
47963 });
47964 /**
47965  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47966  * %0 is replaced with the number of selected rows.
47967  * @type String
47968  */
47969 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47970  * Based on:
47971  * Ext JS Library 1.1.1
47972  * Copyright(c) 2006-2007, Ext JS, LLC.
47973  *
47974  * Originally Released Under LGPL - original licence link has changed is not relivant.
47975  *
47976  * Fork - LGPL
47977  * <script type="text/javascript">
47978  */
47979  
47980 Roo.grid.AbstractGridView = function(){
47981         this.grid = null;
47982         
47983         this.events = {
47984             "beforerowremoved" : true,
47985             "beforerowsinserted" : true,
47986             "beforerefresh" : true,
47987             "rowremoved" : true,
47988             "rowsinserted" : true,
47989             "rowupdated" : true,
47990             "refresh" : true
47991         };
47992     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47993 };
47994
47995 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47996     rowClass : "x-grid-row",
47997     cellClass : "x-grid-cell",
47998     tdClass : "x-grid-td",
47999     hdClass : "x-grid-hd",
48000     splitClass : "x-grid-hd-split",
48001     
48002         init: function(grid){
48003         this.grid = grid;
48004                 var cid = this.grid.getGridEl().id;
48005         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48006         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48007         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48008         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48009         },
48010         
48011         getColumnRenderers : function(){
48012         var renderers = [];
48013         var cm = this.grid.colModel;
48014         var colCount = cm.getColumnCount();
48015         for(var i = 0; i < colCount; i++){
48016             renderers[i] = cm.getRenderer(i);
48017         }
48018         return renderers;
48019     },
48020     
48021     getColumnIds : function(){
48022         var ids = [];
48023         var cm = this.grid.colModel;
48024         var colCount = cm.getColumnCount();
48025         for(var i = 0; i < colCount; i++){
48026             ids[i] = cm.getColumnId(i);
48027         }
48028         return ids;
48029     },
48030     
48031     getDataIndexes : function(){
48032         if(!this.indexMap){
48033             this.indexMap = this.buildIndexMap();
48034         }
48035         return this.indexMap.colToData;
48036     },
48037     
48038     getColumnIndexByDataIndex : function(dataIndex){
48039         if(!this.indexMap){
48040             this.indexMap = this.buildIndexMap();
48041         }
48042         return this.indexMap.dataToCol[dataIndex];
48043     },
48044     
48045     /**
48046      * Set a css style for a column dynamically. 
48047      * @param {Number} colIndex The index of the column
48048      * @param {String} name The css property name
48049      * @param {String} value The css value
48050      */
48051     setCSSStyle : function(colIndex, name, value){
48052         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48053         Roo.util.CSS.updateRule(selector, name, value);
48054     },
48055     
48056     generateRules : function(cm){
48057         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48058         Roo.util.CSS.removeStyleSheet(rulesId);
48059         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48060             var cid = cm.getColumnId(i);
48061             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48062                          this.tdSelector, cid, " {\n}\n",
48063                          this.hdSelector, cid, " {\n}\n",
48064                          this.splitSelector, cid, " {\n}\n");
48065         }
48066         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48067     }
48068 });/*
48069  * Based on:
48070  * Ext JS Library 1.1.1
48071  * Copyright(c) 2006-2007, Ext JS, LLC.
48072  *
48073  * Originally Released Under LGPL - original licence link has changed is not relivant.
48074  *
48075  * Fork - LGPL
48076  * <script type="text/javascript">
48077  */
48078
48079 // private
48080 // This is a support class used internally by the Grid components
48081 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48082     this.grid = grid;
48083     this.view = grid.getView();
48084     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48085     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48086     if(hd2){
48087         this.setHandleElId(Roo.id(hd));
48088         this.setOuterHandleElId(Roo.id(hd2));
48089     }
48090     this.scroll = false;
48091 };
48092 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48093     maxDragWidth: 120,
48094     getDragData : function(e){
48095         var t = Roo.lib.Event.getTarget(e);
48096         var h = this.view.findHeaderCell(t);
48097         if(h){
48098             return {ddel: h.firstChild, header:h};
48099         }
48100         return false;
48101     },
48102
48103     onInitDrag : function(e){
48104         this.view.headersDisabled = true;
48105         var clone = this.dragData.ddel.cloneNode(true);
48106         clone.id = Roo.id();
48107         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48108         this.proxy.update(clone);
48109         return true;
48110     },
48111
48112     afterValidDrop : function(){
48113         var v = this.view;
48114         setTimeout(function(){
48115             v.headersDisabled = false;
48116         }, 50);
48117     },
48118
48119     afterInvalidDrop : function(){
48120         var v = this.view;
48121         setTimeout(function(){
48122             v.headersDisabled = false;
48123         }, 50);
48124     }
48125 });
48126 /*
48127  * Based on:
48128  * Ext JS Library 1.1.1
48129  * Copyright(c) 2006-2007, Ext JS, LLC.
48130  *
48131  * Originally Released Under LGPL - original licence link has changed is not relivant.
48132  *
48133  * Fork - LGPL
48134  * <script type="text/javascript">
48135  */
48136 // private
48137 // This is a support class used internally by the Grid components
48138 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48139     this.grid = grid;
48140     this.view = grid.getView();
48141     // split the proxies so they don't interfere with mouse events
48142     this.proxyTop = Roo.DomHelper.append(document.body, {
48143         cls:"col-move-top", html:"&#160;"
48144     }, true);
48145     this.proxyBottom = Roo.DomHelper.append(document.body, {
48146         cls:"col-move-bottom", html:"&#160;"
48147     }, true);
48148     this.proxyTop.hide = this.proxyBottom.hide = function(){
48149         this.setLeftTop(-100,-100);
48150         this.setStyle("visibility", "hidden");
48151     };
48152     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48153     // temporarily disabled
48154     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48155     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48156 };
48157 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48158     proxyOffsets : [-4, -9],
48159     fly: Roo.Element.fly,
48160
48161     getTargetFromEvent : function(e){
48162         var t = Roo.lib.Event.getTarget(e);
48163         var cindex = this.view.findCellIndex(t);
48164         if(cindex !== false){
48165             return this.view.getHeaderCell(cindex);
48166         }
48167         return null;
48168     },
48169
48170     nextVisible : function(h){
48171         var v = this.view, cm = this.grid.colModel;
48172         h = h.nextSibling;
48173         while(h){
48174             if(!cm.isHidden(v.getCellIndex(h))){
48175                 return h;
48176             }
48177             h = h.nextSibling;
48178         }
48179         return null;
48180     },
48181
48182     prevVisible : function(h){
48183         var v = this.view, cm = this.grid.colModel;
48184         h = h.prevSibling;
48185         while(h){
48186             if(!cm.isHidden(v.getCellIndex(h))){
48187                 return h;
48188             }
48189             h = h.prevSibling;
48190         }
48191         return null;
48192     },
48193
48194     positionIndicator : function(h, n, e){
48195         var x = Roo.lib.Event.getPageX(e);
48196         var r = Roo.lib.Dom.getRegion(n.firstChild);
48197         var px, pt, py = r.top + this.proxyOffsets[1];
48198         if((r.right - x) <= (r.right-r.left)/2){
48199             px = r.right+this.view.borderWidth;
48200             pt = "after";
48201         }else{
48202             px = r.left;
48203             pt = "before";
48204         }
48205         var oldIndex = this.view.getCellIndex(h);
48206         var newIndex = this.view.getCellIndex(n);
48207
48208         if(this.grid.colModel.isFixed(newIndex)){
48209             return false;
48210         }
48211
48212         var locked = this.grid.colModel.isLocked(newIndex);
48213
48214         if(pt == "after"){
48215             newIndex++;
48216         }
48217         if(oldIndex < newIndex){
48218             newIndex--;
48219         }
48220         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48221             return false;
48222         }
48223         px +=  this.proxyOffsets[0];
48224         this.proxyTop.setLeftTop(px, py);
48225         this.proxyTop.show();
48226         if(!this.bottomOffset){
48227             this.bottomOffset = this.view.mainHd.getHeight();
48228         }
48229         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48230         this.proxyBottom.show();
48231         return pt;
48232     },
48233
48234     onNodeEnter : function(n, dd, e, data){
48235         if(data.header != n){
48236             this.positionIndicator(data.header, n, e);
48237         }
48238     },
48239
48240     onNodeOver : function(n, dd, e, data){
48241         var result = false;
48242         if(data.header != n){
48243             result = this.positionIndicator(data.header, n, e);
48244         }
48245         if(!result){
48246             this.proxyTop.hide();
48247             this.proxyBottom.hide();
48248         }
48249         return result ? this.dropAllowed : this.dropNotAllowed;
48250     },
48251
48252     onNodeOut : function(n, dd, e, data){
48253         this.proxyTop.hide();
48254         this.proxyBottom.hide();
48255     },
48256
48257     onNodeDrop : function(n, dd, e, data){
48258         var h = data.header;
48259         if(h != n){
48260             var cm = this.grid.colModel;
48261             var x = Roo.lib.Event.getPageX(e);
48262             var r = Roo.lib.Dom.getRegion(n.firstChild);
48263             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48264             var oldIndex = this.view.getCellIndex(h);
48265             var newIndex = this.view.getCellIndex(n);
48266             var locked = cm.isLocked(newIndex);
48267             if(pt == "after"){
48268                 newIndex++;
48269             }
48270             if(oldIndex < newIndex){
48271                 newIndex--;
48272             }
48273             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48274                 return false;
48275             }
48276             cm.setLocked(oldIndex, locked, true);
48277             cm.moveColumn(oldIndex, newIndex);
48278             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48279             return true;
48280         }
48281         return false;
48282     }
48283 });
48284 /*
48285  * Based on:
48286  * Ext JS Library 1.1.1
48287  * Copyright(c) 2006-2007, Ext JS, LLC.
48288  *
48289  * Originally Released Under LGPL - original licence link has changed is not relivant.
48290  *
48291  * Fork - LGPL
48292  * <script type="text/javascript">
48293  */
48294   
48295 /**
48296  * @class Roo.grid.GridView
48297  * @extends Roo.util.Observable
48298  *
48299  * @constructor
48300  * @param {Object} config
48301  */
48302 Roo.grid.GridView = function(config){
48303     Roo.grid.GridView.superclass.constructor.call(this);
48304     this.el = null;
48305
48306     Roo.apply(this, config);
48307 };
48308
48309 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48310
48311     /**
48312      * Override this function to apply custom css classes to rows during rendering
48313      * @param {Record} record The record
48314      * @param {Number} index
48315      * @method getRowClass
48316      */
48317     rowClass : "x-grid-row",
48318
48319     cellClass : "x-grid-col",
48320
48321     tdClass : "x-grid-td",
48322
48323     hdClass : "x-grid-hd",
48324
48325     splitClass : "x-grid-split",
48326
48327     sortClasses : ["sort-asc", "sort-desc"],
48328
48329     enableMoveAnim : false,
48330
48331     hlColor: "C3DAF9",
48332
48333     dh : Roo.DomHelper,
48334
48335     fly : Roo.Element.fly,
48336
48337     css : Roo.util.CSS,
48338
48339     borderWidth: 1,
48340
48341     splitOffset: 3,
48342
48343     scrollIncrement : 22,
48344
48345     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48346
48347     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48348
48349     bind : function(ds, cm){
48350         if(this.ds){
48351             this.ds.un("load", this.onLoad, this);
48352             this.ds.un("datachanged", this.onDataChange, this);
48353             this.ds.un("add", this.onAdd, this);
48354             this.ds.un("remove", this.onRemove, this);
48355             this.ds.un("update", this.onUpdate, this);
48356             this.ds.un("clear", this.onClear, this);
48357         }
48358         if(ds){
48359             ds.on("load", this.onLoad, this);
48360             ds.on("datachanged", this.onDataChange, this);
48361             ds.on("add", this.onAdd, this);
48362             ds.on("remove", this.onRemove, this);
48363             ds.on("update", this.onUpdate, this);
48364             ds.on("clear", this.onClear, this);
48365         }
48366         this.ds = ds;
48367
48368         if(this.cm){
48369             this.cm.un("widthchange", this.onColWidthChange, this);
48370             this.cm.un("headerchange", this.onHeaderChange, this);
48371             this.cm.un("hiddenchange", this.onHiddenChange, this);
48372             this.cm.un("columnmoved", this.onColumnMove, this);
48373             this.cm.un("columnlockchange", this.onColumnLock, this);
48374         }
48375         if(cm){
48376             this.generateRules(cm);
48377             cm.on("widthchange", this.onColWidthChange, this);
48378             cm.on("headerchange", this.onHeaderChange, this);
48379             cm.on("hiddenchange", this.onHiddenChange, this);
48380             cm.on("columnmoved", this.onColumnMove, this);
48381             cm.on("columnlockchange", this.onColumnLock, this);
48382         }
48383         this.cm = cm;
48384     },
48385
48386     init: function(grid){
48387         Roo.grid.GridView.superclass.init.call(this, grid);
48388
48389         this.bind(grid.dataSource, grid.colModel);
48390
48391         grid.on("headerclick", this.handleHeaderClick, this);
48392
48393         if(grid.trackMouseOver){
48394             grid.on("mouseover", this.onRowOver, this);
48395             grid.on("mouseout", this.onRowOut, this);
48396         }
48397         grid.cancelTextSelection = function(){};
48398         this.gridId = grid.id;
48399
48400         var tpls = this.templates || {};
48401
48402         if(!tpls.master){
48403             tpls.master = new Roo.Template(
48404                '<div class="x-grid" hidefocus="true">',
48405                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48406                   '<div class="x-grid-topbar"></div>',
48407                   '<div class="x-grid-scroller"><div></div></div>',
48408                   '<div class="x-grid-locked">',
48409                       '<div class="x-grid-header">{lockedHeader}</div>',
48410                       '<div class="x-grid-body">{lockedBody}</div>',
48411                   "</div>",
48412                   '<div class="x-grid-viewport">',
48413                       '<div class="x-grid-header">{header}</div>',
48414                       '<div class="x-grid-body">{body}</div>',
48415                   "</div>",
48416                   '<div class="x-grid-bottombar"></div>',
48417                  
48418                   '<div class="x-grid-resize-proxy">&#160;</div>',
48419                "</div>"
48420             );
48421             tpls.master.disableformats = true;
48422         }
48423
48424         if(!tpls.header){
48425             tpls.header = new Roo.Template(
48426                '<table border="0" cellspacing="0" cellpadding="0">',
48427                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48428                "</table>{splits}"
48429             );
48430             tpls.header.disableformats = true;
48431         }
48432         tpls.header.compile();
48433
48434         if(!tpls.hcell){
48435             tpls.hcell = new Roo.Template(
48436                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48437                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48438                 "</div></td>"
48439              );
48440              tpls.hcell.disableFormats = true;
48441         }
48442         tpls.hcell.compile();
48443
48444         if(!tpls.hsplit){
48445             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48446             tpls.hsplit.disableFormats = true;
48447         }
48448         tpls.hsplit.compile();
48449
48450         if(!tpls.body){
48451             tpls.body = new Roo.Template(
48452                '<table border="0" cellspacing="0" cellpadding="0">',
48453                "<tbody>{rows}</tbody>",
48454                "</table>"
48455             );
48456             tpls.body.disableFormats = true;
48457         }
48458         tpls.body.compile();
48459
48460         if(!tpls.row){
48461             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48462             tpls.row.disableFormats = true;
48463         }
48464         tpls.row.compile();
48465
48466         if(!tpls.cell){
48467             tpls.cell = new Roo.Template(
48468                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48469                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48470                 "</td>"
48471             );
48472             tpls.cell.disableFormats = true;
48473         }
48474         tpls.cell.compile();
48475
48476         this.templates = tpls;
48477     },
48478
48479     // remap these for backwards compat
48480     onColWidthChange : function(){
48481         this.updateColumns.apply(this, arguments);
48482     },
48483     onHeaderChange : function(){
48484         this.updateHeaders.apply(this, arguments);
48485     }, 
48486     onHiddenChange : function(){
48487         this.handleHiddenChange.apply(this, arguments);
48488     },
48489     onColumnMove : function(){
48490         this.handleColumnMove.apply(this, arguments);
48491     },
48492     onColumnLock : function(){
48493         this.handleLockChange.apply(this, arguments);
48494     },
48495
48496     onDataChange : function(){
48497         this.refresh();
48498         this.updateHeaderSortState();
48499     },
48500
48501     onClear : function(){
48502         this.refresh();
48503     },
48504
48505     onUpdate : function(ds, record){
48506         this.refreshRow(record);
48507     },
48508
48509     refreshRow : function(record){
48510         var ds = this.ds, index;
48511         if(typeof record == 'number'){
48512             index = record;
48513             record = ds.getAt(index);
48514         }else{
48515             index = ds.indexOf(record);
48516         }
48517         this.insertRows(ds, index, index, true);
48518         this.onRemove(ds, record, index+1, true);
48519         this.syncRowHeights(index, index);
48520         this.layout();
48521         this.fireEvent("rowupdated", this, index, record);
48522     },
48523
48524     onAdd : function(ds, records, index){
48525         this.insertRows(ds, index, index + (records.length-1));
48526     },
48527
48528     onRemove : function(ds, record, index, isUpdate){
48529         if(isUpdate !== true){
48530             this.fireEvent("beforerowremoved", this, index, record);
48531         }
48532         var bt = this.getBodyTable(), lt = this.getLockedTable();
48533         if(bt.rows[index]){
48534             bt.firstChild.removeChild(bt.rows[index]);
48535         }
48536         if(lt.rows[index]){
48537             lt.firstChild.removeChild(lt.rows[index]);
48538         }
48539         if(isUpdate !== true){
48540             this.stripeRows(index);
48541             this.syncRowHeights(index, index);
48542             this.layout();
48543             this.fireEvent("rowremoved", this, index, record);
48544         }
48545     },
48546
48547     onLoad : function(){
48548         this.scrollToTop();
48549     },
48550
48551     /**
48552      * Scrolls the grid to the top
48553      */
48554     scrollToTop : function(){
48555         if(this.scroller){
48556             this.scroller.dom.scrollTop = 0;
48557             this.syncScroll();
48558         }
48559     },
48560
48561     /**
48562      * Gets a panel in the header of the grid that can be used for toolbars etc.
48563      * After modifying the contents of this panel a call to grid.autoSize() may be
48564      * required to register any changes in size.
48565      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48566      * @return Roo.Element
48567      */
48568     getHeaderPanel : function(doShow){
48569         if(doShow){
48570             this.headerPanel.show();
48571         }
48572         return this.headerPanel;
48573     },
48574
48575     /**
48576      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48577      * After modifying the contents of this panel a call to grid.autoSize() may be
48578      * required to register any changes in size.
48579      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48580      * @return Roo.Element
48581      */
48582     getFooterPanel : function(doShow){
48583         if(doShow){
48584             this.footerPanel.show();
48585         }
48586         return this.footerPanel;
48587     },
48588
48589     initElements : function(){
48590         var E = Roo.Element;
48591         var el = this.grid.getGridEl().dom.firstChild;
48592         var cs = el.childNodes;
48593
48594         this.el = new E(el);
48595         
48596          this.focusEl = new E(el.firstChild);
48597         this.focusEl.swallowEvent("click", true);
48598         
48599         this.headerPanel = new E(cs[1]);
48600         this.headerPanel.enableDisplayMode("block");
48601
48602         this.scroller = new E(cs[2]);
48603         this.scrollSizer = new E(this.scroller.dom.firstChild);
48604
48605         this.lockedWrap = new E(cs[3]);
48606         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48607         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48608
48609         this.mainWrap = new E(cs[4]);
48610         this.mainHd = new E(this.mainWrap.dom.firstChild);
48611         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48612
48613         this.footerPanel = new E(cs[5]);
48614         this.footerPanel.enableDisplayMode("block");
48615
48616         this.resizeProxy = new E(cs[6]);
48617
48618         this.headerSelector = String.format(
48619            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48620            this.lockedHd.id, this.mainHd.id
48621         );
48622
48623         this.splitterSelector = String.format(
48624            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48625            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48626         );
48627     },
48628     idToCssName : function(s)
48629     {
48630         return s.replace(/[^a-z0-9]+/ig, '-');
48631     },
48632
48633     getHeaderCell : function(index){
48634         return Roo.DomQuery.select(this.headerSelector)[index];
48635     },
48636
48637     getHeaderCellMeasure : function(index){
48638         return this.getHeaderCell(index).firstChild;
48639     },
48640
48641     getHeaderCellText : function(index){
48642         return this.getHeaderCell(index).firstChild.firstChild;
48643     },
48644
48645     getLockedTable : function(){
48646         return this.lockedBody.dom.firstChild;
48647     },
48648
48649     getBodyTable : function(){
48650         return this.mainBody.dom.firstChild;
48651     },
48652
48653     getLockedRow : function(index){
48654         return this.getLockedTable().rows[index];
48655     },
48656
48657     getRow : function(index){
48658         return this.getBodyTable().rows[index];
48659     },
48660
48661     getRowComposite : function(index){
48662         if(!this.rowEl){
48663             this.rowEl = new Roo.CompositeElementLite();
48664         }
48665         var els = [], lrow, mrow;
48666         if(lrow = this.getLockedRow(index)){
48667             els.push(lrow);
48668         }
48669         if(mrow = this.getRow(index)){
48670             els.push(mrow);
48671         }
48672         this.rowEl.elements = els;
48673         return this.rowEl;
48674     },
48675     /**
48676      * Gets the 'td' of the cell
48677      * 
48678      * @param {Integer} rowIndex row to select
48679      * @param {Integer} colIndex column to select
48680      * 
48681      * @return {Object} 
48682      */
48683     getCell : function(rowIndex, colIndex){
48684         var locked = this.cm.getLockedCount();
48685         var source;
48686         if(colIndex < locked){
48687             source = this.lockedBody.dom.firstChild;
48688         }else{
48689             source = this.mainBody.dom.firstChild;
48690             colIndex -= locked;
48691         }
48692         return source.rows[rowIndex].childNodes[colIndex];
48693     },
48694
48695     getCellText : function(rowIndex, colIndex){
48696         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48697     },
48698
48699     getCellBox : function(cell){
48700         var b = this.fly(cell).getBox();
48701         if(Roo.isOpera){ // opera fails to report the Y
48702             b.y = cell.offsetTop + this.mainBody.getY();
48703         }
48704         return b;
48705     },
48706
48707     getCellIndex : function(cell){
48708         var id = String(cell.className).match(this.cellRE);
48709         if(id){
48710             return parseInt(id[1], 10);
48711         }
48712         return 0;
48713     },
48714
48715     findHeaderIndex : function(n){
48716         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48717         return r ? this.getCellIndex(r) : false;
48718     },
48719
48720     findHeaderCell : function(n){
48721         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48722         return r ? r : false;
48723     },
48724
48725     findRowIndex : function(n){
48726         if(!n){
48727             return false;
48728         }
48729         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48730         return r ? r.rowIndex : false;
48731     },
48732
48733     findCellIndex : function(node){
48734         var stop = this.el.dom;
48735         while(node && node != stop){
48736             if(this.findRE.test(node.className)){
48737                 return this.getCellIndex(node);
48738             }
48739             node = node.parentNode;
48740         }
48741         return false;
48742     },
48743
48744     getColumnId : function(index){
48745         return this.cm.getColumnId(index);
48746     },
48747
48748     getSplitters : function()
48749     {
48750         if(this.splitterSelector){
48751            return Roo.DomQuery.select(this.splitterSelector);
48752         }else{
48753             return null;
48754       }
48755     },
48756
48757     getSplitter : function(index){
48758         return this.getSplitters()[index];
48759     },
48760
48761     onRowOver : function(e, t){
48762         var row;
48763         if((row = this.findRowIndex(t)) !== false){
48764             this.getRowComposite(row).addClass("x-grid-row-over");
48765         }
48766     },
48767
48768     onRowOut : function(e, t){
48769         var row;
48770         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48771             this.getRowComposite(row).removeClass("x-grid-row-over");
48772         }
48773     },
48774
48775     renderHeaders : function(){
48776         var cm = this.cm;
48777         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48778         var cb = [], lb = [], sb = [], lsb = [], p = {};
48779         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48780             p.cellId = "x-grid-hd-0-" + i;
48781             p.splitId = "x-grid-csplit-0-" + i;
48782             p.id = cm.getColumnId(i);
48783             p.title = cm.getColumnTooltip(i) || "";
48784             p.value = cm.getColumnHeader(i) || "";
48785             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48786             if(!cm.isLocked(i)){
48787                 cb[cb.length] = ct.apply(p);
48788                 sb[sb.length] = st.apply(p);
48789             }else{
48790                 lb[lb.length] = ct.apply(p);
48791                 lsb[lsb.length] = st.apply(p);
48792             }
48793         }
48794         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48795                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48796     },
48797
48798     updateHeaders : function(){
48799         var html = this.renderHeaders();
48800         this.lockedHd.update(html[0]);
48801         this.mainHd.update(html[1]);
48802     },
48803
48804     /**
48805      * Focuses the specified row.
48806      * @param {Number} row The row index
48807      */
48808     focusRow : function(row)
48809     {
48810         //Roo.log('GridView.focusRow');
48811         var x = this.scroller.dom.scrollLeft;
48812         this.focusCell(row, 0, false);
48813         this.scroller.dom.scrollLeft = x;
48814     },
48815
48816     /**
48817      * Focuses the specified cell.
48818      * @param {Number} row The row index
48819      * @param {Number} col The column index
48820      * @param {Boolean} hscroll false to disable horizontal scrolling
48821      */
48822     focusCell : function(row, col, hscroll)
48823     {
48824         //Roo.log('GridView.focusCell');
48825         var el = this.ensureVisible(row, col, hscroll);
48826         this.focusEl.alignTo(el, "tl-tl");
48827         if(Roo.isGecko){
48828             this.focusEl.focus();
48829         }else{
48830             this.focusEl.focus.defer(1, this.focusEl);
48831         }
48832     },
48833
48834     /**
48835      * Scrolls the specified cell into view
48836      * @param {Number} row The row index
48837      * @param {Number} col The column index
48838      * @param {Boolean} hscroll false to disable horizontal scrolling
48839      */
48840     ensureVisible : function(row, col, hscroll)
48841     {
48842         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48843         //return null; //disable for testing.
48844         if(typeof row != "number"){
48845             row = row.rowIndex;
48846         }
48847         if(row < 0 && row >= this.ds.getCount()){
48848             return  null;
48849         }
48850         col = (col !== undefined ? col : 0);
48851         var cm = this.grid.colModel;
48852         while(cm.isHidden(col)){
48853             col++;
48854         }
48855
48856         var el = this.getCell(row, col);
48857         if(!el){
48858             return null;
48859         }
48860         var c = this.scroller.dom;
48861
48862         var ctop = parseInt(el.offsetTop, 10);
48863         var cleft = parseInt(el.offsetLeft, 10);
48864         var cbot = ctop + el.offsetHeight;
48865         var cright = cleft + el.offsetWidth;
48866         
48867         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48868         var stop = parseInt(c.scrollTop, 10);
48869         var sleft = parseInt(c.scrollLeft, 10);
48870         var sbot = stop + ch;
48871         var sright = sleft + c.clientWidth;
48872         /*
48873         Roo.log('GridView.ensureVisible:' +
48874                 ' ctop:' + ctop +
48875                 ' c.clientHeight:' + c.clientHeight +
48876                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48877                 ' stop:' + stop +
48878                 ' cbot:' + cbot +
48879                 ' sbot:' + sbot +
48880                 ' ch:' + ch  
48881                 );
48882         */
48883         if(ctop < stop){
48884              c.scrollTop = ctop;
48885             //Roo.log("set scrolltop to ctop DISABLE?");
48886         }else if(cbot > sbot){
48887             //Roo.log("set scrolltop to cbot-ch");
48888             c.scrollTop = cbot-ch;
48889         }
48890         
48891         if(hscroll !== false){
48892             if(cleft < sleft){
48893                 c.scrollLeft = cleft;
48894             }else if(cright > sright){
48895                 c.scrollLeft = cright-c.clientWidth;
48896             }
48897         }
48898          
48899         return el;
48900     },
48901
48902     updateColumns : function(){
48903         this.grid.stopEditing();
48904         var cm = this.grid.colModel, colIds = this.getColumnIds();
48905         //var totalWidth = cm.getTotalWidth();
48906         var pos = 0;
48907         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48908             //if(cm.isHidden(i)) continue;
48909             var w = cm.getColumnWidth(i);
48910             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48911             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48912         }
48913         this.updateSplitters();
48914     },
48915
48916     generateRules : function(cm){
48917         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48918         Roo.util.CSS.removeStyleSheet(rulesId);
48919         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48920             var cid = cm.getColumnId(i);
48921             var align = '';
48922             if(cm.config[i].align){
48923                 align = 'text-align:'+cm.config[i].align+';';
48924             }
48925             var hidden = '';
48926             if(cm.isHidden(i)){
48927                 hidden = 'display:none;';
48928             }
48929             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48930             ruleBuf.push(
48931                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48932                     this.hdSelector, cid, " {\n", align, width, "}\n",
48933                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48934                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48935         }
48936         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48937     },
48938
48939     updateSplitters : function(){
48940         var cm = this.cm, s = this.getSplitters();
48941         if(s){ // splitters not created yet
48942             var pos = 0, locked = true;
48943             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48944                 if(cm.isHidden(i)) continue;
48945                 var w = cm.getColumnWidth(i); // make sure it's a number
48946                 if(!cm.isLocked(i) && locked){
48947                     pos = 0;
48948                     locked = false;
48949                 }
48950                 pos += w;
48951                 s[i].style.left = (pos-this.splitOffset) + "px";
48952             }
48953         }
48954     },
48955
48956     handleHiddenChange : function(colModel, colIndex, hidden){
48957         if(hidden){
48958             this.hideColumn(colIndex);
48959         }else{
48960             this.unhideColumn(colIndex);
48961         }
48962     },
48963
48964     hideColumn : function(colIndex){
48965         var cid = this.getColumnId(colIndex);
48966         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48967         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48968         if(Roo.isSafari){
48969             this.updateHeaders();
48970         }
48971         this.updateSplitters();
48972         this.layout();
48973     },
48974
48975     unhideColumn : function(colIndex){
48976         var cid = this.getColumnId(colIndex);
48977         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48978         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48979
48980         if(Roo.isSafari){
48981             this.updateHeaders();
48982         }
48983         this.updateSplitters();
48984         this.layout();
48985     },
48986
48987     insertRows : function(dm, firstRow, lastRow, isUpdate){
48988         if(firstRow == 0 && lastRow == dm.getCount()-1){
48989             this.refresh();
48990         }else{
48991             if(!isUpdate){
48992                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48993             }
48994             var s = this.getScrollState();
48995             var markup = this.renderRows(firstRow, lastRow);
48996             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48997             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48998             this.restoreScroll(s);
48999             if(!isUpdate){
49000                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49001                 this.syncRowHeights(firstRow, lastRow);
49002                 this.stripeRows(firstRow);
49003                 this.layout();
49004             }
49005         }
49006     },
49007
49008     bufferRows : function(markup, target, index){
49009         var before = null, trows = target.rows, tbody = target.tBodies[0];
49010         if(index < trows.length){
49011             before = trows[index];
49012         }
49013         var b = document.createElement("div");
49014         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49015         var rows = b.firstChild.rows;
49016         for(var i = 0, len = rows.length; i < len; i++){
49017             if(before){
49018                 tbody.insertBefore(rows[0], before);
49019             }else{
49020                 tbody.appendChild(rows[0]);
49021             }
49022         }
49023         b.innerHTML = "";
49024         b = null;
49025     },
49026
49027     deleteRows : function(dm, firstRow, lastRow){
49028         if(dm.getRowCount()<1){
49029             this.fireEvent("beforerefresh", this);
49030             this.mainBody.update("");
49031             this.lockedBody.update("");
49032             this.fireEvent("refresh", this);
49033         }else{
49034             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49035             var bt = this.getBodyTable();
49036             var tbody = bt.firstChild;
49037             var rows = bt.rows;
49038             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49039                 tbody.removeChild(rows[firstRow]);
49040             }
49041             this.stripeRows(firstRow);
49042             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49043         }
49044     },
49045
49046     updateRows : function(dataSource, firstRow, lastRow){
49047         var s = this.getScrollState();
49048         this.refresh();
49049         this.restoreScroll(s);
49050     },
49051
49052     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49053         if(!noRefresh){
49054            this.refresh();
49055         }
49056         this.updateHeaderSortState();
49057     },
49058
49059     getScrollState : function(){
49060         
49061         var sb = this.scroller.dom;
49062         return {left: sb.scrollLeft, top: sb.scrollTop};
49063     },
49064
49065     stripeRows : function(startRow){
49066         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49067             return;
49068         }
49069         startRow = startRow || 0;
49070         var rows = this.getBodyTable().rows;
49071         var lrows = this.getLockedTable().rows;
49072         var cls = ' x-grid-row-alt ';
49073         for(var i = startRow, len = rows.length; i < len; i++){
49074             var row = rows[i], lrow = lrows[i];
49075             var isAlt = ((i+1) % 2 == 0);
49076             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49077             if(isAlt == hasAlt){
49078                 continue;
49079             }
49080             if(isAlt){
49081                 row.className += " x-grid-row-alt";
49082             }else{
49083                 row.className = row.className.replace("x-grid-row-alt", "");
49084             }
49085             if(lrow){
49086                 lrow.className = row.className;
49087             }
49088         }
49089     },
49090
49091     restoreScroll : function(state){
49092         //Roo.log('GridView.restoreScroll');
49093         var sb = this.scroller.dom;
49094         sb.scrollLeft = state.left;
49095         sb.scrollTop = state.top;
49096         this.syncScroll();
49097     },
49098
49099     syncScroll : function(){
49100         //Roo.log('GridView.syncScroll');
49101         var sb = this.scroller.dom;
49102         var sh = this.mainHd.dom;
49103         var bs = this.mainBody.dom;
49104         var lv = this.lockedBody.dom;
49105         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49106         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49107     },
49108
49109     handleScroll : function(e){
49110         this.syncScroll();
49111         var sb = this.scroller.dom;
49112         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49113         e.stopEvent();
49114     },
49115
49116     handleWheel : function(e){
49117         var d = e.getWheelDelta();
49118         this.scroller.dom.scrollTop -= d*22;
49119         // set this here to prevent jumpy scrolling on large tables
49120         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49121         e.stopEvent();
49122     },
49123
49124     renderRows : function(startRow, endRow){
49125         // pull in all the crap needed to render rows
49126         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49127         var colCount = cm.getColumnCount();
49128
49129         if(ds.getCount() < 1){
49130             return ["", ""];
49131         }
49132
49133         // build a map for all the columns
49134         var cs = [];
49135         for(var i = 0; i < colCount; i++){
49136             var name = cm.getDataIndex(i);
49137             cs[i] = {
49138                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49139                 renderer : cm.getRenderer(i),
49140                 id : cm.getColumnId(i),
49141                 locked : cm.isLocked(i)
49142             };
49143         }
49144
49145         startRow = startRow || 0;
49146         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49147
49148         // records to render
49149         var rs = ds.getRange(startRow, endRow);
49150
49151         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49152     },
49153
49154     // As much as I hate to duplicate code, this was branched because FireFox really hates
49155     // [].join("") on strings. The performance difference was substantial enough to
49156     // branch this function
49157     doRender : Roo.isGecko ?
49158             function(cs, rs, ds, startRow, colCount, stripe){
49159                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49160                 // buffers
49161                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49162                 
49163                 var hasListener = this.grid.hasListener('rowclass');
49164                 var rowcfg = {};
49165                 for(var j = 0, len = rs.length; j < len; j++){
49166                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49167                     for(var i = 0; i < colCount; i++){
49168                         c = cs[i];
49169                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49170                         p.id = c.id;
49171                         p.css = p.attr = "";
49172                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49173                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49174                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49175                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49176                         }
49177                         var markup = ct.apply(p);
49178                         if(!c.locked){
49179                             cb+= markup;
49180                         }else{
49181                             lcb+= markup;
49182                         }
49183                     }
49184                     var alt = [];
49185                     if(stripe && ((rowIndex+1) % 2 == 0)){
49186                         alt.push("x-grid-row-alt")
49187                     }
49188                     if(r.dirty){
49189                         alt.push(  " x-grid-dirty-row");
49190                     }
49191                     rp.cells = lcb;
49192                     if(this.getRowClass){
49193                         alt.push(this.getRowClass(r, rowIndex));
49194                     }
49195                     if (hasListener) {
49196                         rowcfg = {
49197                              
49198                             record: r,
49199                             rowIndex : rowIndex,
49200                             rowClass : ''
49201                         }
49202                         this.grid.fireEvent('rowclass', this, rowcfg);
49203                         alt.push(rowcfg.rowClass);
49204                     }
49205                     rp.alt = alt.join(" ");
49206                     lbuf+= rt.apply(rp);
49207                     rp.cells = cb;
49208                     buf+=  rt.apply(rp);
49209                 }
49210                 return [lbuf, buf];
49211             } :
49212             function(cs, rs, ds, startRow, colCount, stripe){
49213                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49214                 // buffers
49215                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49216                 var hasListener = this.grid.hasListener('rowclass');
49217                 var rowcfg = {};
49218                 for(var j = 0, len = rs.length; j < len; j++){
49219                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49220                     for(var i = 0; i < colCount; i++){
49221                         c = cs[i];
49222                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49223                         p.id = c.id;
49224                         p.css = p.attr = "";
49225                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49226                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49227                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49228                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49229                         }
49230                         var markup = ct.apply(p);
49231                         if(!c.locked){
49232                             cb[cb.length] = markup;
49233                         }else{
49234                             lcb[lcb.length] = markup;
49235                         }
49236                     }
49237                     var alt = [];
49238                     if(stripe && ((rowIndex+1) % 2 == 0)){
49239                         alt.push( "x-grid-row-alt");
49240                     }
49241                     if(r.dirty){
49242                         alt.push(" x-grid-dirty-row");
49243                     }
49244                     rp.cells = lcb;
49245                     if(this.getRowClass){
49246                         alt.push( this.getRowClass(r, rowIndex));
49247                     }
49248                     if (hasListener) {
49249                         rowcfg = {
49250                              
49251                             record: r,
49252                             rowIndex : rowIndex,
49253                             rowClass : ''
49254                         }
49255                         this.grid.fireEvent('rowclass', this, rowcfg);
49256                         alt.push(rowcfg.rowClass);
49257                     }
49258                     rp.alt = alt.join(" ");
49259                     rp.cells = lcb.join("");
49260                     lbuf[lbuf.length] = rt.apply(rp);
49261                     rp.cells = cb.join("");
49262                     buf[buf.length] =  rt.apply(rp);
49263                 }
49264                 return [lbuf.join(""), buf.join("")];
49265             },
49266
49267     renderBody : function(){
49268         var markup = this.renderRows();
49269         var bt = this.templates.body;
49270         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49271     },
49272
49273     /**
49274      * Refreshes the grid
49275      * @param {Boolean} headersToo
49276      */
49277     refresh : function(headersToo){
49278         this.fireEvent("beforerefresh", this);
49279         this.grid.stopEditing();
49280         var result = this.renderBody();
49281         this.lockedBody.update(result[0]);
49282         this.mainBody.update(result[1]);
49283         if(headersToo === true){
49284             this.updateHeaders();
49285             this.updateColumns();
49286             this.updateSplitters();
49287             this.updateHeaderSortState();
49288         }
49289         this.syncRowHeights();
49290         this.layout();
49291         this.fireEvent("refresh", this);
49292     },
49293
49294     handleColumnMove : function(cm, oldIndex, newIndex){
49295         this.indexMap = null;
49296         var s = this.getScrollState();
49297         this.refresh(true);
49298         this.restoreScroll(s);
49299         this.afterMove(newIndex);
49300     },
49301
49302     afterMove : function(colIndex){
49303         if(this.enableMoveAnim && Roo.enableFx){
49304             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49305         }
49306         // if multisort - fix sortOrder, and reload..
49307         if (this.grid.dataSource.multiSort) {
49308             // the we can call sort again..
49309             var dm = this.grid.dataSource;
49310             var cm = this.grid.colModel;
49311             var so = [];
49312             for(var i = 0; i < cm.config.length; i++ ) {
49313                 
49314                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49315                     continue; // dont' bother, it's not in sort list or being set.
49316                 }
49317                 
49318                 so.push(cm.config[i].dataIndex);
49319             };
49320             dm.sortOrder = so;
49321             dm.load(dm.lastOptions);
49322             
49323             
49324         }
49325         
49326     },
49327
49328     updateCell : function(dm, rowIndex, dataIndex){
49329         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49330         if(typeof colIndex == "undefined"){ // not present in grid
49331             return;
49332         }
49333         var cm = this.grid.colModel;
49334         var cell = this.getCell(rowIndex, colIndex);
49335         var cellText = this.getCellText(rowIndex, colIndex);
49336
49337         var p = {
49338             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49339             id : cm.getColumnId(colIndex),
49340             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49341         };
49342         var renderer = cm.getRenderer(colIndex);
49343         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49344         if(typeof val == "undefined" || val === "") val = "&#160;";
49345         cellText.innerHTML = val;
49346         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49347         this.syncRowHeights(rowIndex, rowIndex);
49348     },
49349
49350     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49351         var maxWidth = 0;
49352         if(this.grid.autoSizeHeaders){
49353             var h = this.getHeaderCellMeasure(colIndex);
49354             maxWidth = Math.max(maxWidth, h.scrollWidth);
49355         }
49356         var tb, index;
49357         if(this.cm.isLocked(colIndex)){
49358             tb = this.getLockedTable();
49359             index = colIndex;
49360         }else{
49361             tb = this.getBodyTable();
49362             index = colIndex - this.cm.getLockedCount();
49363         }
49364         if(tb && tb.rows){
49365             var rows = tb.rows;
49366             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49367             for(var i = 0; i < stopIndex; i++){
49368                 var cell = rows[i].childNodes[index].firstChild;
49369                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49370             }
49371         }
49372         return maxWidth + /*margin for error in IE*/ 5;
49373     },
49374     /**
49375      * Autofit a column to its content.
49376      * @param {Number} colIndex
49377      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49378      */
49379      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49380          if(this.cm.isHidden(colIndex)){
49381              return; // can't calc a hidden column
49382          }
49383         if(forceMinSize){
49384             var cid = this.cm.getColumnId(colIndex);
49385             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49386            if(this.grid.autoSizeHeaders){
49387                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49388            }
49389         }
49390         var newWidth = this.calcColumnWidth(colIndex);
49391         this.cm.setColumnWidth(colIndex,
49392             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49393         if(!suppressEvent){
49394             this.grid.fireEvent("columnresize", colIndex, newWidth);
49395         }
49396     },
49397
49398     /**
49399      * Autofits all columns to their content and then expands to fit any extra space in the grid
49400      */
49401      autoSizeColumns : function(){
49402         var cm = this.grid.colModel;
49403         var colCount = cm.getColumnCount();
49404         for(var i = 0; i < colCount; i++){
49405             this.autoSizeColumn(i, true, true);
49406         }
49407         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49408             this.fitColumns();
49409         }else{
49410             this.updateColumns();
49411             this.layout();
49412         }
49413     },
49414
49415     /**
49416      * Autofits all columns to the grid's width proportionate with their current size
49417      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49418      */
49419     fitColumns : function(reserveScrollSpace){
49420         var cm = this.grid.colModel;
49421         var colCount = cm.getColumnCount();
49422         var cols = [];
49423         var width = 0;
49424         var i, w;
49425         for (i = 0; i < colCount; i++){
49426             if(!cm.isHidden(i) && !cm.isFixed(i)){
49427                 w = cm.getColumnWidth(i);
49428                 cols.push(i);
49429                 cols.push(w);
49430                 width += w;
49431             }
49432         }
49433         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49434         if(reserveScrollSpace){
49435             avail -= 17;
49436         }
49437         var frac = (avail - cm.getTotalWidth())/width;
49438         while (cols.length){
49439             w = cols.pop();
49440             i = cols.pop();
49441             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49442         }
49443         this.updateColumns();
49444         this.layout();
49445     },
49446
49447     onRowSelect : function(rowIndex){
49448         var row = this.getRowComposite(rowIndex);
49449         row.addClass("x-grid-row-selected");
49450     },
49451
49452     onRowDeselect : function(rowIndex){
49453         var row = this.getRowComposite(rowIndex);
49454         row.removeClass("x-grid-row-selected");
49455     },
49456
49457     onCellSelect : function(row, col){
49458         var cell = this.getCell(row, col);
49459         if(cell){
49460             Roo.fly(cell).addClass("x-grid-cell-selected");
49461         }
49462     },
49463
49464     onCellDeselect : function(row, col){
49465         var cell = this.getCell(row, col);
49466         if(cell){
49467             Roo.fly(cell).removeClass("x-grid-cell-selected");
49468         }
49469     },
49470
49471     updateHeaderSortState : function(){
49472         
49473         // sort state can be single { field: xxx, direction : yyy}
49474         // or   { xxx=>ASC , yyy : DESC ..... }
49475         
49476         var mstate = {};
49477         if (!this.ds.multiSort) { 
49478             var state = this.ds.getSortState();
49479             if(!state){
49480                 return;
49481             }
49482             mstate[state.field] = state.direction;
49483             // FIXME... - this is not used here.. but might be elsewhere..
49484             this.sortState = state;
49485             
49486         } else {
49487             mstate = this.ds.sortToggle;
49488         }
49489         //remove existing sort classes..
49490         
49491         var sc = this.sortClasses;
49492         var hds = this.el.select(this.headerSelector).removeClass(sc);
49493         
49494         for(var f in mstate) {
49495         
49496             var sortColumn = this.cm.findColumnIndex(f);
49497             
49498             if(sortColumn != -1){
49499                 var sortDir = mstate[f];        
49500                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49501             }
49502         }
49503         
49504          
49505         
49506     },
49507
49508
49509     handleHeaderClick : function(g, index){
49510         if(this.headersDisabled){
49511             return;
49512         }
49513         var dm = g.dataSource, cm = g.colModel;
49514         if(!cm.isSortable(index)){
49515             return;
49516         }
49517         g.stopEditing();
49518         
49519         if (dm.multiSort) {
49520             // update the sortOrder
49521             var so = [];
49522             for(var i = 0; i < cm.config.length; i++ ) {
49523                 
49524                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49525                     continue; // dont' bother, it's not in sort list or being set.
49526                 }
49527                 
49528                 so.push(cm.config[i].dataIndex);
49529             };
49530             dm.sortOrder = so;
49531         }
49532         
49533         
49534         dm.sort(cm.getDataIndex(index));
49535     },
49536
49537
49538     destroy : function(){
49539         if(this.colMenu){
49540             this.colMenu.removeAll();
49541             Roo.menu.MenuMgr.unregister(this.colMenu);
49542             this.colMenu.getEl().remove();
49543             delete this.colMenu;
49544         }
49545         if(this.hmenu){
49546             this.hmenu.removeAll();
49547             Roo.menu.MenuMgr.unregister(this.hmenu);
49548             this.hmenu.getEl().remove();
49549             delete this.hmenu;
49550         }
49551         if(this.grid.enableColumnMove){
49552             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49553             if(dds){
49554                 for(var dd in dds){
49555                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49556                         var elid = dds[dd].dragElId;
49557                         dds[dd].unreg();
49558                         Roo.get(elid).remove();
49559                     } else if(dds[dd].config.isTarget){
49560                         dds[dd].proxyTop.remove();
49561                         dds[dd].proxyBottom.remove();
49562                         dds[dd].unreg();
49563                     }
49564                     if(Roo.dd.DDM.locationCache[dd]){
49565                         delete Roo.dd.DDM.locationCache[dd];
49566                     }
49567                 }
49568                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49569             }
49570         }
49571         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49572         this.bind(null, null);
49573         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49574     },
49575
49576     handleLockChange : function(){
49577         this.refresh(true);
49578     },
49579
49580     onDenyColumnLock : function(){
49581
49582     },
49583
49584     onDenyColumnHide : function(){
49585
49586     },
49587
49588     handleHdMenuClick : function(item){
49589         var index = this.hdCtxIndex;
49590         var cm = this.cm, ds = this.ds;
49591         switch(item.id){
49592             case "asc":
49593                 ds.sort(cm.getDataIndex(index), "ASC");
49594                 break;
49595             case "desc":
49596                 ds.sort(cm.getDataIndex(index), "DESC");
49597                 break;
49598             case "lock":
49599                 var lc = cm.getLockedCount();
49600                 if(cm.getColumnCount(true) <= lc+1){
49601                     this.onDenyColumnLock();
49602                     return;
49603                 }
49604                 if(lc != index){
49605                     cm.setLocked(index, true, true);
49606                     cm.moveColumn(index, lc);
49607                     this.grid.fireEvent("columnmove", index, lc);
49608                 }else{
49609                     cm.setLocked(index, true);
49610                 }
49611             break;
49612             case "unlock":
49613                 var lc = cm.getLockedCount();
49614                 if((lc-1) != index){
49615                     cm.setLocked(index, false, true);
49616                     cm.moveColumn(index, lc-1);
49617                     this.grid.fireEvent("columnmove", index, lc-1);
49618                 }else{
49619                     cm.setLocked(index, false);
49620                 }
49621             break;
49622             default:
49623                 index = cm.getIndexById(item.id.substr(4));
49624                 if(index != -1){
49625                     if(item.checked && cm.getColumnCount(true) <= 1){
49626                         this.onDenyColumnHide();
49627                         return false;
49628                     }
49629                     cm.setHidden(index, item.checked);
49630                 }
49631         }
49632         return true;
49633     },
49634
49635     beforeColMenuShow : function(){
49636         var cm = this.cm,  colCount = cm.getColumnCount();
49637         this.colMenu.removeAll();
49638         for(var i = 0; i < colCount; i++){
49639             this.colMenu.add(new Roo.menu.CheckItem({
49640                 id: "col-"+cm.getColumnId(i),
49641                 text: cm.getColumnHeader(i),
49642                 checked: !cm.isHidden(i),
49643                 hideOnClick:false
49644             }));
49645         }
49646     },
49647
49648     handleHdCtx : function(g, index, e){
49649         e.stopEvent();
49650         var hd = this.getHeaderCell(index);
49651         this.hdCtxIndex = index;
49652         var ms = this.hmenu.items, cm = this.cm;
49653         ms.get("asc").setDisabled(!cm.isSortable(index));
49654         ms.get("desc").setDisabled(!cm.isSortable(index));
49655         if(this.grid.enableColLock !== false){
49656             ms.get("lock").setDisabled(cm.isLocked(index));
49657             ms.get("unlock").setDisabled(!cm.isLocked(index));
49658         }
49659         this.hmenu.show(hd, "tl-bl");
49660     },
49661
49662     handleHdOver : function(e){
49663         var hd = this.findHeaderCell(e.getTarget());
49664         if(hd && !this.headersDisabled){
49665             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49666                this.fly(hd).addClass("x-grid-hd-over");
49667             }
49668         }
49669     },
49670
49671     handleHdOut : function(e){
49672         var hd = this.findHeaderCell(e.getTarget());
49673         if(hd){
49674             this.fly(hd).removeClass("x-grid-hd-over");
49675         }
49676     },
49677
49678     handleSplitDblClick : function(e, t){
49679         var i = this.getCellIndex(t);
49680         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49681             this.autoSizeColumn(i, true);
49682             this.layout();
49683         }
49684     },
49685
49686     render : function(){
49687
49688         var cm = this.cm;
49689         var colCount = cm.getColumnCount();
49690
49691         if(this.grid.monitorWindowResize === true){
49692             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49693         }
49694         var header = this.renderHeaders();
49695         var body = this.templates.body.apply({rows:""});
49696         var html = this.templates.master.apply({
49697             lockedBody: body,
49698             body: body,
49699             lockedHeader: header[0],
49700             header: header[1]
49701         });
49702
49703         //this.updateColumns();
49704
49705         this.grid.getGridEl().dom.innerHTML = html;
49706
49707         this.initElements();
49708         
49709         // a kludge to fix the random scolling effect in webkit
49710         this.el.on("scroll", function() {
49711             this.el.dom.scrollTop=0; // hopefully not recursive..
49712         },this);
49713
49714         this.scroller.on("scroll", this.handleScroll, this);
49715         this.lockedBody.on("mousewheel", this.handleWheel, this);
49716         this.mainBody.on("mousewheel", this.handleWheel, this);
49717
49718         this.mainHd.on("mouseover", this.handleHdOver, this);
49719         this.mainHd.on("mouseout", this.handleHdOut, this);
49720         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49721                 {delegate: "."+this.splitClass});
49722
49723         this.lockedHd.on("mouseover", this.handleHdOver, this);
49724         this.lockedHd.on("mouseout", this.handleHdOut, this);
49725         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49726                 {delegate: "."+this.splitClass});
49727
49728         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49729             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49730         }
49731
49732         this.updateSplitters();
49733
49734         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49735             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49736             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49737         }
49738
49739         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49740             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49741             this.hmenu.add(
49742                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49743                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49744             );
49745             if(this.grid.enableColLock !== false){
49746                 this.hmenu.add('-',
49747                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49748                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49749                 );
49750             }
49751             if(this.grid.enableColumnHide !== false){
49752
49753                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49754                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49755                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49756
49757                 this.hmenu.add('-',
49758                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49759                 );
49760             }
49761             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49762
49763             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49764         }
49765
49766         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49767             this.dd = new Roo.grid.GridDragZone(this.grid, {
49768                 ddGroup : this.grid.ddGroup || 'GridDD'
49769             });
49770         }
49771
49772         /*
49773         for(var i = 0; i < colCount; i++){
49774             if(cm.isHidden(i)){
49775                 this.hideColumn(i);
49776             }
49777             if(cm.config[i].align){
49778                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49779                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49780             }
49781         }*/
49782         
49783         this.updateHeaderSortState();
49784
49785         this.beforeInitialResize();
49786         this.layout(true);
49787
49788         // two part rendering gives faster view to the user
49789         this.renderPhase2.defer(1, this);
49790     },
49791
49792     renderPhase2 : function(){
49793         // render the rows now
49794         this.refresh();
49795         if(this.grid.autoSizeColumns){
49796             this.autoSizeColumns();
49797         }
49798     },
49799
49800     beforeInitialResize : function(){
49801
49802     },
49803
49804     onColumnSplitterMoved : function(i, w){
49805         this.userResized = true;
49806         var cm = this.grid.colModel;
49807         cm.setColumnWidth(i, w, true);
49808         var cid = cm.getColumnId(i);
49809         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49810         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49811         this.updateSplitters();
49812         this.layout();
49813         this.grid.fireEvent("columnresize", i, w);
49814     },
49815
49816     syncRowHeights : function(startIndex, endIndex){
49817         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49818             startIndex = startIndex || 0;
49819             var mrows = this.getBodyTable().rows;
49820             var lrows = this.getLockedTable().rows;
49821             var len = mrows.length-1;
49822             endIndex = Math.min(endIndex || len, len);
49823             for(var i = startIndex; i <= endIndex; i++){
49824                 var m = mrows[i], l = lrows[i];
49825                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49826                 m.style.height = l.style.height = h + "px";
49827             }
49828         }
49829     },
49830
49831     layout : function(initialRender, is2ndPass){
49832         var g = this.grid;
49833         var auto = g.autoHeight;
49834         var scrollOffset = 16;
49835         var c = g.getGridEl(), cm = this.cm,
49836                 expandCol = g.autoExpandColumn,
49837                 gv = this;
49838         //c.beginMeasure();
49839
49840         if(!c.dom.offsetWidth){ // display:none?
49841             if(initialRender){
49842                 this.lockedWrap.show();
49843                 this.mainWrap.show();
49844             }
49845             return;
49846         }
49847
49848         var hasLock = this.cm.isLocked(0);
49849
49850         var tbh = this.headerPanel.getHeight();
49851         var bbh = this.footerPanel.getHeight();
49852
49853         if(auto){
49854             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49855             var newHeight = ch + c.getBorderWidth("tb");
49856             if(g.maxHeight){
49857                 newHeight = Math.min(g.maxHeight, newHeight);
49858             }
49859             c.setHeight(newHeight);
49860         }
49861
49862         if(g.autoWidth){
49863             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49864         }
49865
49866         var s = this.scroller;
49867
49868         var csize = c.getSize(true);
49869
49870         this.el.setSize(csize.width, csize.height);
49871
49872         this.headerPanel.setWidth(csize.width);
49873         this.footerPanel.setWidth(csize.width);
49874
49875         var hdHeight = this.mainHd.getHeight();
49876         var vw = csize.width;
49877         var vh = csize.height - (tbh + bbh);
49878
49879         s.setSize(vw, vh);
49880
49881         var bt = this.getBodyTable();
49882         var ltWidth = hasLock ?
49883                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49884
49885         var scrollHeight = bt.offsetHeight;
49886         var scrollWidth = ltWidth + bt.offsetWidth;
49887         var vscroll = false, hscroll = false;
49888
49889         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49890
49891         var lw = this.lockedWrap, mw = this.mainWrap;
49892         var lb = this.lockedBody, mb = this.mainBody;
49893
49894         setTimeout(function(){
49895             var t = s.dom.offsetTop;
49896             var w = s.dom.clientWidth,
49897                 h = s.dom.clientHeight;
49898
49899             lw.setTop(t);
49900             lw.setSize(ltWidth, h);
49901
49902             mw.setLeftTop(ltWidth, t);
49903             mw.setSize(w-ltWidth, h);
49904
49905             lb.setHeight(h-hdHeight);
49906             mb.setHeight(h-hdHeight);
49907
49908             if(is2ndPass !== true && !gv.userResized && expandCol){
49909                 // high speed resize without full column calculation
49910                 
49911                 var ci = cm.getIndexById(expandCol);
49912                 if (ci < 0) {
49913                     ci = cm.findColumnIndex(expandCol);
49914                 }
49915                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49916                 var expandId = cm.getColumnId(ci);
49917                 var  tw = cm.getTotalWidth(false);
49918                 var currentWidth = cm.getColumnWidth(ci);
49919                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49920                 if(currentWidth != cw){
49921                     cm.setColumnWidth(ci, cw, true);
49922                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49923                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49924                     gv.updateSplitters();
49925                     gv.layout(false, true);
49926                 }
49927             }
49928
49929             if(initialRender){
49930                 lw.show();
49931                 mw.show();
49932             }
49933             //c.endMeasure();
49934         }, 10);
49935     },
49936
49937     onWindowResize : function(){
49938         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49939             return;
49940         }
49941         this.layout();
49942     },
49943
49944     appendFooter : function(parentEl){
49945         return null;
49946     },
49947
49948     sortAscText : "Sort Ascending",
49949     sortDescText : "Sort Descending",
49950     lockText : "Lock Column",
49951     unlockText : "Unlock Column",
49952     columnsText : "Columns"
49953 });
49954
49955
49956 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49957     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49958     this.proxy.el.addClass('x-grid3-col-dd');
49959 };
49960
49961 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49962     handleMouseDown : function(e){
49963
49964     },
49965
49966     callHandleMouseDown : function(e){
49967         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49968     }
49969 });
49970 /*
49971  * Based on:
49972  * Ext JS Library 1.1.1
49973  * Copyright(c) 2006-2007, Ext JS, LLC.
49974  *
49975  * Originally Released Under LGPL - original licence link has changed is not relivant.
49976  *
49977  * Fork - LGPL
49978  * <script type="text/javascript">
49979  */
49980  
49981 // private
49982 // This is a support class used internally by the Grid components
49983 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49984     this.grid = grid;
49985     this.view = grid.getView();
49986     this.proxy = this.view.resizeProxy;
49987     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49988         "gridSplitters" + this.grid.getGridEl().id, {
49989         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49990     });
49991     this.setHandleElId(Roo.id(hd));
49992     this.setOuterHandleElId(Roo.id(hd2));
49993     this.scroll = false;
49994 };
49995 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49996     fly: Roo.Element.fly,
49997
49998     b4StartDrag : function(x, y){
49999         this.view.headersDisabled = true;
50000         this.proxy.setHeight(this.view.mainWrap.getHeight());
50001         var w = this.cm.getColumnWidth(this.cellIndex);
50002         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50003         this.resetConstraints();
50004         this.setXConstraint(minw, 1000);
50005         this.setYConstraint(0, 0);
50006         this.minX = x - minw;
50007         this.maxX = x + 1000;
50008         this.startPos = x;
50009         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50010     },
50011
50012
50013     handleMouseDown : function(e){
50014         ev = Roo.EventObject.setEvent(e);
50015         var t = this.fly(ev.getTarget());
50016         if(t.hasClass("x-grid-split")){
50017             this.cellIndex = this.view.getCellIndex(t.dom);
50018             this.split = t.dom;
50019             this.cm = this.grid.colModel;
50020             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50021                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50022             }
50023         }
50024     },
50025
50026     endDrag : function(e){
50027         this.view.headersDisabled = false;
50028         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50029         var diff = endX - this.startPos;
50030         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50031     },
50032
50033     autoOffset : function(){
50034         this.setDelta(0,0);
50035     }
50036 });/*
50037  * Based on:
50038  * Ext JS Library 1.1.1
50039  * Copyright(c) 2006-2007, Ext JS, LLC.
50040  *
50041  * Originally Released Under LGPL - original licence link has changed is not relivant.
50042  *
50043  * Fork - LGPL
50044  * <script type="text/javascript">
50045  */
50046  
50047 // private
50048 // This is a support class used internally by the Grid components
50049 Roo.grid.GridDragZone = function(grid, config){
50050     this.view = grid.getView();
50051     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50052     if(this.view.lockedBody){
50053         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50054         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50055     }
50056     this.scroll = false;
50057     this.grid = grid;
50058     this.ddel = document.createElement('div');
50059     this.ddel.className = 'x-grid-dd-wrap';
50060 };
50061
50062 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50063     ddGroup : "GridDD",
50064
50065     getDragData : function(e){
50066         var t = Roo.lib.Event.getTarget(e);
50067         var rowIndex = this.view.findRowIndex(t);
50068         if(rowIndex !== false){
50069             var sm = this.grid.selModel;
50070             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50071               //  sm.mouseDown(e, t);
50072             //}
50073             if (e.hasModifier()){
50074                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50075             }
50076             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50077         }
50078         return false;
50079     },
50080
50081     onInitDrag : function(e){
50082         var data = this.dragData;
50083         this.ddel.innerHTML = this.grid.getDragDropText();
50084         this.proxy.update(this.ddel);
50085         // fire start drag?
50086     },
50087
50088     afterRepair : function(){
50089         this.dragging = false;
50090     },
50091
50092     getRepairXY : function(e, data){
50093         return false;
50094     },
50095
50096     onEndDrag : function(data, e){
50097         // fire end drag?
50098     },
50099
50100     onValidDrop : function(dd, e, id){
50101         // fire drag drop?
50102         this.hideProxy();
50103     },
50104
50105     beforeInvalidDrop : function(e, id){
50106
50107     }
50108 });/*
50109  * Based on:
50110  * Ext JS Library 1.1.1
50111  * Copyright(c) 2006-2007, Ext JS, LLC.
50112  *
50113  * Originally Released Under LGPL - original licence link has changed is not relivant.
50114  *
50115  * Fork - LGPL
50116  * <script type="text/javascript">
50117  */
50118  
50119
50120 /**
50121  * @class Roo.grid.ColumnModel
50122  * @extends Roo.util.Observable
50123  * This is the default implementation of a ColumnModel used by the Grid. It defines
50124  * the columns in the grid.
50125  * <br>Usage:<br>
50126  <pre><code>
50127  var colModel = new Roo.grid.ColumnModel([
50128         {header: "Ticker", width: 60, sortable: true, locked: true},
50129         {header: "Company Name", width: 150, sortable: true},
50130         {header: "Market Cap.", width: 100, sortable: true},
50131         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50132         {header: "Employees", width: 100, sortable: true, resizable: false}
50133  ]);
50134  </code></pre>
50135  * <p>
50136  
50137  * The config options listed for this class are options which may appear in each
50138  * individual column definition.
50139  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50140  * @constructor
50141  * @param {Object} config An Array of column config objects. See this class's
50142  * config objects for details.
50143 */
50144 Roo.grid.ColumnModel = function(config){
50145         /**
50146      * The config passed into the constructor
50147      */
50148     this.config = config;
50149     this.lookup = {};
50150
50151     // if no id, create one
50152     // if the column does not have a dataIndex mapping,
50153     // map it to the order it is in the config
50154     for(var i = 0, len = config.length; i < len; i++){
50155         var c = config[i];
50156         if(typeof c.dataIndex == "undefined"){
50157             c.dataIndex = i;
50158         }
50159         if(typeof c.renderer == "string"){
50160             c.renderer = Roo.util.Format[c.renderer];
50161         }
50162         if(typeof c.id == "undefined"){
50163             c.id = Roo.id();
50164         }
50165         if(c.editor && c.editor.xtype){
50166             c.editor  = Roo.factory(c.editor, Roo.grid);
50167         }
50168         if(c.editor && c.editor.isFormField){
50169             c.editor = new Roo.grid.GridEditor(c.editor);
50170         }
50171         this.lookup[c.id] = c;
50172     }
50173
50174     /**
50175      * The width of columns which have no width specified (defaults to 100)
50176      * @type Number
50177      */
50178     this.defaultWidth = 100;
50179
50180     /**
50181      * Default sortable of columns which have no sortable specified (defaults to false)
50182      * @type Boolean
50183      */
50184     this.defaultSortable = false;
50185
50186     this.addEvents({
50187         /**
50188              * @event widthchange
50189              * Fires when the width of a column changes.
50190              * @param {ColumnModel} this
50191              * @param {Number} columnIndex The column index
50192              * @param {Number} newWidth The new width
50193              */
50194             "widthchange": true,
50195         /**
50196              * @event headerchange
50197              * Fires when the text of a header changes.
50198              * @param {ColumnModel} this
50199              * @param {Number} columnIndex The column index
50200              * @param {Number} newText The new header text
50201              */
50202             "headerchange": true,
50203         /**
50204              * @event hiddenchange
50205              * Fires when a column is hidden or "unhidden".
50206              * @param {ColumnModel} this
50207              * @param {Number} columnIndex The column index
50208              * @param {Boolean} hidden true if hidden, false otherwise
50209              */
50210             "hiddenchange": true,
50211             /**
50212          * @event columnmoved
50213          * Fires when a column is moved.
50214          * @param {ColumnModel} this
50215          * @param {Number} oldIndex
50216          * @param {Number} newIndex
50217          */
50218         "columnmoved" : true,
50219         /**
50220          * @event columlockchange
50221          * Fires when a column's locked state is changed
50222          * @param {ColumnModel} this
50223          * @param {Number} colIndex
50224          * @param {Boolean} locked true if locked
50225          */
50226         "columnlockchange" : true
50227     });
50228     Roo.grid.ColumnModel.superclass.constructor.call(this);
50229 };
50230 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50231     /**
50232      * @cfg {String} header The header text to display in the Grid view.
50233      */
50234     /**
50235      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50236      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50237      * specified, the column's index is used as an index into the Record's data Array.
50238      */
50239     /**
50240      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50241      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50242      */
50243     /**
50244      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50245      * Defaults to the value of the {@link #defaultSortable} property.
50246      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50247      */
50248     /**
50249      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50250      */
50251     /**
50252      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50253      */
50254     /**
50255      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50256      */
50257     /**
50258      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50259      */
50260     /**
50261      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50262      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50263      * default renderer uses the raw data value.
50264      */
50265        /**
50266      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50267      */
50268     /**
50269      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50270      */
50271
50272     /**
50273      * Returns the id of the column at the specified index.
50274      * @param {Number} index The column index
50275      * @return {String} the id
50276      */
50277     getColumnId : function(index){
50278         return this.config[index].id;
50279     },
50280
50281     /**
50282      * Returns the column for a specified id.
50283      * @param {String} id The column id
50284      * @return {Object} the column
50285      */
50286     getColumnById : function(id){
50287         return this.lookup[id];
50288     },
50289
50290     
50291     /**
50292      * Returns the column for a specified dataIndex.
50293      * @param {String} dataIndex The column dataIndex
50294      * @return {Object|Boolean} the column or false if not found
50295      */
50296     getColumnByDataIndex: function(dataIndex){
50297         var index = this.findColumnIndex(dataIndex);
50298         return index > -1 ? this.config[index] : false;
50299     },
50300     
50301     /**
50302      * Returns the index for a specified column id.
50303      * @param {String} id The column id
50304      * @return {Number} the index, or -1 if not found
50305      */
50306     getIndexById : function(id){
50307         for(var i = 0, len = this.config.length; i < len; i++){
50308             if(this.config[i].id == id){
50309                 return i;
50310             }
50311         }
50312         return -1;
50313     },
50314     
50315     /**
50316      * Returns the index for a specified column dataIndex.
50317      * @param {String} dataIndex The column dataIndex
50318      * @return {Number} the index, or -1 if not found
50319      */
50320     
50321     findColumnIndex : function(dataIndex){
50322         for(var i = 0, len = this.config.length; i < len; i++){
50323             if(this.config[i].dataIndex == dataIndex){
50324                 return i;
50325             }
50326         }
50327         return -1;
50328     },
50329     
50330     
50331     moveColumn : function(oldIndex, newIndex){
50332         var c = this.config[oldIndex];
50333         this.config.splice(oldIndex, 1);
50334         this.config.splice(newIndex, 0, c);
50335         this.dataMap = null;
50336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50337     },
50338
50339     isLocked : function(colIndex){
50340         return this.config[colIndex].locked === true;
50341     },
50342
50343     setLocked : function(colIndex, value, suppressEvent){
50344         if(this.isLocked(colIndex) == value){
50345             return;
50346         }
50347         this.config[colIndex].locked = value;
50348         if(!suppressEvent){
50349             this.fireEvent("columnlockchange", this, colIndex, value);
50350         }
50351     },
50352
50353     getTotalLockedWidth : function(){
50354         var totalWidth = 0;
50355         for(var i = 0; i < this.config.length; i++){
50356             if(this.isLocked(i) && !this.isHidden(i)){
50357                 this.totalWidth += this.getColumnWidth(i);
50358             }
50359         }
50360         return totalWidth;
50361     },
50362
50363     getLockedCount : function(){
50364         for(var i = 0, len = this.config.length; i < len; i++){
50365             if(!this.isLocked(i)){
50366                 return i;
50367             }
50368         }
50369     },
50370
50371     /**
50372      * Returns the number of columns.
50373      * @return {Number}
50374      */
50375     getColumnCount : function(visibleOnly){
50376         if(visibleOnly === true){
50377             var c = 0;
50378             for(var i = 0, len = this.config.length; i < len; i++){
50379                 if(!this.isHidden(i)){
50380                     c++;
50381                 }
50382             }
50383             return c;
50384         }
50385         return this.config.length;
50386     },
50387
50388     /**
50389      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50390      * @param {Function} fn
50391      * @param {Object} scope (optional)
50392      * @return {Array} result
50393      */
50394     getColumnsBy : function(fn, scope){
50395         var r = [];
50396         for(var i = 0, len = this.config.length; i < len; i++){
50397             var c = this.config[i];
50398             if(fn.call(scope||this, c, i) === true){
50399                 r[r.length] = c;
50400             }
50401         }
50402         return r;
50403     },
50404
50405     /**
50406      * Returns true if the specified column is sortable.
50407      * @param {Number} col The column index
50408      * @return {Boolean}
50409      */
50410     isSortable : function(col){
50411         if(typeof this.config[col].sortable == "undefined"){
50412             return this.defaultSortable;
50413         }
50414         return this.config[col].sortable;
50415     },
50416
50417     /**
50418      * Returns the rendering (formatting) function defined for the column.
50419      * @param {Number} col The column index.
50420      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50421      */
50422     getRenderer : function(col){
50423         if(!this.config[col].renderer){
50424             return Roo.grid.ColumnModel.defaultRenderer;
50425         }
50426         return this.config[col].renderer;
50427     },
50428
50429     /**
50430      * Sets the rendering (formatting) function for a column.
50431      * @param {Number} col The column index
50432      * @param {Function} fn The function to use to process the cell's raw data
50433      * to return HTML markup for the grid view. The render function is called with
50434      * the following parameters:<ul>
50435      * <li>Data value.</li>
50436      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50437      * <li>css A CSS style string to apply to the table cell.</li>
50438      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50439      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50440      * <li>Row index</li>
50441      * <li>Column index</li>
50442      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50443      */
50444     setRenderer : function(col, fn){
50445         this.config[col].renderer = fn;
50446     },
50447
50448     /**
50449      * Returns the width for the specified column.
50450      * @param {Number} col The column index
50451      * @return {Number}
50452      */
50453     getColumnWidth : function(col){
50454         return this.config[col].width * 1 || this.defaultWidth;
50455     },
50456
50457     /**
50458      * Sets the width for a column.
50459      * @param {Number} col The column index
50460      * @param {Number} width The new width
50461      */
50462     setColumnWidth : function(col, width, suppressEvent){
50463         this.config[col].width = width;
50464         this.totalWidth = null;
50465         if(!suppressEvent){
50466              this.fireEvent("widthchange", this, col, width);
50467         }
50468     },
50469
50470     /**
50471      * Returns the total width of all columns.
50472      * @param {Boolean} includeHidden True to include hidden column widths
50473      * @return {Number}
50474      */
50475     getTotalWidth : function(includeHidden){
50476         if(!this.totalWidth){
50477             this.totalWidth = 0;
50478             for(var i = 0, len = this.config.length; i < len; i++){
50479                 if(includeHidden || !this.isHidden(i)){
50480                     this.totalWidth += this.getColumnWidth(i);
50481                 }
50482             }
50483         }
50484         return this.totalWidth;
50485     },
50486
50487     /**
50488      * Returns the header for the specified column.
50489      * @param {Number} col The column index
50490      * @return {String}
50491      */
50492     getColumnHeader : function(col){
50493         return this.config[col].header;
50494     },
50495
50496     /**
50497      * Sets the header for a column.
50498      * @param {Number} col The column index
50499      * @param {String} header The new header
50500      */
50501     setColumnHeader : function(col, header){
50502         this.config[col].header = header;
50503         this.fireEvent("headerchange", this, col, header);
50504     },
50505
50506     /**
50507      * Returns the tooltip for the specified column.
50508      * @param {Number} col The column index
50509      * @return {String}
50510      */
50511     getColumnTooltip : function(col){
50512             return this.config[col].tooltip;
50513     },
50514     /**
50515      * Sets the tooltip for a column.
50516      * @param {Number} col The column index
50517      * @param {String} tooltip The new tooltip
50518      */
50519     setColumnTooltip : function(col, tooltip){
50520             this.config[col].tooltip = tooltip;
50521     },
50522
50523     /**
50524      * Returns the dataIndex for the specified column.
50525      * @param {Number} col The column index
50526      * @return {Number}
50527      */
50528     getDataIndex : function(col){
50529         return this.config[col].dataIndex;
50530     },
50531
50532     /**
50533      * Sets the dataIndex for a column.
50534      * @param {Number} col The column index
50535      * @param {Number} dataIndex The new dataIndex
50536      */
50537     setDataIndex : function(col, dataIndex){
50538         this.config[col].dataIndex = dataIndex;
50539     },
50540
50541     
50542     
50543     /**
50544      * Returns true if the cell is editable.
50545      * @param {Number} colIndex The column index
50546      * @param {Number} rowIndex The row index
50547      * @return {Boolean}
50548      */
50549     isCellEditable : function(colIndex, rowIndex){
50550         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50551     },
50552
50553     /**
50554      * Returns the editor defined for the cell/column.
50555      * return false or null to disable editing.
50556      * @param {Number} colIndex The column index
50557      * @param {Number} rowIndex The row index
50558      * @return {Object}
50559      */
50560     getCellEditor : function(colIndex, rowIndex){
50561         return this.config[colIndex].editor;
50562     },
50563
50564     /**
50565      * Sets if a column is editable.
50566      * @param {Number} col The column index
50567      * @param {Boolean} editable True if the column is editable
50568      */
50569     setEditable : function(col, editable){
50570         this.config[col].editable = editable;
50571     },
50572
50573
50574     /**
50575      * Returns true if the column is hidden.
50576      * @param {Number} colIndex The column index
50577      * @return {Boolean}
50578      */
50579     isHidden : function(colIndex){
50580         return this.config[colIndex].hidden;
50581     },
50582
50583
50584     /**
50585      * Returns true if the column width cannot be changed
50586      */
50587     isFixed : function(colIndex){
50588         return this.config[colIndex].fixed;
50589     },
50590
50591     /**
50592      * Returns true if the column can be resized
50593      * @return {Boolean}
50594      */
50595     isResizable : function(colIndex){
50596         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50597     },
50598     /**
50599      * Sets if a column is hidden.
50600      * @param {Number} colIndex The column index
50601      * @param {Boolean} hidden True if the column is hidden
50602      */
50603     setHidden : function(colIndex, hidden){
50604         this.config[colIndex].hidden = hidden;
50605         this.totalWidth = null;
50606         this.fireEvent("hiddenchange", this, colIndex, hidden);
50607     },
50608
50609     /**
50610      * Sets the editor for a column.
50611      * @param {Number} col The column index
50612      * @param {Object} editor The editor object
50613      */
50614     setEditor : function(col, editor){
50615         this.config[col].editor = editor;
50616     }
50617 });
50618
50619 Roo.grid.ColumnModel.defaultRenderer = function(value){
50620         if(typeof value == "string" && value.length < 1){
50621             return "&#160;";
50622         }
50623         return value;
50624 };
50625
50626 // Alias for backwards compatibility
50627 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50628 /*
50629  * Based on:
50630  * Ext JS Library 1.1.1
50631  * Copyright(c) 2006-2007, Ext JS, LLC.
50632  *
50633  * Originally Released Under LGPL - original licence link has changed is not relivant.
50634  *
50635  * Fork - LGPL
50636  * <script type="text/javascript">
50637  */
50638
50639 /**
50640  * @class Roo.grid.AbstractSelectionModel
50641  * @extends Roo.util.Observable
50642  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50643  * implemented by descendant classes.  This class should not be directly instantiated.
50644  * @constructor
50645  */
50646 Roo.grid.AbstractSelectionModel = function(){
50647     this.locked = false;
50648     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50649 };
50650
50651 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50652     /** @ignore Called by the grid automatically. Do not call directly. */
50653     init : function(grid){
50654         this.grid = grid;
50655         this.initEvents();
50656     },
50657
50658     /**
50659      * Locks the selections.
50660      */
50661     lock : function(){
50662         this.locked = true;
50663     },
50664
50665     /**
50666      * Unlocks the selections.
50667      */
50668     unlock : function(){
50669         this.locked = false;
50670     },
50671
50672     /**
50673      * Returns true if the selections are locked.
50674      * @return {Boolean}
50675      */
50676     isLocked : function(){
50677         return this.locked;
50678     }
50679 });/*
50680  * Based on:
50681  * Ext JS Library 1.1.1
50682  * Copyright(c) 2006-2007, Ext JS, LLC.
50683  *
50684  * Originally Released Under LGPL - original licence link has changed is not relivant.
50685  *
50686  * Fork - LGPL
50687  * <script type="text/javascript">
50688  */
50689 /**
50690  * @extends Roo.grid.AbstractSelectionModel
50691  * @class Roo.grid.RowSelectionModel
50692  * The default SelectionModel used by {@link Roo.grid.Grid}.
50693  * It supports multiple selections and keyboard selection/navigation. 
50694  * @constructor
50695  * @param {Object} config
50696  */
50697 Roo.grid.RowSelectionModel = function(config){
50698     Roo.apply(this, config);
50699     this.selections = new Roo.util.MixedCollection(false, function(o){
50700         return o.id;
50701     });
50702
50703     this.last = false;
50704     this.lastActive = false;
50705
50706     this.addEvents({
50707         /**
50708              * @event selectionchange
50709              * Fires when the selection changes
50710              * @param {SelectionModel} this
50711              */
50712             "selectionchange" : true,
50713         /**
50714              * @event afterselectionchange
50715              * Fires after the selection changes (eg. by key press or clicking)
50716              * @param {SelectionModel} this
50717              */
50718             "afterselectionchange" : true,
50719         /**
50720              * @event beforerowselect
50721              * Fires when a row is selected being selected, return false to cancel.
50722              * @param {SelectionModel} this
50723              * @param {Number} rowIndex The selected index
50724              * @param {Boolean} keepExisting False if other selections will be cleared
50725              */
50726             "beforerowselect" : true,
50727         /**
50728              * @event rowselect
50729              * Fires when a row is selected.
50730              * @param {SelectionModel} this
50731              * @param {Number} rowIndex The selected index
50732              * @param {Roo.data.Record} r The record
50733              */
50734             "rowselect" : true,
50735         /**
50736              * @event rowdeselect
50737              * Fires when a row is deselected.
50738              * @param {SelectionModel} this
50739              * @param {Number} rowIndex The selected index
50740              */
50741         "rowdeselect" : true
50742     });
50743     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50744     this.locked = false;
50745 };
50746
50747 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50748     /**
50749      * @cfg {Boolean} singleSelect
50750      * True to allow selection of only one row at a time (defaults to false)
50751      */
50752     singleSelect : false,
50753
50754     // private
50755     initEvents : function(){
50756
50757         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50758             this.grid.on("mousedown", this.handleMouseDown, this);
50759         }else{ // allow click to work like normal
50760             this.grid.on("rowclick", this.handleDragableRowClick, this);
50761         }
50762
50763         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50764             "up" : function(e){
50765                 if(!e.shiftKey){
50766                     this.selectPrevious(e.shiftKey);
50767                 }else if(this.last !== false && this.lastActive !== false){
50768                     var last = this.last;
50769                     this.selectRange(this.last,  this.lastActive-1);
50770                     this.grid.getView().focusRow(this.lastActive);
50771                     if(last !== false){
50772                         this.last = last;
50773                     }
50774                 }else{
50775                     this.selectFirstRow();
50776                 }
50777                 this.fireEvent("afterselectionchange", this);
50778             },
50779             "down" : function(e){
50780                 if(!e.shiftKey){
50781                     this.selectNext(e.shiftKey);
50782                 }else if(this.last !== false && this.lastActive !== false){
50783                     var last = this.last;
50784                     this.selectRange(this.last,  this.lastActive+1);
50785                     this.grid.getView().focusRow(this.lastActive);
50786                     if(last !== false){
50787                         this.last = last;
50788                     }
50789                 }else{
50790                     this.selectFirstRow();
50791                 }
50792                 this.fireEvent("afterselectionchange", this);
50793             },
50794             scope: this
50795         });
50796
50797         var view = this.grid.view;
50798         view.on("refresh", this.onRefresh, this);
50799         view.on("rowupdated", this.onRowUpdated, this);
50800         view.on("rowremoved", this.onRemove, this);
50801     },
50802
50803     // private
50804     onRefresh : function(){
50805         var ds = this.grid.dataSource, i, v = this.grid.view;
50806         var s = this.selections;
50807         s.each(function(r){
50808             if((i = ds.indexOfId(r.id)) != -1){
50809                 v.onRowSelect(i);
50810             }else{
50811                 s.remove(r);
50812             }
50813         });
50814     },
50815
50816     // private
50817     onRemove : function(v, index, r){
50818         this.selections.remove(r);
50819     },
50820
50821     // private
50822     onRowUpdated : function(v, index, r){
50823         if(this.isSelected(r)){
50824             v.onRowSelect(index);
50825         }
50826     },
50827
50828     /**
50829      * Select records.
50830      * @param {Array} records The records to select
50831      * @param {Boolean} keepExisting (optional) True to keep existing selections
50832      */
50833     selectRecords : function(records, keepExisting){
50834         if(!keepExisting){
50835             this.clearSelections();
50836         }
50837         var ds = this.grid.dataSource;
50838         for(var i = 0, len = records.length; i < len; i++){
50839             this.selectRow(ds.indexOf(records[i]), true);
50840         }
50841     },
50842
50843     /**
50844      * Gets the number of selected rows.
50845      * @return {Number}
50846      */
50847     getCount : function(){
50848         return this.selections.length;
50849     },
50850
50851     /**
50852      * Selects the first row in the grid.
50853      */
50854     selectFirstRow : function(){
50855         this.selectRow(0);
50856     },
50857
50858     /**
50859      * Select the last row.
50860      * @param {Boolean} keepExisting (optional) True to keep existing selections
50861      */
50862     selectLastRow : function(keepExisting){
50863         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50864     },
50865
50866     /**
50867      * Selects the row immediately following the last selected row.
50868      * @param {Boolean} keepExisting (optional) True to keep existing selections
50869      */
50870     selectNext : function(keepExisting){
50871         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50872             this.selectRow(this.last+1, keepExisting);
50873             this.grid.getView().focusRow(this.last);
50874         }
50875     },
50876
50877     /**
50878      * Selects the row that precedes the last selected row.
50879      * @param {Boolean} keepExisting (optional) True to keep existing selections
50880      */
50881     selectPrevious : function(keepExisting){
50882         if(this.last){
50883             this.selectRow(this.last-1, keepExisting);
50884             this.grid.getView().focusRow(this.last);
50885         }
50886     },
50887
50888     /**
50889      * Returns the selected records
50890      * @return {Array} Array of selected records
50891      */
50892     getSelections : function(){
50893         return [].concat(this.selections.items);
50894     },
50895
50896     /**
50897      * Returns the first selected record.
50898      * @return {Record}
50899      */
50900     getSelected : function(){
50901         return this.selections.itemAt(0);
50902     },
50903
50904
50905     /**
50906      * Clears all selections.
50907      */
50908     clearSelections : function(fast){
50909         if(this.locked) return;
50910         if(fast !== true){
50911             var ds = this.grid.dataSource;
50912             var s = this.selections;
50913             s.each(function(r){
50914                 this.deselectRow(ds.indexOfId(r.id));
50915             }, this);
50916             s.clear();
50917         }else{
50918             this.selections.clear();
50919         }
50920         this.last = false;
50921     },
50922
50923
50924     /**
50925      * Selects all rows.
50926      */
50927     selectAll : function(){
50928         if(this.locked) return;
50929         this.selections.clear();
50930         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50931             this.selectRow(i, true);
50932         }
50933     },
50934
50935     /**
50936      * Returns True if there is a selection.
50937      * @return {Boolean}
50938      */
50939     hasSelection : function(){
50940         return this.selections.length > 0;
50941     },
50942
50943     /**
50944      * Returns True if the specified row is selected.
50945      * @param {Number/Record} record The record or index of the record to check
50946      * @return {Boolean}
50947      */
50948     isSelected : function(index){
50949         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50950         return (r && this.selections.key(r.id) ? true : false);
50951     },
50952
50953     /**
50954      * Returns True if the specified record id is selected.
50955      * @param {String} id The id of record to check
50956      * @return {Boolean}
50957      */
50958     isIdSelected : function(id){
50959         return (this.selections.key(id) ? true : false);
50960     },
50961
50962     // private
50963     handleMouseDown : function(e, t){
50964         var view = this.grid.getView(), rowIndex;
50965         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50966             return;
50967         };
50968         if(e.shiftKey && this.last !== false){
50969             var last = this.last;
50970             this.selectRange(last, rowIndex, e.ctrlKey);
50971             this.last = last; // reset the last
50972             view.focusRow(rowIndex);
50973         }else{
50974             var isSelected = this.isSelected(rowIndex);
50975             if(e.button !== 0 && isSelected){
50976                 view.focusRow(rowIndex);
50977             }else if(e.ctrlKey && isSelected){
50978                 this.deselectRow(rowIndex);
50979             }else if(!isSelected){
50980                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50981                 view.focusRow(rowIndex);
50982             }
50983         }
50984         this.fireEvent("afterselectionchange", this);
50985     },
50986     // private
50987     handleDragableRowClick :  function(grid, rowIndex, e) 
50988     {
50989         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50990             this.selectRow(rowIndex, false);
50991             grid.view.focusRow(rowIndex);
50992              this.fireEvent("afterselectionchange", this);
50993         }
50994     },
50995     
50996     /**
50997      * Selects multiple rows.
50998      * @param {Array} rows Array of the indexes of the row to select
50999      * @param {Boolean} keepExisting (optional) True to keep existing selections
51000      */
51001     selectRows : function(rows, keepExisting){
51002         if(!keepExisting){
51003             this.clearSelections();
51004         }
51005         for(var i = 0, len = rows.length; i < len; i++){
51006             this.selectRow(rows[i], true);
51007         }
51008     },
51009
51010     /**
51011      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51012      * @param {Number} startRow The index of the first row in the range
51013      * @param {Number} endRow The index of the last row in the range
51014      * @param {Boolean} keepExisting (optional) True to retain existing selections
51015      */
51016     selectRange : function(startRow, endRow, keepExisting){
51017         if(this.locked) return;
51018         if(!keepExisting){
51019             this.clearSelections();
51020         }
51021         if(startRow <= endRow){
51022             for(var i = startRow; i <= endRow; i++){
51023                 this.selectRow(i, true);
51024             }
51025         }else{
51026             for(var i = startRow; i >= endRow; i--){
51027                 this.selectRow(i, true);
51028             }
51029         }
51030     },
51031
51032     /**
51033      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51034      * @param {Number} startRow The index of the first row in the range
51035      * @param {Number} endRow The index of the last row in the range
51036      */
51037     deselectRange : function(startRow, endRow, preventViewNotify){
51038         if(this.locked) return;
51039         for(var i = startRow; i <= endRow; i++){
51040             this.deselectRow(i, preventViewNotify);
51041         }
51042     },
51043
51044     /**
51045      * Selects a row.
51046      * @param {Number} row The index of the row to select
51047      * @param {Boolean} keepExisting (optional) True to keep existing selections
51048      */
51049     selectRow : function(index, keepExisting, preventViewNotify){
51050         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51051         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51052             if(!keepExisting || this.singleSelect){
51053                 this.clearSelections();
51054             }
51055             var r = this.grid.dataSource.getAt(index);
51056             this.selections.add(r);
51057             this.last = this.lastActive = index;
51058             if(!preventViewNotify){
51059                 this.grid.getView().onRowSelect(index);
51060             }
51061             this.fireEvent("rowselect", this, index, r);
51062             this.fireEvent("selectionchange", this);
51063         }
51064     },
51065
51066     /**
51067      * Deselects a row.
51068      * @param {Number} row The index of the row to deselect
51069      */
51070     deselectRow : function(index, preventViewNotify){
51071         if(this.locked) return;
51072         if(this.last == index){
51073             this.last = false;
51074         }
51075         if(this.lastActive == index){
51076             this.lastActive = false;
51077         }
51078         var r = this.grid.dataSource.getAt(index);
51079         this.selections.remove(r);
51080         if(!preventViewNotify){
51081             this.grid.getView().onRowDeselect(index);
51082         }
51083         this.fireEvent("rowdeselect", this, index);
51084         this.fireEvent("selectionchange", this);
51085     },
51086
51087     // private
51088     restoreLast : function(){
51089         if(this._last){
51090             this.last = this._last;
51091         }
51092     },
51093
51094     // private
51095     acceptsNav : function(row, col, cm){
51096         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51097     },
51098
51099     // private
51100     onEditorKey : function(field, e){
51101         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51102         if(k == e.TAB){
51103             e.stopEvent();
51104             ed.completeEdit();
51105             if(e.shiftKey){
51106                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51107             }else{
51108                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51109             }
51110         }else if(k == e.ENTER && !e.ctrlKey){
51111             e.stopEvent();
51112             ed.completeEdit();
51113             if(e.shiftKey){
51114                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51115             }else{
51116                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51117             }
51118         }else if(k == e.ESC){
51119             ed.cancelEdit();
51120         }
51121         if(newCell){
51122             g.startEditing(newCell[0], newCell[1]);
51123         }
51124     }
51125 });/*
51126  * Based on:
51127  * Ext JS Library 1.1.1
51128  * Copyright(c) 2006-2007, Ext JS, LLC.
51129  *
51130  * Originally Released Under LGPL - original licence link has changed is not relivant.
51131  *
51132  * Fork - LGPL
51133  * <script type="text/javascript">
51134  */
51135 /**
51136  * @class Roo.grid.CellSelectionModel
51137  * @extends Roo.grid.AbstractSelectionModel
51138  * This class provides the basic implementation for cell selection in a grid.
51139  * @constructor
51140  * @param {Object} config The object containing the configuration of this model.
51141  */
51142 Roo.grid.CellSelectionModel = function(config){
51143     Roo.apply(this, config);
51144
51145     this.selection = null;
51146
51147     this.addEvents({
51148         /**
51149              * @event beforerowselect
51150              * Fires before a cell is selected.
51151              * @param {SelectionModel} this
51152              * @param {Number} rowIndex The selected row index
51153              * @param {Number} colIndex The selected cell index
51154              */
51155             "beforecellselect" : true,
51156         /**
51157              * @event cellselect
51158              * Fires when a cell is selected.
51159              * @param {SelectionModel} this
51160              * @param {Number} rowIndex The selected row index
51161              * @param {Number} colIndex The selected cell index
51162              */
51163             "cellselect" : true,
51164         /**
51165              * @event selectionchange
51166              * Fires when the active selection changes.
51167              * @param {SelectionModel} this
51168              * @param {Object} selection null for no selection or an object (o) with two properties
51169                 <ul>
51170                 <li>o.record: the record object for the row the selection is in</li>
51171                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51172                 </ul>
51173              */
51174             "selectionchange" : true
51175     });
51176     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51177 };
51178
51179 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51180
51181     /** @ignore */
51182     initEvents : function(){
51183         this.grid.on("mousedown", this.handleMouseDown, this);
51184         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51185         var view = this.grid.view;
51186         view.on("refresh", this.onViewChange, this);
51187         view.on("rowupdated", this.onRowUpdated, this);
51188         view.on("beforerowremoved", this.clearSelections, this);
51189         view.on("beforerowsinserted", this.clearSelections, this);
51190         if(this.grid.isEditor){
51191             this.grid.on("beforeedit", this.beforeEdit,  this);
51192         }
51193     },
51194
51195         //private
51196     beforeEdit : function(e){
51197         this.select(e.row, e.column, false, true, e.record);
51198     },
51199
51200         //private
51201     onRowUpdated : function(v, index, r){
51202         if(this.selection && this.selection.record == r){
51203             v.onCellSelect(index, this.selection.cell[1]);
51204         }
51205     },
51206
51207         //private
51208     onViewChange : function(){
51209         this.clearSelections(true);
51210     },
51211
51212         /**
51213          * Returns the currently selected cell,.
51214          * @return {Array} The selected cell (row, column) or null if none selected.
51215          */
51216     getSelectedCell : function(){
51217         return this.selection ? this.selection.cell : null;
51218     },
51219
51220     /**
51221      * Clears all selections.
51222      * @param {Boolean} true to prevent the gridview from being notified about the change.
51223      */
51224     clearSelections : function(preventNotify){
51225         var s = this.selection;
51226         if(s){
51227             if(preventNotify !== true){
51228                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51229             }
51230             this.selection = null;
51231             this.fireEvent("selectionchange", this, null);
51232         }
51233     },
51234
51235     /**
51236      * Returns true if there is a selection.
51237      * @return {Boolean}
51238      */
51239     hasSelection : function(){
51240         return this.selection ? true : false;
51241     },
51242
51243     /** @ignore */
51244     handleMouseDown : function(e, t){
51245         var v = this.grid.getView();
51246         if(this.isLocked()){
51247             return;
51248         };
51249         var row = v.findRowIndex(t);
51250         var cell = v.findCellIndex(t);
51251         if(row !== false && cell !== false){
51252             this.select(row, cell);
51253         }
51254     },
51255
51256     /**
51257      * Selects a cell.
51258      * @param {Number} rowIndex
51259      * @param {Number} collIndex
51260      */
51261     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51262         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51263             this.clearSelections();
51264             r = r || this.grid.dataSource.getAt(rowIndex);
51265             this.selection = {
51266                 record : r,
51267                 cell : [rowIndex, colIndex]
51268             };
51269             if(!preventViewNotify){
51270                 var v = this.grid.getView();
51271                 v.onCellSelect(rowIndex, colIndex);
51272                 if(preventFocus !== true){
51273                     v.focusCell(rowIndex, colIndex);
51274                 }
51275             }
51276             this.fireEvent("cellselect", this, rowIndex, colIndex);
51277             this.fireEvent("selectionchange", this, this.selection);
51278         }
51279     },
51280
51281         //private
51282     isSelectable : function(rowIndex, colIndex, cm){
51283         return !cm.isHidden(colIndex);
51284     },
51285
51286     /** @ignore */
51287     handleKeyDown : function(e){
51288         //Roo.log('Cell Sel Model handleKeyDown');
51289         if(!e.isNavKeyPress()){
51290             return;
51291         }
51292         var g = this.grid, s = this.selection;
51293         if(!s){
51294             e.stopEvent();
51295             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51296             if(cell){
51297                 this.select(cell[0], cell[1]);
51298             }
51299             return;
51300         }
51301         var sm = this;
51302         var walk = function(row, col, step){
51303             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51304         };
51305         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51306         var newCell;
51307
51308         switch(k){
51309             case e.TAB:
51310                 // handled by onEditorKey
51311                 if (g.isEditor && g.editing) {
51312                     return;
51313                 }
51314                 if(e.shiftKey){
51315                      newCell = walk(r, c-1, -1);
51316                 }else{
51317                      newCell = walk(r, c+1, 1);
51318                 }
51319              break;
51320              case e.DOWN:
51321                  newCell = walk(r+1, c, 1);
51322              break;
51323              case e.UP:
51324                  newCell = walk(r-1, c, -1);
51325              break;
51326              case e.RIGHT:
51327                  newCell = walk(r, c+1, 1);
51328              break;
51329              case e.LEFT:
51330                  newCell = walk(r, c-1, -1);
51331              break;
51332              case e.ENTER:
51333                  if(g.isEditor && !g.editing){
51334                     g.startEditing(r, c);
51335                     e.stopEvent();
51336                     return;
51337                 }
51338              break;
51339         };
51340         if(newCell){
51341             this.select(newCell[0], newCell[1]);
51342             e.stopEvent();
51343         }
51344     },
51345
51346     acceptsNav : function(row, col, cm){
51347         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51348     },
51349     /**
51350      * Selects a cell.
51351      * @param {Number} field (not used) - as it's normally used as a listener
51352      * @param {Number} e - event - fake it by using
51353      *
51354      * var e = Roo.EventObjectImpl.prototype;
51355      * e.keyCode = e.TAB
51356      *
51357      * 
51358      */
51359     onEditorKey : function(field, e){
51360         
51361         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51362         ///Roo.log('onEditorKey' + k);
51363         if (!ed) {
51364             
51365             
51366             
51367         }
51368         if(k == e.TAB){
51369             if(e.shiftKey){
51370                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51371             }else{
51372                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51373             }
51374             
51375             e.stopEvent();
51376             
51377         }else if(k == e.ENTER &&  !e.ctrlKey){
51378             ed.completeEdit();
51379             e.stopEvent();
51380             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51381         }else if(k == e.ESC){
51382             ed.cancelEdit();
51383         }
51384         
51385         
51386         if(newCell){
51387             //Roo.log('next cell after edit');
51388             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51389         }
51390     }
51391 });/*
51392  * Based on:
51393  * Ext JS Library 1.1.1
51394  * Copyright(c) 2006-2007, Ext JS, LLC.
51395  *
51396  * Originally Released Under LGPL - original licence link has changed is not relivant.
51397  *
51398  * Fork - LGPL
51399  * <script type="text/javascript">
51400  */
51401  
51402 /**
51403  * @class Roo.grid.EditorGrid
51404  * @extends Roo.grid.Grid
51405  * Class for creating and editable grid.
51406  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51407  * The container MUST have some type of size defined for the grid to fill. The container will be 
51408  * automatically set to position relative if it isn't already.
51409  * @param {Object} dataSource The data model to bind to
51410  * @param {Object} colModel The column model with info about this grid's columns
51411  */
51412 Roo.grid.EditorGrid = function(container, config){
51413     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51414     this.getGridEl().addClass("xedit-grid");
51415
51416     if(!this.selModel){
51417         this.selModel = new Roo.grid.CellSelectionModel();
51418     }
51419
51420     this.activeEditor = null;
51421
51422         this.addEvents({
51423             /**
51424              * @event beforeedit
51425              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51426              * <ul style="padding:5px;padding-left:16px;">
51427              * <li>grid - This grid</li>
51428              * <li>record - The record being edited</li>
51429              * <li>field - The field name being edited</li>
51430              * <li>value - The value for the field being edited.</li>
51431              * <li>row - The grid row index</li>
51432              * <li>column - The grid column index</li>
51433              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51434              * </ul>
51435              * @param {Object} e An edit event (see above for description)
51436              */
51437             "beforeedit" : true,
51438             /**
51439              * @event afteredit
51440              * Fires after a cell is edited. <br />
51441              * <ul style="padding:5px;padding-left:16px;">
51442              * <li>grid - This grid</li>
51443              * <li>record - The record being edited</li>
51444              * <li>field - The field name being edited</li>
51445              * <li>value - The value being set</li>
51446              * <li>originalValue - The original value for the field, before the edit.</li>
51447              * <li>row - The grid row index</li>
51448              * <li>column - The grid column index</li>
51449              * </ul>
51450              * @param {Object} e An edit event (see above for description)
51451              */
51452             "afteredit" : true,
51453             /**
51454              * @event validateedit
51455              * Fires after a cell is edited, but before the value is set in the record. 
51456          * You can use this to modify the value being set in the field, Return false
51457              * to cancel the change. The edit event object has the following properties <br />
51458              * <ul style="padding:5px;padding-left:16px;">
51459          * <li>editor - This editor</li>
51460              * <li>grid - This grid</li>
51461              * <li>record - The record being edited</li>
51462              * <li>field - The field name being edited</li>
51463              * <li>value - The value being set</li>
51464              * <li>originalValue - The original value for the field, before the edit.</li>
51465              * <li>row - The grid row index</li>
51466              * <li>column - The grid column index</li>
51467              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51468              * </ul>
51469              * @param {Object} e An edit event (see above for description)
51470              */
51471             "validateedit" : true
51472         });
51473     this.on("bodyscroll", this.stopEditing,  this);
51474     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51475 };
51476
51477 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51478     /**
51479      * @cfg {Number} clicksToEdit
51480      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51481      */
51482     clicksToEdit: 2,
51483
51484     // private
51485     isEditor : true,
51486     // private
51487     trackMouseOver: false, // causes very odd FF errors
51488
51489     onCellDblClick : function(g, row, col){
51490         this.startEditing(row, col);
51491     },
51492
51493     onEditComplete : function(ed, value, startValue){
51494         this.editing = false;
51495         this.activeEditor = null;
51496         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51497         var r = ed.record;
51498         var field = this.colModel.getDataIndex(ed.col);
51499         var e = {
51500             grid: this,
51501             record: r,
51502             field: field,
51503             originalValue: startValue,
51504             value: value,
51505             row: ed.row,
51506             column: ed.col,
51507             cancel:false,
51508             editor: ed
51509         };
51510         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51511         cell.show();
51512           
51513         if(String(value) !== String(startValue)){
51514             
51515             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51516                 r.set(field, e.value);
51517                 // if we are dealing with a combo box..
51518                 // then we also set the 'name' colum to be the displayField
51519                 if (ed.field.displayField && ed.field.name) {
51520                     r.set(ed.field.name, ed.field.el.dom.value);
51521                 }
51522                 
51523                 delete e.cancel; //?? why!!!
51524                 this.fireEvent("afteredit", e);
51525             }
51526         } else {
51527             this.fireEvent("afteredit", e); // always fire it!
51528         }
51529         this.view.focusCell(ed.row, ed.col);
51530     },
51531
51532     /**
51533      * Starts editing the specified for the specified row/column
51534      * @param {Number} rowIndex
51535      * @param {Number} colIndex
51536      */
51537     startEditing : function(row, col){
51538         this.stopEditing();
51539         if(this.colModel.isCellEditable(col, row)){
51540             this.view.ensureVisible(row, col, true);
51541           
51542             var r = this.dataSource.getAt(row);
51543             var field = this.colModel.getDataIndex(col);
51544             var cell = Roo.get(this.view.getCell(row,col));
51545             var e = {
51546                 grid: this,
51547                 record: r,
51548                 field: field,
51549                 value: r.data[field],
51550                 row: row,
51551                 column: col,
51552                 cancel:false 
51553             };
51554             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51555                 this.editing = true;
51556                 var ed = this.colModel.getCellEditor(col, row);
51557                 
51558                 if (!ed) {
51559                     return;
51560                 }
51561                 if(!ed.rendered){
51562                     ed.render(ed.parentEl || document.body);
51563                 }
51564                 ed.field.reset();
51565                
51566                 cell.hide();
51567                 
51568                 (function(){ // complex but required for focus issues in safari, ie and opera
51569                     ed.row = row;
51570                     ed.col = col;
51571                     ed.record = r;
51572                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51573                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51574                     this.activeEditor = ed;
51575                     var v = r.data[field];
51576                     ed.startEdit(this.view.getCell(row, col), v);
51577                     // combo's with 'displayField and name set
51578                     if (ed.field.displayField && ed.field.name) {
51579                         ed.field.el.dom.value = r.data[ed.field.name];
51580                     }
51581                     
51582                     
51583                 }).defer(50, this);
51584             }
51585         }
51586     },
51587         
51588     /**
51589      * Stops any active editing
51590      */
51591     stopEditing : function(){
51592         if(this.activeEditor){
51593             this.activeEditor.completeEdit();
51594         }
51595         this.activeEditor = null;
51596     }
51597 });/*
51598  * Based on:
51599  * Ext JS Library 1.1.1
51600  * Copyright(c) 2006-2007, Ext JS, LLC.
51601  *
51602  * Originally Released Under LGPL - original licence link has changed is not relivant.
51603  *
51604  * Fork - LGPL
51605  * <script type="text/javascript">
51606  */
51607
51608 // private - not really -- you end up using it !
51609 // This is a support class used internally by the Grid components
51610
51611 /**
51612  * @class Roo.grid.GridEditor
51613  * @extends Roo.Editor
51614  * Class for creating and editable grid elements.
51615  * @param {Object} config any settings (must include field)
51616  */
51617 Roo.grid.GridEditor = function(field, config){
51618     if (!config && field.field) {
51619         config = field;
51620         field = Roo.factory(config.field, Roo.form);
51621     }
51622     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51623     field.monitorTab = false;
51624 };
51625
51626 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51627     
51628     /**
51629      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51630      */
51631     
51632     alignment: "tl-tl",
51633     autoSize: "width",
51634     hideEl : false,
51635     cls: "x-small-editor x-grid-editor",
51636     shim:false,
51637     shadow:"frame"
51638 });/*
51639  * Based on:
51640  * Ext JS Library 1.1.1
51641  * Copyright(c) 2006-2007, Ext JS, LLC.
51642  *
51643  * Originally Released Under LGPL - original licence link has changed is not relivant.
51644  *
51645  * Fork - LGPL
51646  * <script type="text/javascript">
51647  */
51648   
51649
51650   
51651 Roo.grid.PropertyRecord = Roo.data.Record.create([
51652     {name:'name',type:'string'},  'value'
51653 ]);
51654
51655
51656 Roo.grid.PropertyStore = function(grid, source){
51657     this.grid = grid;
51658     this.store = new Roo.data.Store({
51659         recordType : Roo.grid.PropertyRecord
51660     });
51661     this.store.on('update', this.onUpdate,  this);
51662     if(source){
51663         this.setSource(source);
51664     }
51665     Roo.grid.PropertyStore.superclass.constructor.call(this);
51666 };
51667
51668
51669
51670 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51671     setSource : function(o){
51672         this.source = o;
51673         this.store.removeAll();
51674         var data = [];
51675         for(var k in o){
51676             if(this.isEditableValue(o[k])){
51677                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51678             }
51679         }
51680         this.store.loadRecords({records: data}, {}, true);
51681     },
51682
51683     onUpdate : function(ds, record, type){
51684         if(type == Roo.data.Record.EDIT){
51685             var v = record.data['value'];
51686             var oldValue = record.modified['value'];
51687             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51688                 this.source[record.id] = v;
51689                 record.commit();
51690                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51691             }else{
51692                 record.reject();
51693             }
51694         }
51695     },
51696
51697     getProperty : function(row){
51698        return this.store.getAt(row);
51699     },
51700
51701     isEditableValue: function(val){
51702         if(val && val instanceof Date){
51703             return true;
51704         }else if(typeof val == 'object' || typeof val == 'function'){
51705             return false;
51706         }
51707         return true;
51708     },
51709
51710     setValue : function(prop, value){
51711         this.source[prop] = value;
51712         this.store.getById(prop).set('value', value);
51713     },
51714
51715     getSource : function(){
51716         return this.source;
51717     }
51718 });
51719
51720 Roo.grid.PropertyColumnModel = function(grid, store){
51721     this.grid = grid;
51722     var g = Roo.grid;
51723     g.PropertyColumnModel.superclass.constructor.call(this, [
51724         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51725         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51726     ]);
51727     this.store = store;
51728     this.bselect = Roo.DomHelper.append(document.body, {
51729         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51730             {tag: 'option', value: 'true', html: 'true'},
51731             {tag: 'option', value: 'false', html: 'false'}
51732         ]
51733     });
51734     Roo.id(this.bselect);
51735     var f = Roo.form;
51736     this.editors = {
51737         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51738         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51739         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51740         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51741         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51742     };
51743     this.renderCellDelegate = this.renderCell.createDelegate(this);
51744     this.renderPropDelegate = this.renderProp.createDelegate(this);
51745 };
51746
51747 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51748     
51749     
51750     nameText : 'Name',
51751     valueText : 'Value',
51752     
51753     dateFormat : 'm/j/Y',
51754     
51755     
51756     renderDate : function(dateVal){
51757         return dateVal.dateFormat(this.dateFormat);
51758     },
51759
51760     renderBool : function(bVal){
51761         return bVal ? 'true' : 'false';
51762     },
51763
51764     isCellEditable : function(colIndex, rowIndex){
51765         return colIndex == 1;
51766     },
51767
51768     getRenderer : function(col){
51769         return col == 1 ?
51770             this.renderCellDelegate : this.renderPropDelegate;
51771     },
51772
51773     renderProp : function(v){
51774         return this.getPropertyName(v);
51775     },
51776
51777     renderCell : function(val){
51778         var rv = val;
51779         if(val instanceof Date){
51780             rv = this.renderDate(val);
51781         }else if(typeof val == 'boolean'){
51782             rv = this.renderBool(val);
51783         }
51784         return Roo.util.Format.htmlEncode(rv);
51785     },
51786
51787     getPropertyName : function(name){
51788         var pn = this.grid.propertyNames;
51789         return pn && pn[name] ? pn[name] : name;
51790     },
51791
51792     getCellEditor : function(colIndex, rowIndex){
51793         var p = this.store.getProperty(rowIndex);
51794         var n = p.data['name'], val = p.data['value'];
51795         
51796         if(typeof(this.grid.customEditors[n]) == 'string'){
51797             return this.editors[this.grid.customEditors[n]];
51798         }
51799         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51800             return this.grid.customEditors[n];
51801         }
51802         if(val instanceof Date){
51803             return this.editors['date'];
51804         }else if(typeof val == 'number'){
51805             return this.editors['number'];
51806         }else if(typeof val == 'boolean'){
51807             return this.editors['boolean'];
51808         }else{
51809             return this.editors['string'];
51810         }
51811     }
51812 });
51813
51814 /**
51815  * @class Roo.grid.PropertyGrid
51816  * @extends Roo.grid.EditorGrid
51817  * This class represents the  interface of a component based property grid control.
51818  * <br><br>Usage:<pre><code>
51819  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51820       
51821  });
51822  // set any options
51823  grid.render();
51824  * </code></pre>
51825   
51826  * @constructor
51827  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51828  * The container MUST have some type of size defined for the grid to fill. The container will be
51829  * automatically set to position relative if it isn't already.
51830  * @param {Object} config A config object that sets properties on this grid.
51831  */
51832 Roo.grid.PropertyGrid = function(container, config){
51833     config = config || {};
51834     var store = new Roo.grid.PropertyStore(this);
51835     this.store = store;
51836     var cm = new Roo.grid.PropertyColumnModel(this, store);
51837     store.store.sort('name', 'ASC');
51838     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51839         ds: store.store,
51840         cm: cm,
51841         enableColLock:false,
51842         enableColumnMove:false,
51843         stripeRows:false,
51844         trackMouseOver: false,
51845         clicksToEdit:1
51846     }, config));
51847     this.getGridEl().addClass('x-props-grid');
51848     this.lastEditRow = null;
51849     this.on('columnresize', this.onColumnResize, this);
51850     this.addEvents({
51851          /**
51852              * @event beforepropertychange
51853              * Fires before a property changes (return false to stop?)
51854              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51855              * @param {String} id Record Id
51856              * @param {String} newval New Value
51857          * @param {String} oldval Old Value
51858              */
51859         "beforepropertychange": true,
51860         /**
51861              * @event propertychange
51862              * Fires after a property changes
51863              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51864              * @param {String} id Record Id
51865              * @param {String} newval New Value
51866          * @param {String} oldval Old Value
51867              */
51868         "propertychange": true
51869     });
51870     this.customEditors = this.customEditors || {};
51871 };
51872 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51873     
51874      /**
51875      * @cfg {Object} customEditors map of colnames=> custom editors.
51876      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51877      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51878      * false disables editing of the field.
51879          */
51880     
51881       /**
51882      * @cfg {Object} propertyNames map of property Names to their displayed value
51883          */
51884     
51885     render : function(){
51886         Roo.grid.PropertyGrid.superclass.render.call(this);
51887         this.autoSize.defer(100, this);
51888     },
51889
51890     autoSize : function(){
51891         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51892         if(this.view){
51893             this.view.fitColumns();
51894         }
51895     },
51896
51897     onColumnResize : function(){
51898         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51899         this.autoSize();
51900     },
51901     /**
51902      * Sets the data for the Grid
51903      * accepts a Key => Value object of all the elements avaiable.
51904      * @param {Object} data  to appear in grid.
51905      */
51906     setSource : function(source){
51907         this.store.setSource(source);
51908         //this.autoSize();
51909     },
51910     /**
51911      * Gets all the data from the grid.
51912      * @return {Object} data  data stored in grid
51913      */
51914     getSource : function(){
51915         return this.store.getSource();
51916     }
51917 });/*
51918  * Based on:
51919  * Ext JS Library 1.1.1
51920  * Copyright(c) 2006-2007, Ext JS, LLC.
51921  *
51922  * Originally Released Under LGPL - original licence link has changed is not relivant.
51923  *
51924  * Fork - LGPL
51925  * <script type="text/javascript">
51926  */
51927  
51928 /**
51929  * @class Roo.LoadMask
51930  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51931  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51932  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51933  * element's UpdateManager load indicator and will be destroyed after the initial load.
51934  * @constructor
51935  * Create a new LoadMask
51936  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51937  * @param {Object} config The config object
51938  */
51939 Roo.LoadMask = function(el, config){
51940     this.el = Roo.get(el);
51941     Roo.apply(this, config);
51942     if(this.store){
51943         this.store.on('beforeload', this.onBeforeLoad, this);
51944         this.store.on('load', this.onLoad, this);
51945         this.store.on('loadexception', this.onLoad, this);
51946         this.removeMask = false;
51947     }else{
51948         var um = this.el.getUpdateManager();
51949         um.showLoadIndicator = false; // disable the default indicator
51950         um.on('beforeupdate', this.onBeforeLoad, this);
51951         um.on('update', this.onLoad, this);
51952         um.on('failure', this.onLoad, this);
51953         this.removeMask = true;
51954     }
51955 };
51956
51957 Roo.LoadMask.prototype = {
51958     /**
51959      * @cfg {Boolean} removeMask
51960      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51961      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51962      */
51963     /**
51964      * @cfg {String} msg
51965      * The text to display in a centered loading message box (defaults to 'Loading...')
51966      */
51967     msg : 'Loading...',
51968     /**
51969      * @cfg {String} msgCls
51970      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51971      */
51972     msgCls : 'x-mask-loading',
51973
51974     /**
51975      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51976      * @type Boolean
51977      */
51978     disabled: false,
51979
51980     /**
51981      * Disables the mask to prevent it from being displayed
51982      */
51983     disable : function(){
51984        this.disabled = true;
51985     },
51986
51987     /**
51988      * Enables the mask so that it can be displayed
51989      */
51990     enable : function(){
51991         this.disabled = false;
51992     },
51993
51994     // private
51995     onLoad : function(){
51996         this.el.unmask(this.removeMask);
51997     },
51998
51999     // private
52000     onBeforeLoad : function(){
52001         if(!this.disabled){
52002             this.el.mask(this.msg, this.msgCls);
52003         }
52004     },
52005
52006     // private
52007     destroy : function(){
52008         if(this.store){
52009             this.store.un('beforeload', this.onBeforeLoad, this);
52010             this.store.un('load', this.onLoad, this);
52011             this.store.un('loadexception', this.onLoad, this);
52012         }else{
52013             var um = this.el.getUpdateManager();
52014             um.un('beforeupdate', this.onBeforeLoad, this);
52015             um.un('update', this.onLoad, this);
52016             um.un('failure', this.onLoad, this);
52017         }
52018     }
52019 };/*
52020  * Based on:
52021  * Ext JS Library 1.1.1
52022  * Copyright(c) 2006-2007, Ext JS, LLC.
52023  *
52024  * Originally Released Under LGPL - original licence link has changed is not relivant.
52025  *
52026  * Fork - LGPL
52027  * <script type="text/javascript">
52028  */
52029 Roo.XTemplate = function(){
52030     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52031     var s = this.html;
52032
52033     s = ['<tpl>', s, '</tpl>'].join('');
52034
52035     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52036
52037     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52038     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52039     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52040     var m, id = 0;
52041     var tpls = [];
52042
52043     while(m = s.match(re)){
52044        var m2 = m[0].match(nameRe);
52045        var m3 = m[0].match(ifRe);
52046        var m4 = m[0].match(execRe);
52047        var exp = null, fn = null, exec = null;
52048        var name = m2 && m2[1] ? m2[1] : '';
52049        if(m3){
52050            exp = m3 && m3[1] ? m3[1] : null;
52051            if(exp){
52052                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52053            }
52054        }
52055        if(m4){
52056            exp = m4 && m4[1] ? m4[1] : null;
52057            if(exp){
52058                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52059            }
52060        }
52061        if(name){
52062            switch(name){
52063                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52064                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52065                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52066            }
52067        }
52068        tpls.push({
52069             id: id,
52070             target: name,
52071             exec: exec,
52072             test: fn,
52073             body: m[1]||''
52074         });
52075        s = s.replace(m[0], '{xtpl'+ id + '}');
52076        ++id;
52077     }
52078     for(var i = tpls.length-1; i >= 0; --i){
52079         this.compileTpl(tpls[i]);
52080     }
52081     this.master = tpls[tpls.length-1];
52082     this.tpls = tpls;
52083 };
52084 Roo.extend(Roo.XTemplate, Roo.Template, {
52085
52086     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52087
52088     applySubTemplate : function(id, values, parent){
52089         var t = this.tpls[id];
52090         if(t.test && !t.test.call(this, values, parent)){
52091             return '';
52092         }
52093         if(t.exec && t.exec.call(this, values, parent)){
52094             return '';
52095         }
52096         var vs = t.target ? t.target.call(this, values, parent) : values;
52097         parent = t.target ? values : parent;
52098         if(t.target && vs instanceof Array){
52099             var buf = [];
52100             for(var i = 0, len = vs.length; i < len; i++){
52101                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52102             }
52103             return buf.join('');
52104         }
52105         return t.compiled.call(this, vs, parent);
52106     },
52107
52108     compileTpl : function(tpl){
52109         var fm = Roo.util.Format;
52110         var useF = this.disableFormats !== true;
52111         var sep = Roo.isGecko ? "+" : ",";
52112         var fn = function(m, name, format, args){
52113             if(name.substr(0, 4) == 'xtpl'){
52114                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52115             }
52116             var v;
52117             if(name.indexOf('.') != -1){
52118                 v = name;
52119             }else{
52120                 v = "values['" + name + "']";
52121             }
52122             if(format && useF){
52123                 args = args ? ',' + args : "";
52124                 if(format.substr(0, 5) != "this."){
52125                     format = "fm." + format + '(';
52126                 }else{
52127                     format = 'this.call("'+ format.substr(5) + '", ';
52128                     args = ", values";
52129                 }
52130             }else{
52131                 args= ''; format = "("+v+" === undefined ? '' : ";
52132             }
52133             return "'"+ sep + format + v + args + ")"+sep+"'";
52134         };
52135         var body;
52136         // branched to use + in gecko and [].join() in others
52137         if(Roo.isGecko){
52138             body = "tpl.compiled = function(values, parent){ return '" +
52139                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52140                     "';};";
52141         }else{
52142             body = ["tpl.compiled = function(values, parent){ return ['"];
52143             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52144             body.push("'].join('');};");
52145             body = body.join('');
52146         }
52147         /** eval:var:zzzzzzz */
52148         eval(body);
52149         return this;
52150     },
52151
52152     applyTemplate : function(values){
52153         return this.master.compiled.call(this, values, {});
52154         var s = this.subs;
52155     },
52156
52157     apply : function(){
52158         return this.applyTemplate.apply(this, arguments);
52159     },
52160
52161     compile : function(){return this;}
52162 });
52163
52164 Roo.XTemplate.from = function(el){
52165     el = Roo.getDom(el);
52166     return new Roo.XTemplate(el.value || el.innerHTML);
52167 };/*
52168  * Original code for Roojs - LGPL
52169  * <script type="text/javascript">
52170  */
52171  
52172 /**
52173  * @class Roo.XComponent
52174  * A delayed Element creator...
52175  * Or a way to group chunks of interface together.
52176  * 
52177  * Mypart.xyx = new Roo.XComponent({
52178
52179     parent : 'Mypart.xyz', // empty == document.element.!!
52180     order : '001',
52181     name : 'xxxx'
52182     region : 'xxxx'
52183     disabled : function() {} 
52184      
52185     tree : function() { // return an tree of xtype declared components
52186         var MODULE = this;
52187         return 
52188         {
52189             xtype : 'NestedLayoutPanel',
52190             // technicall
52191         }
52192      ]
52193  *})
52194  *
52195  *
52196  * It can be used to build a big heiracy, with parent etc.
52197  * or you can just use this to render a single compoent to a dom element
52198  * MYPART.render(Roo.Element | String(id) | dom_element )
52199  * 
52200  * @extends Roo.util.Observable
52201  * @constructor
52202  * @param cfg {Object} configuration of component
52203  * 
52204  */
52205 Roo.XComponent = function(cfg) {
52206     Roo.apply(this, cfg);
52207     this.addEvents({ 
52208         /**
52209              * @event built
52210              * Fires when this the componnt is built
52211              * @param {Roo.XComponent} c the component
52212              */
52213         'built' : true,
52214         /**
52215              * @event buildcomplete
52216              * Fires on the top level element when all elements have been built
52217              * @param {Roo.XComponent} c the top level component.
52218          */
52219         'buildcomplete' : true
52220         
52221     });
52222     this.region = this.region || 'center'; // default..
52223     Roo.XComponent.register(this);
52224     this.modules = false;
52225     this.el = false; // where the layout goes..
52226     
52227     
52228 }
52229 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52230     /**
52231      * @property el
52232      * The created element (with Roo.factory())
52233      * @type {Roo.Layout}
52234      */
52235     el  : false,
52236     
52237     /**
52238      * @property el
52239      * for BC  - use el in new code
52240      * @type {Roo.Layout}
52241      */
52242     panel : false,
52243     
52244     /**
52245      * @property layout
52246      * for BC  - use el in new code
52247      * @type {Roo.Layout}
52248      */
52249     layout : false,
52250     
52251      /**
52252      * @cfg {Function|boolean} disabled
52253      * If this module is disabled by some rule, return true from the funtion
52254      */
52255     disabled : false,
52256     
52257     /**
52258      * @cfg {String} parent 
52259      * Name of parent element which it get xtype added to..
52260      */
52261     parent: false,
52262     
52263     /**
52264      * @cfg {String} order
52265      * Used to set the order in which elements are created (usefull for multiple tabs)
52266      */
52267     
52268     order : false,
52269     /**
52270      * @cfg {String} name
52271      * String to display while loading.
52272      */
52273     name : false,
52274     /**
52275      * @cfg {String} region
52276      * Region to render component to (defaults to center)
52277      */
52278     region : 'center',
52279     
52280     /**
52281      * @cfg {Array} items
52282      * A single item array - the first element is the root of the tree..
52283      * It's done this way to stay compatible with the Xtype system...
52284      */
52285     items : false,
52286     
52287     
52288      /**
52289      * render
52290      * render element to dom or tree
52291      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52292      */
52293     
52294     render : function(el)
52295     {
52296         
52297         el = el || false;
52298         var hp = this.parent ? 1 : 0;
52299         
52300         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52301             // if parent is a '#.....' string, then let's use that..
52302             var ename = this.parent.substr(1)
52303             this.parent = false;
52304             el = Roo.get(ename);
52305             if (!el) {
52306                 Roo.log("Warning - element can not be found :#" + ename );
52307                 return;
52308             }
52309         }
52310         
52311         
52312         if (!this.parent) {
52313             
52314             el = el ? Roo.get(el) : false;
52315             
52316             // it's a top level one..
52317             this.parent =  {
52318                 el : new Roo.BorderLayout(el || document.body, {
52319                 
52320                      center: {
52321                          titlebar: false,
52322                          autoScroll:false,
52323                          closeOnTab: true,
52324                          tabPosition: 'top',
52325                           //resizeTabs: true,
52326                          alwaysShowTabs: el && hp? false :  true,
52327                          hideTabs: el || !hp ? true :  false,
52328                          minTabWidth: 140
52329                      }
52330                  })
52331             }
52332         }
52333         
52334         
52335             
52336         var tree = this.tree();
52337         tree.region = tree.region || this.region;
52338         this.el = this.parent.el.addxtype(tree);
52339         this.fireEvent('built', this);
52340         
52341         this.panel = this.el;
52342         this.layout = this.panel.layout;    
52343          
52344     }
52345     
52346 });
52347
52348 Roo.apply(Roo.XComponent, {
52349     
52350     /**
52351      * @property  buildCompleted
52352      * True when the builder has completed building the interface.
52353      * @type Boolean
52354      */
52355     buildCompleted : false,
52356      
52357     /**
52358      * @property  topModule
52359      * the upper most module - uses document.element as it's constructor.
52360      * @type Object
52361      */
52362      
52363     topModule  : false,
52364       
52365     /**
52366      * @property  modules
52367      * array of modules to be created by registration system.
52368      * @type {Array} of Roo.XComponent
52369      */
52370     
52371     modules : [],
52372     /**
52373      * @property  elmodules
52374      * array of modules to be created by which use #ID 
52375      * @type {Array} of Roo.XComponent
52376      */
52377      
52378     elmodules : [],
52379
52380     
52381     /**
52382      * Register components to be built later.
52383      *
52384      * This solves the following issues
52385      * - Building is not done on page load, but after an authentication process has occured.
52386      * - Interface elements are registered on page load
52387      * - Parent Interface elements may not be loaded before child, so this handles that..
52388      * 
52389      *
52390      * example:
52391      * 
52392      * MyApp.register({
52393           order : '000001',
52394           module : 'Pman.Tab.projectMgr',
52395           region : 'center',
52396           parent : 'Pman.layout',
52397           disabled : false,  // or use a function..
52398         })
52399      
52400      * * @param {Object} details about module
52401      */
52402     register : function(obj) {
52403         this.modules.push(obj);
52404          
52405     },
52406     /**
52407      * convert a string to an object..
52408      * eg. 'AAA.BBB' -> finds AAA.BBB
52409
52410      */
52411     
52412     toObject : function(str)
52413     {
52414         if (!str || typeof(str) == 'object') {
52415             return str;
52416         }
52417         if (str.substring(0,1) == '#') {
52418             return str;
52419         }
52420
52421         var ar = str.split('.');
52422         var rt, o;
52423         rt = ar.shift();
52424             /** eval:var:o */
52425         try {
52426             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52427         } catch (e) {
52428             throw "Module not found : " + str;
52429         }
52430         
52431         if (o === false) {
52432             throw "Module not found : " + str;
52433         }
52434         Roo.each(ar, function(e) {
52435             if (typeof(o[e]) == 'undefined') {
52436                 throw "Module not found : " + str;
52437             }
52438             o = o[e];
52439         });
52440         
52441         return o;
52442         
52443     },
52444     
52445     
52446     /**
52447      * move modules into their correct place in the tree..
52448      * 
52449      */
52450     preBuild : function ()
52451     {
52452         var _t = this;
52453         Roo.each(this.modules , function (obj)
52454         {
52455             var opar = obj.parent;
52456             try { 
52457                 obj.parent = this.toObject(opar);
52458             } catch(e) {
52459                 Roo.log(e.toString());
52460                 return;
52461             }
52462             
52463             if (!obj.parent) {
52464                 this.topModule = obj;
52465                 return;
52466             }
52467             if (typeof(obj.parent) == 'string') {
52468                 this.elmodules.push(obj);
52469                 return;
52470             }
52471             if (obj.parent.constructor != Roo.XComponent) {
52472                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52473             }
52474             if (!obj.parent.modules) {
52475                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52476                     function(o) { return o.order + '' }
52477                 );
52478             }
52479             
52480             obj.parent.modules.add(obj);
52481         }, this);
52482     },
52483     
52484      /**
52485      * make a list of modules to build.
52486      * @return {Array} list of modules. 
52487      */ 
52488     
52489     buildOrder : function()
52490     {
52491         var _this = this;
52492         var cmp = function(a,b) {   
52493             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52494         };
52495         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52496             throw "No top level modules to build";
52497         }
52498         
52499         // make a flat list in order of modules to build.
52500         var mods = this.topModule ? [ this.topModule ] : [];
52501         Roo.each(this.elmodules,function(e) { mods.push(e) });
52502
52503         
52504         // add modules to their parents..
52505         var addMod = function(m) {
52506            // Roo.debug && Roo.log(m.modKey);
52507             
52508             mods.push(m);
52509             if (m.modules) {
52510                 m.modules.keySort('ASC',  cmp );
52511                 m.modules.each(addMod);
52512             }
52513             // not sure if this is used any more..
52514             if (m.finalize) {
52515                 m.finalize.name = m.name + " (clean up) ";
52516                 mods.push(m.finalize);
52517             }
52518             
52519         }
52520         if (this.topModule) { 
52521             this.topModule.modules.keySort('ASC',  cmp );
52522             this.topModule.modules.each(addMod);
52523         }
52524         return mods;
52525     },
52526     
52527      /**
52528      * Build the registered modules.
52529      * @param {Object} parent element.
52530      * @param {Function} optional method to call after module has been added.
52531      * 
52532      */ 
52533    
52534     build : function() 
52535     {
52536         
52537         this.preBuild();
52538         var mods = this.buildOrder();
52539       
52540         //this.allmods = mods;
52541         //Roo.debug && Roo.log(mods);
52542         //return;
52543         if (!mods.length) { // should not happen
52544             throw "NO modules!!!";
52545         }
52546         
52547         
52548         
52549         // flash it up as modal - so we store the mask!?
52550         Roo.MessageBox.show({ title: 'loading' });
52551         Roo.MessageBox.show({
52552            title: "Please wait...",
52553            msg: "Building Interface...",
52554            width:450,
52555            progress:true,
52556            closable:false,
52557            modal: false
52558           
52559         });
52560         var total = mods.length;
52561         
52562         var _this = this;
52563         var progressRun = function() {
52564             if (!mods.length) {
52565                 Roo.debug && Roo.log('hide?');
52566                 Roo.MessageBox.hide();
52567                 if (_this.topModule) { 
52568                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52569                 }
52570                 // THE END...
52571                 return false;   
52572             }
52573             
52574             var m = mods.shift();
52575             
52576             
52577             Roo.debug && Roo.log(m);
52578             // not sure if this is supported any more.. - modules that are are just function
52579             if (typeof(m) == 'function') { 
52580                 m.call(this);
52581                 return progressRun.defer(10, _this);
52582             } 
52583             
52584             
52585             
52586             Roo.MessageBox.updateProgress(
52587                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52588                     " of " + total + 
52589                     (m.name ? (' - ' + m.name) : '')
52590                     );
52591             
52592          
52593             // is the module disabled?
52594             var disabled = (typeof(m.disabled) == 'function') ?
52595                 m.disabled.call(m.module.disabled) : m.disabled;    
52596             
52597             
52598             if (disabled) {
52599                 return progressRun(); // we do not update the display!
52600             }
52601             
52602             // now build 
52603             
52604             m.render();
52605             // it's 10 on top level, and 1 on others??? why...
52606             return progressRun.defer(10, _this);
52607              
52608         }
52609         progressRun.defer(1, _this);
52610      
52611         
52612         
52613     }
52614     
52615      
52616    
52617     
52618     
52619 });
52620  //<script type="text/javascript">
52621
52622
52623 /**
52624  * @class Roo.Login
52625  * @extends Roo.LayoutDialog
52626  * A generic Login Dialog..... - only one needed in theory!?!?
52627  *
52628  * Fires XComponent builder on success...
52629  * 
52630  * Sends 
52631  *    username,password, lang = for login actions.
52632  *    check = 1 for periodic checking that sesion is valid.
52633  *    passwordRequest = email request password
52634  *    logout = 1 = to logout
52635  * 
52636  * Affects: (this id="????" elements)
52637  *   loading  (removed) (used to indicate application is loading)
52638  *   loading-mask (hides) (used to hide application when it's building loading)
52639  *   
52640  * 
52641  * Usage: 
52642  *    
52643  * 
52644  * Myapp.login = Roo.Login({
52645      url: xxxx,
52646    
52647      realm : 'Myapp', 
52648      
52649      
52650      method : 'POST',
52651      
52652      
52653      * 
52654  })
52655  * 
52656  * 
52657  * 
52658  **/
52659  
52660 Roo.Login = function(cfg)
52661 {
52662     this.addEvents({
52663         'refreshed' : true
52664     });
52665     
52666     Roo.apply(this,cfg);
52667     
52668     Roo.onReady(function() {
52669         this.onLoad();
52670     }, this);
52671     // call parent..
52672     
52673    
52674     Roo.Login.superclass.constructor.call(this, this);
52675     //this.addxtype(this.items[0]);
52676     
52677     
52678 }
52679
52680
52681 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52682     
52683     /**
52684      * @cfg {String} method
52685      * Method used to query for login details.
52686      */
52687     
52688     method : 'POST',
52689     /**
52690      * @cfg {String} url
52691      * URL to query login data. - eg. baseURL + '/Login.php'
52692      */
52693     url : '',
52694     
52695     /**
52696      * @property user
52697      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52698      * @type {Object} 
52699      */
52700     user : false,
52701     /**
52702      * @property checkFails
52703      * Number of times we have attempted to get authentication check, and failed.
52704      * @type {Number} 
52705      */
52706     checkFails : 0,
52707       /**
52708      * @property intervalID
52709      * The window interval that does the constant login checking.
52710      * @type {Number} 
52711      */
52712     intervalID : 0,
52713     
52714     
52715     onLoad : function() // called on page load...
52716     {
52717         // load 
52718          
52719         if (Roo.get('loading')) { // clear any loading indicator..
52720             Roo.get('loading').remove();
52721         }
52722         
52723         //this.switchLang('en'); // set the language to english..
52724        
52725         this.check({
52726             success:  function(response, opts)  {  // check successfull...
52727             
52728                 var res = this.processResponse(response);
52729                 this.checkFails =0;
52730                 if (!res.success) { // error!
52731                     this.checkFails = 5;
52732                     //console.log('call failure');
52733                     return this.failure(response,opts);
52734                 }
52735                 
52736                 if (!res.data.id) { // id=0 == login failure.
52737                     return this.show();
52738                 }
52739                 
52740                               
52741                         //console.log(success);
52742                 this.fillAuth(res.data);   
52743                 this.checkFails =0;
52744                 Roo.XComponent.build();
52745             },
52746             failure : this.show
52747         });
52748         
52749     }, 
52750     
52751     
52752     check: function(cfg) // called every so often to refresh cookie etc..
52753     {
52754         if (cfg.again) { // could be undefined..
52755             this.checkFails++;
52756         } else {
52757             this.checkFails = 0;
52758         }
52759         var _this = this;
52760         if (this.sending) {
52761             if ( this.checkFails > 4) {
52762                 Roo.MessageBox.alert("Error",  
52763                     "Error getting authentication status. - try reloading, or wait a while", function() {
52764                         _this.sending = false;
52765                     }); 
52766                 return;
52767             }
52768             cfg.again = true;
52769             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52770             return;
52771         }
52772         this.sending = true;
52773         
52774         Roo.Ajax.request({  
52775             url: this.url,
52776             params: {
52777                 getAuthUser: true
52778             },  
52779             method: this.method,
52780             success:  cfg.success || this.success,
52781             failure : cfg.failure || this.failure,
52782             scope : this,
52783             callCfg : cfg
52784               
52785         });  
52786     }, 
52787     
52788     
52789     logout: function()
52790     {
52791         window.onbeforeunload = function() { }; // false does not work for IE..
52792         this.user = false;
52793         var _this = this;
52794         
52795         Roo.Ajax.request({  
52796             url: this.url,
52797             params: {
52798                 logout: 1
52799             },  
52800             method: 'GET',
52801             failure : function() {
52802                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52803                     document.location = document.location.toString() + '?ts=' + Math.random();
52804                 });
52805                 
52806             },
52807             success : function() {
52808                 _this.user = false;
52809                 this.checkFails =0;
52810                 // fixme..
52811                 document.location = document.location.toString() + '?ts=' + Math.random();
52812             }
52813               
52814               
52815         }); 
52816     },
52817     
52818     processResponse : function (response)
52819     {
52820         var res = '';
52821         try {
52822             res = Roo.decode(response.responseText);
52823             // oops...
52824             if (typeof(res) != 'object') {
52825                 res = { success : false, errorMsg : res, errors : true };
52826             }
52827             if (typeof(res.success) == 'undefined') {
52828                 res.success = false;
52829             }
52830             
52831         } catch(e) {
52832             res = { success : false,  errorMsg : response.responseText, errors : true };
52833         }
52834         return res;
52835     },
52836     
52837     success : function(response, opts)  // check successfull...
52838     {  
52839         this.sending = false;
52840         var res = this.processResponse(response);
52841         if (!res.success) {
52842             return this.failure(response, opts);
52843         }
52844         if (!res.data || !res.data.id) {
52845             return this.failure(response,opts);
52846         }
52847         //console.log(res);
52848         this.fillAuth(res.data);
52849         
52850         this.checkFails =0;
52851         
52852     },
52853     
52854     
52855     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52856     {
52857         this.authUser = -1;
52858         this.sending = false;
52859         var res = this.processResponse(response);
52860         //console.log(res);
52861         if ( this.checkFails > 2) {
52862         
52863             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52864                 "Error getting authentication status. - try reloading"); 
52865             return;
52866         }
52867         opts.callCfg.again = true;
52868         this.check.defer(1000, this, [ opts.callCfg ]);
52869         return;  
52870     },
52871     
52872     
52873     
52874     fillAuth: function(au) {
52875         this.startAuthCheck();
52876         this.authUserId = au.id;
52877         this.authUser = au;
52878         this.lastChecked = new Date();
52879         this.fireEvent('refreshed', au);
52880         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52881         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52882         au.lang = au.lang || 'en';
52883         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52884         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52885         this.switchLang(au.lang );
52886         
52887      
52888         // open system... - -on setyp..
52889         if (this.authUserId  < 0) {
52890             Roo.MessageBox.alert("Warning", 
52891                 "This is an open system - please set up a admin user with a password.");  
52892         }
52893          
52894         //Pman.onload(); // which should do nothing if it's a re-auth result...
52895         
52896              
52897     },
52898     
52899     startAuthCheck : function() // starter for timeout checking..
52900     {
52901         if (this.intervalID) { // timer already in place...
52902             return false;
52903         }
52904         var _this = this;
52905         this.intervalID =  window.setInterval(function() {
52906               _this.check(false);
52907             }, 120000); // every 120 secs = 2mins..
52908         
52909         
52910     },
52911          
52912     
52913     switchLang : function (lang) 
52914     {
52915         _T = typeof(_T) == 'undefined' ? false : _T;
52916           if (!_T || !lang.length) {
52917             return;
52918         }
52919         
52920         if (!_T && lang != 'en') {
52921             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52922             return;
52923         }
52924         
52925         if (typeof(_T.en) == 'undefined') {
52926             _T.en = {};
52927             Roo.apply(_T.en, _T);
52928         }
52929         
52930         if (typeof(_T[lang]) == 'undefined') {
52931             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52932             return;
52933         }
52934         
52935         
52936         Roo.apply(_T, _T[lang]);
52937         // just need to set the text values for everything...
52938         var _this = this;
52939         /* this will not work ...
52940         if (this.form) { 
52941             
52942                
52943             function formLabel(name, val) {
52944                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52945             }
52946             
52947             formLabel('password', "Password"+':');
52948             formLabel('username', "Email Address"+':');
52949             formLabel('lang', "Language"+':');
52950             this.dialog.setTitle("Login");
52951             this.dialog.buttons[0].setText("Forgot Password");
52952             this.dialog.buttons[1].setText("Login");
52953         }
52954         */
52955         
52956         
52957     },
52958     
52959     
52960     title: "Login",
52961     modal: true,
52962     width:  350,
52963     //height: 230,
52964     height: 180,
52965     shadow: true,
52966     minWidth:200,
52967     minHeight:180,
52968     //proxyDrag: true,
52969     closable: false,
52970     draggable: false,
52971     collapsible: false,
52972     resizable: false,
52973     center: {  // needed??
52974         autoScroll:false,
52975         titlebar: false,
52976        // tabPosition: 'top',
52977         hideTabs: true,
52978         closeOnTab: true,
52979         alwaysShowTabs: false
52980     } ,
52981     listeners : {
52982         
52983         show  : function(dlg)
52984         {
52985             //console.log(this);
52986             this.form = this.layout.getRegion('center').activePanel.form;
52987             this.form.dialog = dlg;
52988             this.buttons[0].form = this.form;
52989             this.buttons[0].dialog = dlg;
52990             this.buttons[1].form = this.form;
52991             this.buttons[1].dialog = dlg;
52992            
52993            //this.resizeToLogo.defer(1000,this);
52994             // this is all related to resizing for logos..
52995             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52996            //// if (!sz) {
52997              //   this.resizeToLogo.defer(1000,this);
52998              //   return;
52999            // }
53000             //var w = Ext.lib.Dom.getViewWidth() - 100;
53001             //var h = Ext.lib.Dom.getViewHeight() - 100;
53002             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53003             //this.center();
53004             if (this.disabled) {
53005                 this.hide();
53006                 return;
53007             }
53008             
53009             if (this.user.id < 0) { // used for inital setup situations.
53010                 return;
53011             }
53012             
53013             if (this.intervalID) {
53014                 // remove the timer
53015                 window.clearInterval(this.intervalID);
53016                 this.intervalID = false;
53017             }
53018             
53019             
53020             if (Roo.get('loading')) {
53021                 Roo.get('loading').remove();
53022             }
53023             if (Roo.get('loading-mask')) {
53024                 Roo.get('loading-mask').hide();
53025             }
53026             
53027             //incomming._node = tnode;
53028             this.form.reset();
53029             //this.dialog.modal = !modal;
53030             //this.dialog.show();
53031             this.el.unmask(); 
53032             
53033             
53034             this.form.setValues({
53035                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53036                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53037             });
53038             
53039             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53040             if (this.form.findField('username').getValue().length > 0 ){
53041                 this.form.findField('password').focus();
53042             } else {
53043                this.form.findField('username').focus();
53044             }
53045     
53046         }
53047     },
53048     items : [
53049          {
53050        
53051             xtype : 'ContentPanel',
53052             xns : Roo,
53053             region: 'center',
53054             fitToFrame : true,
53055             
53056             items : [
53057     
53058                 {
53059                
53060                     xtype : 'Form',
53061                     xns : Roo.form,
53062                     labelWidth: 100,
53063                     style : 'margin: 10px;',
53064                     
53065                     listeners : {
53066                         actionfailed : function(f, act) {
53067                             // form can return { errors: .... }
53068                                 
53069                             //act.result.errors // invalid form element list...
53070                             //act.result.errorMsg// invalid form element list...
53071                             
53072                             this.dialog.el.unmask();
53073                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53074                                         "Login failed - communication error - try again.");
53075                                       
53076                         },
53077                         actioncomplete: function(re, act) {
53078                              
53079                             Roo.state.Manager.set(
53080                                 this.dialog.realm + '.username',  
53081                                     this.findField('username').getValue()
53082                             );
53083                             Roo.state.Manager.set(
53084                                 this.dialog.realm + '.lang',  
53085                                 this.findField('lang').getValue() 
53086                             );
53087                             
53088                             this.dialog.fillAuth(act.result.data);
53089                               
53090                             this.dialog.hide();
53091                             
53092                             if (Roo.get('loading-mask')) {
53093                                 Roo.get('loading-mask').show();
53094                             }
53095                             Roo.XComponent.build();
53096                             
53097                              
53098                             
53099                         }
53100                     },
53101                     items : [
53102                         {
53103                             xtype : 'TextField',
53104                             xns : Roo.form,
53105                             fieldLabel: "Email Address",
53106                             name: 'username',
53107                             width:200,
53108                             autoCreate : {tag: "input", type: "text", size: "20"}
53109                         },
53110                         {
53111                             xtype : 'TextField',
53112                             xns : Roo.form,
53113                             fieldLabel: "Password",
53114                             inputType: 'password',
53115                             name: 'password',
53116                             width:200,
53117                             autoCreate : {tag: "input", type: "text", size: "20"},
53118                             listeners : {
53119                                 specialkey : function(e,ev) {
53120                                     if (ev.keyCode == 13) {
53121                                         this.form.dialog.el.mask("Logging in");
53122                                         this.form.doAction('submit', {
53123                                             url: this.form.dialog.url,
53124                                             method: this.form.dialog.method
53125                                         });
53126                                     }
53127                                 }
53128                             }  
53129                         },
53130                         {
53131                             xtype : 'ComboBox',
53132                             xns : Roo.form,
53133                             fieldLabel: "Language",
53134                             name : 'langdisp',
53135                             store: {
53136                                 xtype : 'SimpleStore',
53137                                 fields: ['lang', 'ldisp'],
53138                                 data : [
53139                                     [ 'en', 'English' ],
53140                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53141                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53142                                 ]
53143                             },
53144                             
53145                             valueField : 'lang',
53146                             hiddenName:  'lang',
53147                             width: 200,
53148                             displayField:'ldisp',
53149                             typeAhead: false,
53150                             editable: false,
53151                             mode: 'local',
53152                             triggerAction: 'all',
53153                             emptyText:'Select a Language...',
53154                             selectOnFocus:true,
53155                             listeners : {
53156                                 select :  function(cb, rec, ix) {
53157                                     this.form.switchLang(rec.data.lang);
53158                                 }
53159                             }
53160                         
53161                         }
53162                     ]
53163                 }
53164                   
53165                 
53166             ]
53167         }
53168     ],
53169     buttons : [
53170         {
53171             xtype : 'Button',
53172             xns : 'Roo',
53173             text : "Forgot Password",
53174             listeners : {
53175                 click : function() {
53176                     //console.log(this);
53177                     var n = this.form.findField('username').getValue();
53178                     if (!n.length) {
53179                         Roo.MessageBox.alert("Error", "Fill in your email address");
53180                         return;
53181                     }
53182                     Roo.Ajax.request({
53183                         url: this.dialog.url,
53184                         params: {
53185                             passwordRequest: n
53186                         },
53187                         method: this.dialog.method,
53188                         success:  function(response, opts)  {  // check successfull...
53189                         
53190                             var res = this.dialog.processResponse(response);
53191                             if (!res.success) { // error!
53192                                Roo.MessageBox.alert("Error" ,
53193                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53194                                return;
53195                             }
53196                             Roo.MessageBox.alert("Notice" ,
53197                                 "Please check you email for the Password Reset message");
53198                         },
53199                         failure : function() {
53200                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53201                         }
53202                         
53203                     });
53204                 }
53205             }
53206         },
53207         {
53208             xtype : 'Button',
53209             xns : 'Roo',
53210             text : "Login",
53211             listeners : {
53212                 
53213                 click : function () {
53214                         
53215                     this.dialog.el.mask("Logging in");
53216                     this.form.doAction('submit', {
53217                             url: this.dialog.url,
53218                             method: this.dialog.method
53219                     });
53220                 }
53221             }
53222         }
53223     ]
53224   
53225   
53226 })
53227  
53228
53229
53230