update treeloader to default to allow standard JSON encoding format, along with BC...
[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 (Allows +08, without minutes)
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{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 Roo.lib.Dom = {
1778     getViewWidth : function(full) {
1779         return full ? this.getDocumentWidth() : this.getViewportWidth();
1780     },
1781
1782     getViewHeight : function(full) {
1783         return full ? this.getDocumentHeight() : this.getViewportHeight();
1784     },
1785
1786     getDocumentHeight: function() {
1787         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1788         return Math.max(scrollHeight, this.getViewportHeight());
1789     },
1790
1791     getDocumentWidth: function() {
1792         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1793         return Math.max(scrollWidth, this.getViewportWidth());
1794     },
1795
1796     getViewportHeight: function() {
1797         var height = self.innerHeight;
1798         var mode = document.compatMode;
1799
1800         if ((mode || Roo.isIE) && !Roo.isOpera) {
1801             height = (mode == "CSS1Compat") ?
1802                      document.documentElement.clientHeight :
1803                      document.body.clientHeight;
1804         }
1805
1806         return height;
1807     },
1808
1809     getViewportWidth: function() {
1810         var width = self.innerWidth;
1811         var mode = document.compatMode;
1812
1813         if (mode || Roo.isIE) {
1814             width = (mode == "CSS1Compat") ?
1815                     document.documentElement.clientWidth :
1816                     document.body.clientWidth;
1817         }
1818         return width;
1819     },
1820
1821     isAncestor : function(p, c) {
1822         p = Roo.getDom(p);
1823         c = Roo.getDom(c);
1824         if (!p || !c) {
1825             return false;
1826         }
1827
1828         if (p.contains && !Roo.isSafari) {
1829             return p.contains(c);
1830         } else if (p.compareDocumentPosition) {
1831             return !!(p.compareDocumentPosition(c) & 16);
1832         } else {
1833             var parent = c.parentNode;
1834             while (parent) {
1835                 if (parent == p) {
1836                     return true;
1837                 }
1838                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1839                     return false;
1840                 }
1841                 parent = parent.parentNode;
1842             }
1843             return false;
1844         }
1845     },
1846
1847     getRegion : function(el) {
1848         return Roo.lib.Region.getRegion(el);
1849     },
1850
1851     getY : function(el) {
1852         return this.getXY(el)[1];
1853     },
1854
1855     getX : function(el) {
1856         return this.getXY(el)[0];
1857     },
1858
1859     getXY : function(el) {
1860         var p, pe, b, scroll, bd = document.body;
1861         el = Roo.getDom(el);
1862         var fly = Roo.lib.AnimBase.fly;
1863         if (el.getBoundingClientRect) {
1864             b = el.getBoundingClientRect();
1865             scroll = fly(document).getScroll();
1866             return [b.left + scroll.left, b.top + scroll.top];
1867         }
1868         var x = 0, y = 0;
1869
1870         p = el;
1871
1872         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1873
1874         while (p) {
1875
1876             x += p.offsetLeft;
1877             y += p.offsetTop;
1878
1879             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1880                 hasAbsolute = true;
1881             }
1882
1883             if (Roo.isGecko) {
1884                 pe = fly(p);
1885
1886                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1887                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1888
1889
1890                 x += bl;
1891                 y += bt;
1892
1893
1894                 if (p != el && pe.getStyle('overflow') != 'visible') {
1895                     x += bl;
1896                     y += bt;
1897                 }
1898             }
1899             p = p.offsetParent;
1900         }
1901
1902         if (Roo.isSafari && hasAbsolute) {
1903             x -= bd.offsetLeft;
1904             y -= bd.offsetTop;
1905         }
1906
1907         if (Roo.isGecko && !hasAbsolute) {
1908             var dbd = fly(bd);
1909             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1910             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1911         }
1912
1913         p = el.parentNode;
1914         while (p && p != bd) {
1915             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1916                 x -= p.scrollLeft;
1917                 y -= p.scrollTop;
1918             }
1919             p = p.parentNode;
1920         }
1921         return [x, y];
1922     },
1923  
1924   
1925
1926
1927     setXY : function(el, xy) {
1928         el = Roo.fly(el, '_setXY');
1929         el.position();
1930         var pts = el.translatePoints(xy);
1931         if (xy[0] !== false) {
1932             el.dom.style.left = pts.left + "px";
1933         }
1934         if (xy[1] !== false) {
1935             el.dom.style.top = pts.top + "px";
1936         }
1937     },
1938
1939     setX : function(el, x) {
1940         this.setXY(el, [x, false]);
1941     },
1942
1943     setY : function(el, y) {
1944         this.setXY(el, [false, y]);
1945     }
1946 };
1947 /*
1948  * Portions of this file are based on pieces of Yahoo User Interface Library
1949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1950  * YUI licensed under the BSD License:
1951  * http://developer.yahoo.net/yui/license.txt
1952  * <script type="text/javascript">
1953  *
1954  */
1955
1956 Roo.lib.Event = function() {
1957     var loadComplete = false;
1958     var listeners = [];
1959     var unloadListeners = [];
1960     var retryCount = 0;
1961     var onAvailStack = [];
1962     var counter = 0;
1963     var lastError = null;
1964
1965     return {
1966         POLL_RETRYS: 200,
1967         POLL_INTERVAL: 20,
1968         EL: 0,
1969         TYPE: 1,
1970         FN: 2,
1971         WFN: 3,
1972         OBJ: 3,
1973         ADJ_SCOPE: 4,
1974         _interval: null,
1975
1976         startInterval: function() {
1977             if (!this._interval) {
1978                 var self = this;
1979                 var callback = function() {
1980                     self._tryPreloadAttach();
1981                 };
1982                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1983
1984             }
1985         },
1986
1987         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1988             onAvailStack.push({ id:         p_id,
1989                 fn:         p_fn,
1990                 obj:        p_obj,
1991                 override:   p_override,
1992                 checkReady: false    });
1993
1994             retryCount = this.POLL_RETRYS;
1995             this.startInterval();
1996         },
1997
1998
1999         addListener: function(el, eventName, fn) {
2000             el = Roo.getDom(el);
2001             if (!el || !fn) {
2002                 return false;
2003             }
2004
2005             if ("unload" == eventName) {
2006                 unloadListeners[unloadListeners.length] =
2007                 [el, eventName, fn];
2008                 return true;
2009             }
2010
2011             var wrappedFn = function(e) {
2012                 return fn(Roo.lib.Event.getEvent(e));
2013             };
2014
2015             var li = [el, eventName, fn, wrappedFn];
2016
2017             var index = listeners.length;
2018             listeners[index] = li;
2019
2020             this.doAdd(el, eventName, wrappedFn, false);
2021             return true;
2022
2023         },
2024
2025
2026         removeListener: function(el, eventName, fn) {
2027             var i, len;
2028
2029             el = Roo.getDom(el);
2030
2031             if(!fn) {
2032                 return this.purgeElement(el, false, eventName);
2033             }
2034
2035
2036             if ("unload" == eventName) {
2037
2038                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2039                     var li = unloadListeners[i];
2040                     if (li &&
2041                         li[0] == el &&
2042                         li[1] == eventName &&
2043                         li[2] == fn) {
2044                         unloadListeners.splice(i, 1);
2045                         return true;
2046                     }
2047                 }
2048
2049                 return false;
2050             }
2051
2052             var cacheItem = null;
2053
2054
2055             var index = arguments[3];
2056
2057             if ("undefined" == typeof index) {
2058                 index = this._getCacheIndex(el, eventName, fn);
2059             }
2060
2061             if (index >= 0) {
2062                 cacheItem = listeners[index];
2063             }
2064
2065             if (!el || !cacheItem) {
2066                 return false;
2067             }
2068
2069             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2070
2071             delete listeners[index][this.WFN];
2072             delete listeners[index][this.FN];
2073             listeners.splice(index, 1);
2074
2075             return true;
2076
2077         },
2078
2079
2080         getTarget: function(ev, resolveTextNode) {
2081             ev = ev.browserEvent || ev;
2082             var t = ev.target || ev.srcElement;
2083             return this.resolveTextNode(t);
2084         },
2085
2086
2087         resolveTextNode: function(node) {
2088             if (Roo.isSafari && node && 3 == node.nodeType) {
2089                 return node.parentNode;
2090             } else {
2091                 return node;
2092             }
2093         },
2094
2095
2096         getPageX: function(ev) {
2097             ev = ev.browserEvent || ev;
2098             var x = ev.pageX;
2099             if (!x && 0 !== x) {
2100                 x = ev.clientX || 0;
2101
2102                 if (Roo.isIE) {
2103                     x += this.getScroll()[1];
2104                 }
2105             }
2106
2107             return x;
2108         },
2109
2110
2111         getPageY: function(ev) {
2112             ev = ev.browserEvent || ev;
2113             var y = ev.pageY;
2114             if (!y && 0 !== y) {
2115                 y = ev.clientY || 0;
2116
2117                 if (Roo.isIE) {
2118                     y += this.getScroll()[0];
2119                 }
2120             }
2121
2122
2123             return y;
2124         },
2125
2126
2127         getXY: function(ev) {
2128             ev = ev.browserEvent || ev;
2129             return [this.getPageX(ev), this.getPageY(ev)];
2130         },
2131
2132
2133         getRelatedTarget: function(ev) {
2134             ev = ev.browserEvent || ev;
2135             var t = ev.relatedTarget;
2136             if (!t) {
2137                 if (ev.type == "mouseout") {
2138                     t = ev.toElement;
2139                 } else if (ev.type == "mouseover") {
2140                     t = ev.fromElement;
2141                 }
2142             }
2143
2144             return this.resolveTextNode(t);
2145         },
2146
2147
2148         getTime: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             if (!ev.time) {
2151                 var t = new Date().getTime();
2152                 try {
2153                     ev.time = t;
2154                 } catch(ex) {
2155                     this.lastError = ex;
2156                     return t;
2157                 }
2158             }
2159
2160             return ev.time;
2161         },
2162
2163
2164         stopEvent: function(ev) {
2165             this.stopPropagation(ev);
2166             this.preventDefault(ev);
2167         },
2168
2169
2170         stopPropagation: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             if (ev.stopPropagation) {
2173                 ev.stopPropagation();
2174             } else {
2175                 ev.cancelBubble = true;
2176             }
2177         },
2178
2179
2180         preventDefault: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             if(ev.preventDefault) {
2183                 ev.preventDefault();
2184             } else {
2185                 ev.returnValue = false;
2186             }
2187         },
2188
2189
2190         getEvent: function(e) {
2191             var ev = e || window.event;
2192             if (!ev) {
2193                 var c = this.getEvent.caller;
2194                 while (c) {
2195                     ev = c.arguments[0];
2196                     if (ev && Event == ev.constructor) {
2197                         break;
2198                     }
2199                     c = c.caller;
2200                 }
2201             }
2202             return ev;
2203         },
2204
2205
2206         getCharCode: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             return ev.charCode || ev.keyCode || 0;
2209         },
2210
2211
2212         _getCacheIndex: function(el, eventName, fn) {
2213             for (var i = 0,len = listeners.length; i < len; ++i) {
2214                 var li = listeners[i];
2215                 if (li &&
2216                     li[this.FN] == fn &&
2217                     li[this.EL] == el &&
2218                     li[this.TYPE] == eventName) {
2219                     return i;
2220                 }
2221             }
2222
2223             return -1;
2224         },
2225
2226
2227         elCache: {},
2228
2229
2230         getEl: function(id) {
2231             return document.getElementById(id);
2232         },
2233
2234
2235         clearCache: function() {
2236         },
2237
2238
2239         _load: function(e) {
2240             loadComplete = true;
2241             var EU = Roo.lib.Event;
2242
2243
2244             if (Roo.isIE) {
2245                 EU.doRemove(window, "load", EU._load);
2246             }
2247         },
2248
2249
2250         _tryPreloadAttach: function() {
2251
2252             if (this.locked) {
2253                 return false;
2254             }
2255
2256             this.locked = true;
2257
2258
2259             var tryAgain = !loadComplete;
2260             if (!tryAgain) {
2261                 tryAgain = (retryCount > 0);
2262             }
2263
2264
2265             var notAvail = [];
2266             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2267                 var item = onAvailStack[i];
2268                 if (item) {
2269                     var el = this.getEl(item.id);
2270
2271                     if (el) {
2272                         if (!item.checkReady ||
2273                             loadComplete ||
2274                             el.nextSibling ||
2275                             (document && document.body)) {
2276
2277                             var scope = el;
2278                             if (item.override) {
2279                                 if (item.override === true) {
2280                                     scope = item.obj;
2281                                 } else {
2282                                     scope = item.override;
2283                                 }
2284                             }
2285                             item.fn.call(scope, item.obj);
2286                             onAvailStack[i] = null;
2287                         }
2288                     } else {
2289                         notAvail.push(item);
2290                     }
2291                 }
2292             }
2293
2294             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2295
2296             if (tryAgain) {
2297
2298                 this.startInterval();
2299             } else {
2300                 clearInterval(this._interval);
2301                 this._interval = null;
2302             }
2303
2304             this.locked = false;
2305
2306             return true;
2307
2308         },
2309
2310
2311         purgeElement: function(el, recurse, eventName) {
2312             var elListeners = this.getListeners(el, eventName);
2313             if (elListeners) {
2314                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2315                     var l = elListeners[i];
2316                     this.removeListener(el, l.type, l.fn);
2317                 }
2318             }
2319
2320             if (recurse && el && el.childNodes) {
2321                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2322                     this.purgeElement(el.childNodes[i], recurse, eventName);
2323                 }
2324             }
2325         },
2326
2327
2328         getListeners: function(el, eventName) {
2329             var results = [], searchLists;
2330             if (!eventName) {
2331                 searchLists = [listeners, unloadListeners];
2332             } else if (eventName == "unload") {
2333                 searchLists = [unloadListeners];
2334             } else {
2335                 searchLists = [listeners];
2336             }
2337
2338             for (var j = 0; j < searchLists.length; ++j) {
2339                 var searchList = searchLists[j];
2340                 if (searchList && searchList.length > 0) {
2341                     for (var i = 0,len = searchList.length; i < len; ++i) {
2342                         var l = searchList[i];
2343                         if (l && l[this.EL] === el &&
2344                             (!eventName || eventName === l[this.TYPE])) {
2345                             results.push({
2346                                 type:   l[this.TYPE],
2347                                 fn:     l[this.FN],
2348                                 obj:    l[this.OBJ],
2349                                 adjust: l[this.ADJ_SCOPE],
2350                                 index:  i
2351                             });
2352                         }
2353                     }
2354                 }
2355             }
2356
2357             return (results.length) ? results : null;
2358         },
2359
2360
2361         _unload: function(e) {
2362
2363             var EU = Roo.lib.Event, i, j, l, len, index;
2364
2365             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2366                 l = unloadListeners[i];
2367                 if (l) {
2368                     var scope = window;
2369                     if (l[EU.ADJ_SCOPE]) {
2370                         if (l[EU.ADJ_SCOPE] === true) {
2371                             scope = l[EU.OBJ];
2372                         } else {
2373                             scope = l[EU.ADJ_SCOPE];
2374                         }
2375                     }
2376                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2377                     unloadListeners[i] = null;
2378                     l = null;
2379                     scope = null;
2380                 }
2381             }
2382
2383             unloadListeners = null;
2384
2385             if (listeners && listeners.length > 0) {
2386                 j = listeners.length;
2387                 while (j) {
2388                     index = j - 1;
2389                     l = listeners[index];
2390                     if (l) {
2391                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2392                                 l[EU.FN], index);
2393                     }
2394                     j = j - 1;
2395                 }
2396                 l = null;
2397
2398                 EU.clearCache();
2399             }
2400
2401             EU.doRemove(window, "unload", EU._unload);
2402
2403         },
2404
2405
2406         getScroll: function() {
2407             var dd = document.documentElement, db = document.body;
2408             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2409                 return [dd.scrollTop, dd.scrollLeft];
2410             } else if (db) {
2411                 return [db.scrollTop, db.scrollLeft];
2412             } else {
2413                 return [0, 0];
2414             }
2415         },
2416
2417
2418         doAdd: function () {
2419             if (window.addEventListener) {
2420                 return function(el, eventName, fn, capture) {
2421                     el.addEventListener(eventName, fn, (capture));
2422                 };
2423             } else if (window.attachEvent) {
2424                 return function(el, eventName, fn, capture) {
2425                     el.attachEvent("on" + eventName, fn);
2426                 };
2427             } else {
2428                 return function() {
2429                 };
2430             }
2431         }(),
2432
2433
2434         doRemove: function() {
2435             if (window.removeEventListener) {
2436                 return function (el, eventName, fn, capture) {
2437                     el.removeEventListener(eventName, fn, (capture));
2438                 };
2439             } else if (window.detachEvent) {
2440                 return function (el, eventName, fn) {
2441                     el.detachEvent("on" + eventName, fn);
2442                 };
2443             } else {
2444                 return function() {
2445                 };
2446             }
2447         }()
2448     };
2449     
2450 }();
2451 (function() {     
2452    
2453     var E = Roo.lib.Event;
2454     E.on = E.addListener;
2455     E.un = E.removeListener;
2456
2457     if (document && document.body) {
2458         E._load();
2459     } else {
2460         E.doAdd(window, "load", E._load);
2461     }
2462     E.doAdd(window, "unload", E._unload);
2463     E._tryPreloadAttach();
2464 })();
2465
2466 /*
2467  * Portions of this file are based on pieces of Yahoo User Interface Library
2468  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2469  * YUI licensed under the BSD License:
2470  * http://developer.yahoo.net/yui/license.txt
2471  * <script type="text/javascript">
2472  *
2473  */
2474
2475 (function() {
2476     /**
2477      * @class Roo.lib.Ajax
2478      *
2479      */
2480     Roo.lib.Ajax = {
2481         /**
2482          * @static 
2483          */
2484         request : function(method, uri, cb, data, options) {
2485             if(options){
2486                 var hs = options.headers;
2487                 if(hs){
2488                     for(var h in hs){
2489                         if(hs.hasOwnProperty(h)){
2490                             this.initHeader(h, hs[h], false);
2491                         }
2492                     }
2493                 }
2494                 if(options.xmlData){
2495                     this.initHeader('Content-Type', 'text/xml', false);
2496                     method = 'POST';
2497                     data = options.xmlData;
2498                 }
2499             }
2500
2501             return this.asyncRequest(method, uri, cb, data);
2502         },
2503
2504         serializeForm : function(form) {
2505             if(typeof form == 'string') {
2506                 form = (document.getElementById(form) || document.forms[form]);
2507             }
2508
2509             var el, name, val, disabled, data = '', hasSubmit = false;
2510             for (var i = 0; i < form.elements.length; i++) {
2511                 el = form.elements[i];
2512                 disabled = form.elements[i].disabled;
2513                 name = form.elements[i].name;
2514                 val = form.elements[i].value;
2515
2516                 if (!disabled && name){
2517                     switch (el.type)
2518                             {
2519                         case 'select-one':
2520                         case 'select-multiple':
2521                             for (var j = 0; j < el.options.length; j++) {
2522                                 if (el.options[j].selected) {
2523                                     if (Roo.isIE) {
2524                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2525                                     }
2526                                     else {
2527                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2528                                     }
2529                                 }
2530                             }
2531                             break;
2532                         case 'radio':
2533                         case 'checkbox':
2534                             if (el.checked) {
2535                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2536                             }
2537                             break;
2538                         case 'file':
2539
2540                         case undefined:
2541
2542                         case 'reset':
2543
2544                         case 'button':
2545
2546                             break;
2547                         case 'submit':
2548                             if(hasSubmit == false) {
2549                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2550                                 hasSubmit = true;
2551                             }
2552                             break;
2553                         default:
2554                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2555                             break;
2556                     }
2557                 }
2558             }
2559             data = data.substr(0, data.length - 1);
2560             return data;
2561         },
2562
2563         headers:{},
2564
2565         hasHeaders:false,
2566
2567         useDefaultHeader:true,
2568
2569         defaultPostHeader:'application/x-www-form-urlencoded',
2570
2571         useDefaultXhrHeader:true,
2572
2573         defaultXhrHeader:'XMLHttpRequest',
2574
2575         hasDefaultHeaders:true,
2576
2577         defaultHeaders:{},
2578
2579         poll:{},
2580
2581         timeout:{},
2582
2583         pollInterval:50,
2584
2585         transactionId:0,
2586
2587         setProgId:function(id)
2588         {
2589             this.activeX.unshift(id);
2590         },
2591
2592         setDefaultPostHeader:function(b)
2593         {
2594             this.useDefaultHeader = b;
2595         },
2596
2597         setDefaultXhrHeader:function(b)
2598         {
2599             this.useDefaultXhrHeader = b;
2600         },
2601
2602         setPollingInterval:function(i)
2603         {
2604             if (typeof i == 'number' && isFinite(i)) {
2605                 this.pollInterval = i;
2606             }
2607         },
2608
2609         createXhrObject:function(transactionId)
2610         {
2611             var obj,http;
2612             try
2613             {
2614
2615                 http = new XMLHttpRequest();
2616
2617                 obj = { conn:http, tId:transactionId };
2618             }
2619             catch(e)
2620             {
2621                 for (var i = 0; i < this.activeX.length; ++i) {
2622                     try
2623                     {
2624
2625                         http = new ActiveXObject(this.activeX[i]);
2626
2627                         obj = { conn:http, tId:transactionId };
2628                         break;
2629                     }
2630                     catch(e) {
2631                     }
2632                 }
2633             }
2634             finally
2635             {
2636                 return obj;
2637             }
2638         },
2639
2640         getConnectionObject:function()
2641         {
2642             var o;
2643             var tId = this.transactionId;
2644
2645             try
2646             {
2647                 o = this.createXhrObject(tId);
2648                 if (o) {
2649                     this.transactionId++;
2650                 }
2651             }
2652             catch(e) {
2653             }
2654             finally
2655             {
2656                 return o;
2657             }
2658         },
2659
2660         asyncRequest:function(method, uri, callback, postData)
2661         {
2662             var o = this.getConnectionObject();
2663
2664             if (!o) {
2665                 return null;
2666             }
2667             else {
2668                 o.conn.open(method, uri, true);
2669
2670                 if (this.useDefaultXhrHeader) {
2671                     if (!this.defaultHeaders['X-Requested-With']) {
2672                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2673                     }
2674                 }
2675
2676                 if(postData && this.useDefaultHeader){
2677                     this.initHeader('Content-Type', this.defaultPostHeader);
2678                 }
2679
2680                  if (this.hasDefaultHeaders || this.hasHeaders) {
2681                     this.setHeader(o);
2682                 }
2683
2684                 this.handleReadyState(o, callback);
2685                 o.conn.send(postData || null);
2686
2687                 return o;
2688             }
2689         },
2690
2691         handleReadyState:function(o, callback)
2692         {
2693             var oConn = this;
2694
2695             if (callback && callback.timeout) {
2696                 this.timeout[o.tId] = window.setTimeout(function() {
2697                     oConn.abort(o, callback, true);
2698                 }, callback.timeout);
2699             }
2700
2701             this.poll[o.tId] = window.setInterval(
2702                     function() {
2703                         if (o.conn && o.conn.readyState == 4) {
2704                             window.clearInterval(oConn.poll[o.tId]);
2705                             delete oConn.poll[o.tId];
2706
2707                             if(callback && callback.timeout) {
2708                                 window.clearTimeout(oConn.timeout[o.tId]);
2709                                 delete oConn.timeout[o.tId];
2710                             }
2711
2712                             oConn.handleTransactionResponse(o, callback);
2713                         }
2714                     }
2715                     , this.pollInterval);
2716         },
2717
2718         handleTransactionResponse:function(o, callback, isAbort)
2719         {
2720
2721             if (!callback) {
2722                 this.releaseObject(o);
2723                 return;
2724             }
2725
2726             var httpStatus, responseObject;
2727
2728             try
2729             {
2730                 if (o.conn.status !== undefined && o.conn.status != 0) {
2731                     httpStatus = o.conn.status;
2732                 }
2733                 else {
2734                     httpStatus = 13030;
2735                 }
2736             }
2737             catch(e) {
2738
2739
2740                 httpStatus = 13030;
2741             }
2742
2743             if (httpStatus >= 200 && httpStatus < 300) {
2744                 responseObject = this.createResponseObject(o, callback.argument);
2745                 if (callback.success) {
2746                     if (!callback.scope) {
2747                         callback.success(responseObject);
2748                     }
2749                     else {
2750
2751
2752                         callback.success.apply(callback.scope, [responseObject]);
2753                     }
2754                 }
2755             }
2756             else {
2757                 switch (httpStatus) {
2758
2759                     case 12002:
2760                     case 12029:
2761                     case 12030:
2762                     case 12031:
2763                     case 12152:
2764                     case 13030:
2765                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2766                         if (callback.failure) {
2767                             if (!callback.scope) {
2768                                 callback.failure(responseObject);
2769                             }
2770                             else {
2771                                 callback.failure.apply(callback.scope, [responseObject]);
2772                             }
2773                         }
2774                         break;
2775                     default:
2776                         responseObject = this.createResponseObject(o, callback.argument);
2777                         if (callback.failure) {
2778                             if (!callback.scope) {
2779                                 callback.failure(responseObject);
2780                             }
2781                             else {
2782                                 callback.failure.apply(callback.scope, [responseObject]);
2783                             }
2784                         }
2785                 }
2786             }
2787
2788             this.releaseObject(o);
2789             responseObject = null;
2790         },
2791
2792         createResponseObject:function(o, callbackArg)
2793         {
2794             var obj = {};
2795             var headerObj = {};
2796
2797             try
2798             {
2799                 var headerStr = o.conn.getAllResponseHeaders();
2800                 var header = headerStr.split('\n');
2801                 for (var i = 0; i < header.length; i++) {
2802                     var delimitPos = header[i].indexOf(':');
2803                     if (delimitPos != -1) {
2804                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2805                     }
2806                 }
2807             }
2808             catch(e) {
2809             }
2810
2811             obj.tId = o.tId;
2812             obj.status = o.conn.status;
2813             obj.statusText = o.conn.statusText;
2814             obj.getResponseHeader = headerObj;
2815             obj.getAllResponseHeaders = headerStr;
2816             obj.responseText = o.conn.responseText;
2817             obj.responseXML = o.conn.responseXML;
2818
2819             if (typeof callbackArg !== undefined) {
2820                 obj.argument = callbackArg;
2821             }
2822
2823             return obj;
2824         },
2825
2826         createExceptionObject:function(tId, callbackArg, isAbort)
2827         {
2828             var COMM_CODE = 0;
2829             var COMM_ERROR = 'communication failure';
2830             var ABORT_CODE = -1;
2831             var ABORT_ERROR = 'transaction aborted';
2832
2833             var obj = {};
2834
2835             obj.tId = tId;
2836             if (isAbort) {
2837                 obj.status = ABORT_CODE;
2838                 obj.statusText = ABORT_ERROR;
2839             }
2840             else {
2841                 obj.status = COMM_CODE;
2842                 obj.statusText = COMM_ERROR;
2843             }
2844
2845             if (callbackArg) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         initHeader:function(label, value, isDefault)
2853         {
2854             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2855
2856             if (headerObj[label] === undefined) {
2857                 headerObj[label] = value;
2858             }
2859             else {
2860
2861
2862                 headerObj[label] = value + "," + headerObj[label];
2863             }
2864
2865             if (isDefault) {
2866                 this.hasDefaultHeaders = true;
2867             }
2868             else {
2869                 this.hasHeaders = true;
2870             }
2871         },
2872
2873
2874         setHeader:function(o)
2875         {
2876             if (this.hasDefaultHeaders) {
2877                 for (var prop in this.defaultHeaders) {
2878                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2879                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2880                     }
2881                 }
2882             }
2883
2884             if (this.hasHeaders) {
2885                 for (var prop in this.headers) {
2886                     if (this.headers.hasOwnProperty(prop)) {
2887                         o.conn.setRequestHeader(prop, this.headers[prop]);
2888                     }
2889                 }
2890                 this.headers = {};
2891                 this.hasHeaders = false;
2892             }
2893         },
2894
2895         resetDefaultHeaders:function() {
2896             delete this.defaultHeaders;
2897             this.defaultHeaders = {};
2898             this.hasDefaultHeaders = false;
2899         },
2900
2901         abort:function(o, callback, isTimeout)
2902         {
2903             if(this.isCallInProgress(o)) {
2904                 o.conn.abort();
2905                 window.clearInterval(this.poll[o.tId]);
2906                 delete this.poll[o.tId];
2907                 if (isTimeout) {
2908                     delete this.timeout[o.tId];
2909                 }
2910
2911                 this.handleTransactionResponse(o, callback, true);
2912
2913                 return true;
2914             }
2915             else {
2916                 return false;
2917             }
2918         },
2919
2920
2921         isCallInProgress:function(o)
2922         {
2923             if (o && o.conn) {
2924                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2925             }
2926             else {
2927
2928                 return false;
2929             }
2930         },
2931
2932
2933         releaseObject:function(o)
2934         {
2935
2936             o.conn = null;
2937
2938             o = null;
2939         },
2940
2941         activeX:[
2942         'MSXML2.XMLHTTP.3.0',
2943         'MSXML2.XMLHTTP',
2944         'Microsoft.XMLHTTP'
2945         ]
2946
2947
2948     };
2949 })();/*
2950  * Portions of this file are based on pieces of Yahoo User Interface Library
2951  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2952  * YUI licensed under the BSD License:
2953  * http://developer.yahoo.net/yui/license.txt
2954  * <script type="text/javascript">
2955  *
2956  */
2957
2958 Roo.lib.Region = function(t, r, b, l) {
2959     this.top = t;
2960     this[1] = t;
2961     this.right = r;
2962     this.bottom = b;
2963     this.left = l;
2964     this[0] = l;
2965 };
2966
2967
2968 Roo.lib.Region.prototype = {
2969     contains : function(region) {
2970         return ( region.left >= this.left &&
2971                  region.right <= this.right &&
2972                  region.top >= this.top &&
2973                  region.bottom <= this.bottom    );
2974
2975     },
2976
2977     getArea : function() {
2978         return ( (this.bottom - this.top) * (this.right - this.left) );
2979     },
2980
2981     intersect : function(region) {
2982         var t = Math.max(this.top, region.top);
2983         var r = Math.min(this.right, region.right);
2984         var b = Math.min(this.bottom, region.bottom);
2985         var l = Math.max(this.left, region.left);
2986
2987         if (b >= t && r >= l) {
2988             return new Roo.lib.Region(t, r, b, l);
2989         } else {
2990             return null;
2991         }
2992     },
2993     union : function(region) {
2994         var t = Math.min(this.top, region.top);
2995         var r = Math.max(this.right, region.right);
2996         var b = Math.max(this.bottom, region.bottom);
2997         var l = Math.min(this.left, region.left);
2998
2999         return new Roo.lib.Region(t, r, b, l);
3000     },
3001
3002     adjust : function(t, l, b, r) {
3003         this.top += t;
3004         this.left += l;
3005         this.right += r;
3006         this.bottom += b;
3007         return this;
3008     }
3009 };
3010
3011 Roo.lib.Region.getRegion = function(el) {
3012     var p = Roo.lib.Dom.getXY(el);
3013
3014     var t = p[1];
3015     var r = p[0] + el.offsetWidth;
3016     var b = p[1] + el.offsetHeight;
3017     var l = p[0];
3018
3019     return new Roo.lib.Region(t, r, b, l);
3020 };
3021 /*
3022  * Portions of this file are based on pieces of Yahoo User Interface Library
3023  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3024  * YUI licensed under the BSD License:
3025  * http://developer.yahoo.net/yui/license.txt
3026  * <script type="text/javascript">
3027  *
3028  */
3029 //@@dep Roo.lib.Region
3030
3031
3032 Roo.lib.Point = function(x, y) {
3033     if (x instanceof Array) {
3034         y = x[1];
3035         x = x[0];
3036     }
3037     this.x = this.right = this.left = this[0] = x;
3038     this.y = this.top = this.bottom = this[1] = y;
3039 };
3040
3041 Roo.lib.Point.prototype = new Roo.lib.Region();
3042 /*
3043  * Portions of this file are based on pieces of Yahoo User Interface Library
3044  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3045  * YUI licensed under the BSD License:
3046  * http://developer.yahoo.net/yui/license.txt
3047  * <script type="text/javascript">
3048  *
3049  */
3050  
3051 (function() {   
3052
3053     Roo.lib.Anim = {
3054         scroll : function(el, args, duration, easing, cb, scope) {
3055             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3056         },
3057
3058         motion : function(el, args, duration, easing, cb, scope) {
3059             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3060         },
3061
3062         color : function(el, args, duration, easing, cb, scope) {
3063             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3064         },
3065
3066         run : function(el, args, duration, easing, cb, scope, type) {
3067             type = type || Roo.lib.AnimBase;
3068             if (typeof easing == "string") {
3069                 easing = Roo.lib.Easing[easing];
3070             }
3071             var anim = new type(el, args, duration, easing);
3072             anim.animateX(function() {
3073                 Roo.callback(cb, scope);
3074             });
3075             return anim;
3076         }
3077     };
3078 })();/*
3079  * Portions of this file are based on pieces of Yahoo User Interface Library
3080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3081  * YUI licensed under the BSD License:
3082  * http://developer.yahoo.net/yui/license.txt
3083  * <script type="text/javascript">
3084  *
3085  */
3086
3087 (function() {    
3088     var libFlyweight;
3089     
3090     function fly(el) {
3091         if (!libFlyweight) {
3092             libFlyweight = new Roo.Element.Flyweight();
3093         }
3094         libFlyweight.dom = el;
3095         return libFlyweight;
3096     }
3097
3098     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3099     
3100    
3101     
3102     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3103         if (el) {
3104             this.init(el, attributes, duration, method);
3105         }
3106     };
3107
3108     Roo.lib.AnimBase.fly = fly;
3109     
3110     
3111     
3112     Roo.lib.AnimBase.prototype = {
3113
3114         toString: function() {
3115             var el = this.getEl();
3116             var id = el.id || el.tagName;
3117             return ("Anim " + id);
3118         },
3119
3120         patterns: {
3121             noNegatives:        /width|height|opacity|padding/i,
3122             offsetAttribute:  /^((width|height)|(top|left))$/,
3123             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3124             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3125         },
3126
3127
3128         doMethod: function(attr, start, end) {
3129             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3130         },
3131
3132
3133         setAttribute: function(attr, val, unit) {
3134             if (this.patterns.noNegatives.test(attr)) {
3135                 val = (val > 0) ? val : 0;
3136             }
3137
3138             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3139         },
3140
3141
3142         getAttribute: function(attr) {
3143             var el = this.getEl();
3144             var val = fly(el).getStyle(attr);
3145
3146             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3147                 return parseFloat(val);
3148             }
3149
3150             var a = this.patterns.offsetAttribute.exec(attr) || [];
3151             var pos = !!( a[3] );
3152             var box = !!( a[2] );
3153
3154
3155             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3156                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3157             } else {
3158                 val = 0;
3159             }
3160
3161             return val;
3162         },
3163
3164
3165         getDefaultUnit: function(attr) {
3166             if (this.patterns.defaultUnit.test(attr)) {
3167                 return 'px';
3168             }
3169
3170             return '';
3171         },
3172
3173         animateX : function(callback, scope) {
3174             var f = function() {
3175                 this.onComplete.removeListener(f);
3176                 if (typeof callback == "function") {
3177                     callback.call(scope || this, this);
3178                 }
3179             };
3180             this.onComplete.addListener(f, this);
3181             this.animate();
3182         },
3183
3184
3185         setRuntimeAttribute: function(attr) {
3186             var start;
3187             var end;
3188             var attributes = this.attributes;
3189
3190             this.runtimeAttributes[attr] = {};
3191
3192             var isset = function(prop) {
3193                 return (typeof prop !== 'undefined');
3194             };
3195
3196             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3197                 return false;
3198             }
3199
3200             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3201
3202
3203             if (isset(attributes[attr]['to'])) {
3204                 end = attributes[attr]['to'];
3205             } else if (isset(attributes[attr]['by'])) {
3206                 if (start.constructor == Array) {
3207                     end = [];
3208                     for (var i = 0, len = start.length; i < len; ++i) {
3209                         end[i] = start[i] + attributes[attr]['by'][i];
3210                     }
3211                 } else {
3212                     end = start + attributes[attr]['by'];
3213                 }
3214             }
3215
3216             this.runtimeAttributes[attr].start = start;
3217             this.runtimeAttributes[attr].end = end;
3218
3219
3220             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3221         },
3222
3223
3224         init: function(el, attributes, duration, method) {
3225
3226             var isAnimated = false;
3227
3228
3229             var startTime = null;
3230
3231
3232             var actualFrames = 0;
3233
3234
3235             el = Roo.getDom(el);
3236
3237
3238             this.attributes = attributes || {};
3239
3240
3241             this.duration = duration || 1;
3242
3243
3244             this.method = method || Roo.lib.Easing.easeNone;
3245
3246
3247             this.useSeconds = true;
3248
3249
3250             this.currentFrame = 0;
3251
3252
3253             this.totalFrames = Roo.lib.AnimMgr.fps;
3254
3255
3256             this.getEl = function() {
3257                 return el;
3258             };
3259
3260
3261             this.isAnimated = function() {
3262                 return isAnimated;
3263             };
3264
3265
3266             this.getStartTime = function() {
3267                 return startTime;
3268             };
3269
3270             this.runtimeAttributes = {};
3271
3272
3273             this.animate = function() {
3274                 if (this.isAnimated()) {
3275                     return false;
3276                 }
3277
3278                 this.currentFrame = 0;
3279
3280                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3281
3282                 Roo.lib.AnimMgr.registerElement(this);
3283             };
3284
3285
3286             this.stop = function(finish) {
3287                 if (finish) {
3288                     this.currentFrame = this.totalFrames;
3289                     this._onTween.fire();
3290                 }
3291                 Roo.lib.AnimMgr.stop(this);
3292             };
3293
3294             var onStart = function() {
3295                 this.onStart.fire();
3296
3297                 this.runtimeAttributes = {};
3298                 for (var attr in this.attributes) {
3299                     this.setRuntimeAttribute(attr);
3300                 }
3301
3302                 isAnimated = true;
3303                 actualFrames = 0;
3304                 startTime = new Date();
3305             };
3306
3307
3308             var onTween = function() {
3309                 var data = {
3310                     duration: new Date() - this.getStartTime(),
3311                     currentFrame: this.currentFrame
3312                 };
3313
3314                 data.toString = function() {
3315                     return (
3316                             'duration: ' + data.duration +
3317                             ', currentFrame: ' + data.currentFrame
3318                             );
3319                 };
3320
3321                 this.onTween.fire(data);
3322
3323                 var runtimeAttributes = this.runtimeAttributes;
3324
3325                 for (var attr in runtimeAttributes) {
3326                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3327                 }
3328
3329                 actualFrames += 1;
3330             };
3331
3332             var onComplete = function() {
3333                 var actual_duration = (new Date() - startTime) / 1000 ;
3334
3335                 var data = {
3336                     duration: actual_duration,
3337                     frames: actualFrames,
3338                     fps: actualFrames / actual_duration
3339                 };
3340
3341                 data.toString = function() {
3342                     return (
3343                             'duration: ' + data.duration +
3344                             ', frames: ' + data.frames +
3345                             ', fps: ' + data.fps
3346                             );
3347                 };
3348
3349                 isAnimated = false;
3350                 actualFrames = 0;
3351                 this.onComplete.fire(data);
3352             };
3353
3354
3355             this._onStart = new Roo.util.Event(this);
3356             this.onStart = new Roo.util.Event(this);
3357             this.onTween = new Roo.util.Event(this);
3358             this._onTween = new Roo.util.Event(this);
3359             this.onComplete = new Roo.util.Event(this);
3360             this._onComplete = new Roo.util.Event(this);
3361             this._onStart.addListener(onStart);
3362             this._onTween.addListener(onTween);
3363             this._onComplete.addListener(onComplete);
3364         }
3365     };
3366 })();
3367 /*
3368  * Portions of this file are based on pieces of Yahoo User Interface Library
3369  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3370  * YUI licensed under the BSD License:
3371  * http://developer.yahoo.net/yui/license.txt
3372  * <script type="text/javascript">
3373  *
3374  */
3375
3376 Roo.lib.AnimMgr = new function() {
3377
3378         var thread = null;
3379
3380
3381         var queue = [];
3382
3383
3384         var tweenCount = 0;
3385
3386
3387         this.fps = 1000;
3388
3389
3390         this.delay = 1;
3391
3392
3393         this.registerElement = function(tween) {
3394             queue[queue.length] = tween;
3395             tweenCount += 1;
3396             tween._onStart.fire();
3397             this.start();
3398         };
3399
3400
3401         this.unRegister = function(tween, index) {
3402             tween._onComplete.fire();
3403             index = index || getIndex(tween);
3404             if (index != -1) {
3405                 queue.splice(index, 1);
3406             }
3407
3408             tweenCount -= 1;
3409             if (tweenCount <= 0) {
3410                 this.stop();
3411             }
3412         };
3413
3414
3415         this.start = function() {
3416             if (thread === null) {
3417                 thread = setInterval(this.run, this.delay);
3418             }
3419         };
3420
3421
3422         this.stop = function(tween) {
3423             if (!tween) {
3424                 clearInterval(thread);
3425
3426                 for (var i = 0, len = queue.length; i < len; ++i) {
3427                     if (queue[0].isAnimated()) {
3428                         this.unRegister(queue[0], 0);
3429                     }
3430                 }
3431
3432                 queue = [];
3433                 thread = null;
3434                 tweenCount = 0;
3435             }
3436             else {
3437                 this.unRegister(tween);
3438             }
3439         };
3440
3441
3442         this.run = function() {
3443             for (var i = 0, len = queue.length; i < len; ++i) {
3444                 var tween = queue[i];
3445                 if (!tween || !tween.isAnimated()) {
3446                     continue;
3447                 }
3448
3449                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3450                 {
3451                     tween.currentFrame += 1;
3452
3453                     if (tween.useSeconds) {
3454                         correctFrame(tween);
3455                     }
3456                     tween._onTween.fire();
3457                 }
3458                 else {
3459                     Roo.lib.AnimMgr.stop(tween, i);
3460                 }
3461             }
3462         };
3463
3464         var getIndex = function(anim) {
3465             for (var i = 0, len = queue.length; i < len; ++i) {
3466                 if (queue[i] == anim) {
3467                     return i;
3468                 }
3469             }
3470             return -1;
3471         };
3472
3473
3474         var correctFrame = function(tween) {
3475             var frames = tween.totalFrames;
3476             var frame = tween.currentFrame;
3477             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3478             var elapsed = (new Date() - tween.getStartTime());
3479             var tweak = 0;
3480
3481             if (elapsed < tween.duration * 1000) {
3482                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3483             } else {
3484                 tweak = frames - (frame + 1);
3485             }
3486             if (tweak > 0 && isFinite(tweak)) {
3487                 if (tween.currentFrame + tweak >= frames) {
3488                     tweak = frames - (frame + 1);
3489                 }
3490
3491                 tween.currentFrame += tweak;
3492             }
3493         };
3494     };/*
3495  * Portions of this file are based on pieces of Yahoo User Interface Library
3496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3497  * YUI licensed under the BSD License:
3498  * http://developer.yahoo.net/yui/license.txt
3499  * <script type="text/javascript">
3500  *
3501  */
3502 Roo.lib.Bezier = new function() {
3503
3504         this.getPosition = function(points, t) {
3505             var n = points.length;
3506             var tmp = [];
3507
3508             for (var i = 0; i < n; ++i) {
3509                 tmp[i] = [points[i][0], points[i][1]];
3510             }
3511
3512             for (var j = 1; j < n; ++j) {
3513                 for (i = 0; i < n - j; ++i) {
3514                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3515                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3516                 }
3517             }
3518
3519             return [ tmp[0][0], tmp[0][1] ];
3520
3521         };
3522     };/*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 (function() {
3531
3532     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3533         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3534     };
3535
3536     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3537
3538     var fly = Roo.lib.AnimBase.fly;
3539     var Y = Roo.lib;
3540     var superclass = Y.ColorAnim.superclass;
3541     var proto = Y.ColorAnim.prototype;
3542
3543     proto.toString = function() {
3544         var el = this.getEl();
3545         var id = el.id || el.tagName;
3546         return ("ColorAnim " + id);
3547     };
3548
3549     proto.patterns.color = /color$/i;
3550     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3551     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3552     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3553     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3554
3555
3556     proto.parseColor = function(s) {
3557         if (s.length == 3) {
3558             return s;
3559         }
3560
3561         var c = this.patterns.hex.exec(s);
3562         if (c && c.length == 4) {
3563             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3564         }
3565
3566         c = this.patterns.rgb.exec(s);
3567         if (c && c.length == 4) {
3568             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3569         }
3570
3571         c = this.patterns.hex3.exec(s);
3572         if (c && c.length == 4) {
3573             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3574         }
3575
3576         return null;
3577     };
3578     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3579     proto.getAttribute = function(attr) {
3580         var el = this.getEl();
3581         if (this.patterns.color.test(attr)) {
3582             var val = fly(el).getStyle(attr);
3583
3584             if (this.patterns.transparent.test(val)) {
3585                 var parent = el.parentNode;
3586                 val = fly(parent).getStyle(attr);
3587
3588                 while (parent && this.patterns.transparent.test(val)) {
3589                     parent = parent.parentNode;
3590                     val = fly(parent).getStyle(attr);
3591                     if (parent.tagName.toUpperCase() == 'HTML') {
3592                         val = '#fff';
3593                     }
3594                 }
3595             }
3596         } else {
3597             val = superclass.getAttribute.call(this, attr);
3598         }
3599
3600         return val;
3601     };
3602     proto.getAttribute = function(attr) {
3603         var el = this.getEl();
3604         if (this.patterns.color.test(attr)) {
3605             var val = fly(el).getStyle(attr);
3606
3607             if (this.patterns.transparent.test(val)) {
3608                 var parent = el.parentNode;
3609                 val = fly(parent).getStyle(attr);
3610
3611                 while (parent && this.patterns.transparent.test(val)) {
3612                     parent = parent.parentNode;
3613                     val = fly(parent).getStyle(attr);
3614                     if (parent.tagName.toUpperCase() == 'HTML') {
3615                         val = '#fff';
3616                     }
3617                 }
3618             }
3619         } else {
3620             val = superclass.getAttribute.call(this, attr);
3621         }
3622
3623         return val;
3624     };
3625
3626     proto.doMethod = function(attr, start, end) {
3627         var val;
3628
3629         if (this.patterns.color.test(attr)) {
3630             val = [];
3631             for (var i = 0, len = start.length; i < len; ++i) {
3632                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3633             }
3634
3635             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3636         }
3637         else {
3638             val = superclass.doMethod.call(this, attr, start, end);
3639         }
3640
3641         return val;
3642     };
3643
3644     proto.setRuntimeAttribute = function(attr) {
3645         superclass.setRuntimeAttribute.call(this, attr);
3646
3647         if (this.patterns.color.test(attr)) {
3648             var attributes = this.attributes;
3649             var start = this.parseColor(this.runtimeAttributes[attr].start);
3650             var end = this.parseColor(this.runtimeAttributes[attr].end);
3651
3652             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3653                 end = this.parseColor(attributes[attr].by);
3654
3655                 for (var i = 0, len = start.length; i < len; ++i) {
3656                     end[i] = start[i] + end[i];
3657                 }
3658             }
3659
3660             this.runtimeAttributes[attr].start = start;
3661             this.runtimeAttributes[attr].end = end;
3662         }
3663     };
3664 })();
3665
3666 /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Easing = {
3675
3676
3677     easeNone: function (t, b, c, d) {
3678         return c * t / d + b;
3679     },
3680
3681
3682     easeIn: function (t, b, c, d) {
3683         return c * (t /= d) * t + b;
3684     },
3685
3686
3687     easeOut: function (t, b, c, d) {
3688         return -c * (t /= d) * (t - 2) + b;
3689     },
3690
3691
3692     easeBoth: function (t, b, c, d) {
3693         if ((t /= d / 2) < 1) {
3694             return c / 2 * t * t + b;
3695         }
3696
3697         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3698     },
3699
3700
3701     easeInStrong: function (t, b, c, d) {
3702         return c * (t /= d) * t * t * t + b;
3703     },
3704
3705
3706     easeOutStrong: function (t, b, c, d) {
3707         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3708     },
3709
3710
3711     easeBothStrong: function (t, b, c, d) {
3712         if ((t /= d / 2) < 1) {
3713             return c / 2 * t * t * t * t + b;
3714         }
3715
3716         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3717     },
3718
3719
3720
3721     elasticIn: function (t, b, c, d, a, p) {
3722         if (t == 0) {
3723             return b;
3724         }
3725         if ((t /= d) == 1) {
3726             return b + c;
3727         }
3728         if (!p) {
3729             p = d * .3;
3730         }
3731
3732         if (!a || a < Math.abs(c)) {
3733             a = c;
3734             var s = p / 4;
3735         }
3736         else {
3737             var s = p / (2 * Math.PI) * Math.asin(c / a);
3738         }
3739
3740         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3741     },
3742
3743
3744     elasticOut: function (t, b, c, d, a, p) {
3745         if (t == 0) {
3746             return b;
3747         }
3748         if ((t /= d) == 1) {
3749             return b + c;
3750         }
3751         if (!p) {
3752             p = d * .3;
3753         }
3754
3755         if (!a || a < Math.abs(c)) {
3756             a = c;
3757             var s = p / 4;
3758         }
3759         else {
3760             var s = p / (2 * Math.PI) * Math.asin(c / a);
3761         }
3762
3763         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3764     },
3765
3766
3767     elasticBoth: function (t, b, c, d, a, p) {
3768         if (t == 0) {
3769             return b;
3770         }
3771
3772         if ((t /= d / 2) == 2) {
3773             return b + c;
3774         }
3775
3776         if (!p) {
3777             p = d * (.3 * 1.5);
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         if (t < 1) {
3789             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3790                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3791         }
3792         return a * Math.pow(2, -10 * (t -= 1)) *
3793                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3794     },
3795
3796
3797
3798     backIn: function (t, b, c, d, s) {
3799         if (typeof s == 'undefined') {
3800             s = 1.70158;
3801         }
3802         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3803     },
3804
3805
3806     backOut: function (t, b, c, d, s) {
3807         if (typeof s == 'undefined') {
3808             s = 1.70158;
3809         }
3810         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3811     },
3812
3813
3814     backBoth: function (t, b, c, d, s) {
3815         if (typeof s == 'undefined') {
3816             s = 1.70158;
3817         }
3818
3819         if ((t /= d / 2 ) < 1) {
3820             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3821         }
3822         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3823     },
3824
3825
3826     bounceIn: function (t, b, c, d) {
3827         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3828     },
3829
3830
3831     bounceOut: function (t, b, c, d) {
3832         if ((t /= d) < (1 / 2.75)) {
3833             return c * (7.5625 * t * t) + b;
3834         } else if (t < (2 / 2.75)) {
3835             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3836         } else if (t < (2.5 / 2.75)) {
3837             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3838         }
3839         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3840     },
3841
3842
3843     bounceBoth: function (t, b, c, d) {
3844         if (t < d / 2) {
3845             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3846         }
3847         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3848     }
3849 };/*
3850  * Portions of this file are based on pieces of Yahoo User Interface Library
3851  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3852  * YUI licensed under the BSD License:
3853  * http://developer.yahoo.net/yui/license.txt
3854  * <script type="text/javascript">
3855  *
3856  */
3857     (function() {
3858         Roo.lib.Motion = function(el, attributes, duration, method) {
3859             if (el) {
3860                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3861             }
3862         };
3863
3864         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3865
3866
3867         var Y = Roo.lib;
3868         var superclass = Y.Motion.superclass;
3869         var proto = Y.Motion.prototype;
3870
3871         proto.toString = function() {
3872             var el = this.getEl();
3873             var id = el.id || el.tagName;
3874             return ("Motion " + id);
3875         };
3876
3877         proto.patterns.points = /^points$/i;
3878
3879         proto.setAttribute = function(attr, val, unit) {
3880             if (this.patterns.points.test(attr)) {
3881                 unit = unit || 'px';
3882                 superclass.setAttribute.call(this, 'left', val[0], unit);
3883                 superclass.setAttribute.call(this, 'top', val[1], unit);
3884             } else {
3885                 superclass.setAttribute.call(this, attr, val, unit);
3886             }
3887         };
3888
3889         proto.getAttribute = function(attr) {
3890             if (this.patterns.points.test(attr)) {
3891                 var val = [
3892                         superclass.getAttribute.call(this, 'left'),
3893                         superclass.getAttribute.call(this, 'top')
3894                         ];
3895             } else {
3896                 val = superclass.getAttribute.call(this, attr);
3897             }
3898
3899             return val;
3900         };
3901
3902         proto.doMethod = function(attr, start, end) {
3903             var val = null;
3904
3905             if (this.patterns.points.test(attr)) {
3906                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3907                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3908             } else {
3909                 val = superclass.doMethod.call(this, attr, start, end);
3910             }
3911             return val;
3912         };
3913
3914         proto.setRuntimeAttribute = function(attr) {
3915             if (this.patterns.points.test(attr)) {
3916                 var el = this.getEl();
3917                 var attributes = this.attributes;
3918                 var start;
3919                 var control = attributes['points']['control'] || [];
3920                 var end;
3921                 var i, len;
3922
3923                 if (control.length > 0 && !(control[0] instanceof Array)) {
3924                     control = [control];
3925                 } else {
3926                     var tmp = [];
3927                     for (i = 0,len = control.length; i < len; ++i) {
3928                         tmp[i] = control[i];
3929                     }
3930                     control = tmp;
3931                 }
3932
3933                 Roo.fly(el).position();
3934
3935                 if (isset(attributes['points']['from'])) {
3936                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3937                 }
3938                 else {
3939                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3940                 }
3941
3942                 start = this.getAttribute('points');
3943
3944
3945                 if (isset(attributes['points']['to'])) {
3946                     end = translateValues.call(this, attributes['points']['to'], start);
3947
3948                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3949                     for (i = 0,len = control.length; i < len; ++i) {
3950                         control[i] = translateValues.call(this, control[i], start);
3951                     }
3952
3953
3954                 } else if (isset(attributes['points']['by'])) {
3955                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3956
3957                     for (i = 0,len = control.length; i < len; ++i) {
3958                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3959                     }
3960                 }
3961
3962                 this.runtimeAttributes[attr] = [start];
3963
3964                 if (control.length > 0) {
3965                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3966                 }
3967
3968                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3969             }
3970             else {
3971                 superclass.setRuntimeAttribute.call(this, attr);
3972             }
3973         };
3974
3975         var translateValues = function(val, start) {
3976             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3978
3979             return val;
3980         };
3981
3982         var isset = function(prop) {
3983             return (typeof prop !== 'undefined');
3984         };
3985     })();
3986 /*
3987  * Portions of this file are based on pieces of Yahoo User Interface Library
3988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3989  * YUI licensed under the BSD License:
3990  * http://developer.yahoo.net/yui/license.txt
3991  * <script type="text/javascript">
3992  *
3993  */
3994     (function() {
3995         Roo.lib.Scroll = function(el, attributes, duration, method) {
3996             if (el) {
3997                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3998             }
3999         };
4000
4001         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4002
4003
4004         var Y = Roo.lib;
4005         var superclass = Y.Scroll.superclass;
4006         var proto = Y.Scroll.prototype;
4007
4008         proto.toString = function() {
4009             var el = this.getEl();
4010             var id = el.id || el.tagName;
4011             return ("Scroll " + id);
4012         };
4013
4014         proto.doMethod = function(attr, start, end) {
4015             var val = null;
4016
4017             if (attr == 'scroll') {
4018                 val = [
4019                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4020                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4021                         ];
4022
4023             } else {
4024                 val = superclass.doMethod.call(this, attr, start, end);
4025             }
4026             return val;
4027         };
4028
4029         proto.getAttribute = function(attr) {
4030             var val = null;
4031             var el = this.getEl();
4032
4033             if (attr == 'scroll') {
4034                 val = [ el.scrollLeft, el.scrollTop ];
4035             } else {
4036                 val = superclass.getAttribute.call(this, attr);
4037             }
4038
4039             return val;
4040         };
4041
4042         proto.setAttribute = function(attr, val, unit) {
4043             var el = this.getEl();
4044
4045             if (attr == 'scroll') {
4046                 el.scrollLeft = val[0];
4047                 el.scrollTop = val[1];
4048             } else {
4049                 superclass.setAttribute.call(this, attr, val, unit);
4050             }
4051         };
4052     })();
4053 /*
4054  * Based on:
4055  * Ext JS Library 1.1.1
4056  * Copyright(c) 2006-2007, Ext JS, LLC.
4057  *
4058  * Originally Released Under LGPL - original licence link has changed is not relivant.
4059  *
4060  * Fork - LGPL
4061  * <script type="text/javascript">
4062  */
4063
4064
4065 // nasty IE9 hack - what a pile of crap that is..
4066
4067  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4068     Range.prototype.createContextualFragment = function (html) {
4069         var doc = window.document;
4070         var container = doc.createElement("div");
4071         container.innerHTML = html;
4072         var frag = doc.createDocumentFragment(), n;
4073         while ((n = container.firstChild)) {
4074             frag.appendChild(n);
4075         }
4076         return frag;
4077     };
4078 }
4079
4080 /**
4081  * @class Roo.DomHelper
4082  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4083  * 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>.
4084  * @singleton
4085  */
4086 Roo.DomHelper = function(){
4087     var tempTableEl = null;
4088     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4089     var tableRe = /^table|tbody|tr|td$/i;
4090     var xmlns = {};
4091     // build as innerHTML where available
4092     /** @ignore */
4093     var createHtml = function(o){
4094         if(typeof o == 'string'){
4095             return o;
4096         }
4097         var b = "";
4098         if(!o.tag){
4099             o.tag = "div";
4100         }
4101         b += "<" + o.tag;
4102         for(var attr in o){
4103             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4104             if(attr == "style"){
4105                 var s = o["style"];
4106                 if(typeof s == "function"){
4107                     s = s.call();
4108                 }
4109                 if(typeof s == "string"){
4110                     b += ' style="' + s + '"';
4111                 }else if(typeof s == "object"){
4112                     b += ' style="';
4113                     for(var key in s){
4114                         if(typeof s[key] != "function"){
4115                             b += key + ":" + s[key] + ";";
4116                         }
4117                     }
4118                     b += '"';
4119                 }
4120             }else{
4121                 if(attr == "cls"){
4122                     b += ' class="' + o["cls"] + '"';
4123                 }else if(attr == "htmlFor"){
4124                     b += ' for="' + o["htmlFor"] + '"';
4125                 }else{
4126                     b += " " + attr + '="' + o[attr] + '"';
4127                 }
4128             }
4129         }
4130         if(emptyTags.test(o.tag)){
4131             b += "/>";
4132         }else{
4133             b += ">";
4134             var cn = o.children || o.cn;
4135             if(cn){
4136                 //http://bugs.kde.org/show_bug.cgi?id=71506
4137                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4138                     for(var i = 0, len = cn.length; i < len; i++) {
4139                         b += createHtml(cn[i], b);
4140                     }
4141                 }else{
4142                     b += createHtml(cn, b);
4143                 }
4144             }
4145             if(o.html){
4146                 b += o.html;
4147             }
4148             b += "</" + o.tag + ">";
4149         }
4150         return b;
4151     };
4152
4153     // build as dom
4154     /** @ignore */
4155     var createDom = function(o, parentNode){
4156          
4157         // defininition craeted..
4158         var ns = false;
4159         if (o.ns && o.ns != 'html') {
4160                
4161             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4162                 xmlns[o.ns] = o.xmlns;
4163                 ns = o.xmlns;
4164             }
4165             if (typeof(xmlns[o.ns]) == 'undefined') {
4166                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4167             }
4168             ns = xmlns[o.ns];
4169         }
4170         
4171         
4172         if (typeof(o) == 'string') {
4173             return parentNode.appendChild(document.createTextNode(o));
4174         }
4175         o.tag = o.tag || div;
4176         if (o.ns && Roo.isIE) {
4177             ns = false;
4178             o.tag = o.ns + ':' + o.tag;
4179             
4180         }
4181         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4182         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4183         for(var attr in o){
4184             
4185             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4186                     attr == "style" || typeof o[attr] == "function") continue;
4187                     
4188             if(attr=="cls" && Roo.isIE){
4189                 el.className = o["cls"];
4190             }else{
4191                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4192                 else el[attr] = o[attr];
4193             }
4194         }
4195         Roo.DomHelper.applyStyles(el, o.style);
4196         var cn = o.children || o.cn;
4197         if(cn){
4198             //http://bugs.kde.org/show_bug.cgi?id=71506
4199              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4200                 for(var i = 0, len = cn.length; i < len; i++) {
4201                     createDom(cn[i], el);
4202                 }
4203             }else{
4204                 createDom(cn, el);
4205             }
4206         }
4207         if(o.html){
4208             el.innerHTML = o.html;
4209         }
4210         if(parentNode){
4211            parentNode.appendChild(el);
4212         }
4213         return el;
4214     };
4215
4216     var ieTable = function(depth, s, h, e){
4217         tempTableEl.innerHTML = [s, h, e].join('');
4218         var i = -1, el = tempTableEl;
4219         while(++i < depth){
4220             el = el.firstChild;
4221         }
4222         return el;
4223     };
4224
4225     // kill repeat to save bytes
4226     var ts = '<table>',
4227         te = '</table>',
4228         tbs = ts+'<tbody>',
4229         tbe = '</tbody>'+te,
4230         trs = tbs + '<tr>',
4231         tre = '</tr>'+tbe;
4232
4233     /**
4234      * @ignore
4235      * Nasty code for IE's broken table implementation
4236      */
4237     var insertIntoTable = function(tag, where, el, html){
4238         if(!tempTableEl){
4239             tempTableEl = document.createElement('div');
4240         }
4241         var node;
4242         var before = null;
4243         if(tag == 'td'){
4244             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4245                 return;
4246             }
4247             if(where == 'beforebegin'){
4248                 before = el;
4249                 el = el.parentNode;
4250             } else{
4251                 before = el.nextSibling;
4252                 el = el.parentNode;
4253             }
4254             node = ieTable(4, trs, html, tre);
4255         }
4256         else if(tag == 'tr'){
4257             if(where == 'beforebegin'){
4258                 before = el;
4259                 el = el.parentNode;
4260                 node = ieTable(3, tbs, html, tbe);
4261             } else if(where == 'afterend'){
4262                 before = el.nextSibling;
4263                 el = el.parentNode;
4264                 node = ieTable(3, tbs, html, tbe);
4265             } else{ // INTO a TR
4266                 if(where == 'afterbegin'){
4267                     before = el.firstChild;
4268                 }
4269                 node = ieTable(4, trs, html, tre);
4270             }
4271         } else if(tag == 'tbody'){
4272             if(where == 'beforebegin'){
4273                 before = el;
4274                 el = el.parentNode;
4275                 node = ieTable(2, ts, html, te);
4276             } else if(where == 'afterend'){
4277                 before = el.nextSibling;
4278                 el = el.parentNode;
4279                 node = ieTable(2, ts, html, te);
4280             } else{
4281                 if(where == 'afterbegin'){
4282                     before = el.firstChild;
4283                 }
4284                 node = ieTable(3, tbs, html, tbe);
4285             }
4286         } else{ // TABLE
4287             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4288                 return;
4289             }
4290             if(where == 'afterbegin'){
4291                 before = el.firstChild;
4292             }
4293             node = ieTable(2, ts, html, te);
4294         }
4295         el.insertBefore(node, before);
4296         return node;
4297     };
4298
4299     return {
4300     /** True to force the use of DOM instead of html fragments @type Boolean */
4301     useDom : false,
4302
4303     /**
4304      * Returns the markup for the passed Element(s) config
4305      * @param {Object} o The Dom object spec (and children)
4306      * @return {String}
4307      */
4308     markup : function(o){
4309         return createHtml(o);
4310     },
4311
4312     /**
4313      * Applies a style specification to an element
4314      * @param {String/HTMLElement} el The element to apply styles to
4315      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4316      * a function which returns such a specification.
4317      */
4318     applyStyles : function(el, styles){
4319         if(styles){
4320            el = Roo.fly(el);
4321            if(typeof styles == "string"){
4322                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4323                var matches;
4324                while ((matches = re.exec(styles)) != null){
4325                    el.setStyle(matches[1], matches[2]);
4326                }
4327            }else if (typeof styles == "object"){
4328                for (var style in styles){
4329                   el.setStyle(style, styles[style]);
4330                }
4331            }else if (typeof styles == "function"){
4332                 Roo.DomHelper.applyStyles(el, styles.call());
4333            }
4334         }
4335     },
4336
4337     /**
4338      * Inserts an HTML fragment into the Dom
4339      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4340      * @param {HTMLElement} el The context element
4341      * @param {String} html The HTML fragmenet
4342      * @return {HTMLElement} The new node
4343      */
4344     insertHtml : function(where, el, html){
4345         where = where.toLowerCase();
4346         if(el.insertAdjacentHTML){
4347             if(tableRe.test(el.tagName)){
4348                 var rs;
4349                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4350                     return rs;
4351                 }
4352             }
4353             switch(where){
4354                 case "beforebegin":
4355                     el.insertAdjacentHTML('BeforeBegin', html);
4356                     return el.previousSibling;
4357                 case "afterbegin":
4358                     el.insertAdjacentHTML('AfterBegin', html);
4359                     return el.firstChild;
4360                 case "beforeend":
4361                     el.insertAdjacentHTML('BeforeEnd', html);
4362                     return el.lastChild;
4363                 case "afterend":
4364                     el.insertAdjacentHTML('AfterEnd', html);
4365                     return el.nextSibling;
4366             }
4367             throw 'Illegal insertion point -> "' + where + '"';
4368         }
4369         var range = el.ownerDocument.createRange();
4370         var frag;
4371         switch(where){
4372              case "beforebegin":
4373                 range.setStartBefore(el);
4374                 frag = range.createContextualFragment(html);
4375                 el.parentNode.insertBefore(frag, el);
4376                 return el.previousSibling;
4377              case "afterbegin":
4378                 if(el.firstChild){
4379                     range.setStartBefore(el.firstChild);
4380                     frag = range.createContextualFragment(html);
4381                     el.insertBefore(frag, el.firstChild);
4382                     return el.firstChild;
4383                 }else{
4384                     el.innerHTML = html;
4385                     return el.firstChild;
4386                 }
4387             case "beforeend":
4388                 if(el.lastChild){
4389                     range.setStartAfter(el.lastChild);
4390                     frag = range.createContextualFragment(html);
4391                     el.appendChild(frag);
4392                     return el.lastChild;
4393                 }else{
4394                     el.innerHTML = html;
4395                     return el.lastChild;
4396                 }
4397             case "afterend":
4398                 range.setStartAfter(el);
4399                 frag = range.createContextualFragment(html);
4400                 el.parentNode.insertBefore(frag, el.nextSibling);
4401                 return el.nextSibling;
4402             }
4403             throw 'Illegal insertion point -> "' + where + '"';
4404     },
4405
4406     /**
4407      * Creates new Dom element(s) and inserts them before el
4408      * @param {String/HTMLElement/Element} el The context element
4409      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4410      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4411      * @return {HTMLElement/Roo.Element} The new node
4412      */
4413     insertBefore : function(el, o, returnElement){
4414         return this.doInsert(el, o, returnElement, "beforeBegin");
4415     },
4416
4417     /**
4418      * Creates new Dom element(s) and inserts them after el
4419      * @param {String/HTMLElement/Element} el The context element
4420      * @param {Object} o The Dom object spec (and children)
4421      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4422      * @return {HTMLElement/Roo.Element} The new node
4423      */
4424     insertAfter : function(el, o, returnElement){
4425         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4426     },
4427
4428     /**
4429      * Creates new Dom element(s) and inserts them as the first child of el
4430      * @param {String/HTMLElement/Element} el The context element
4431      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4432      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4433      * @return {HTMLElement/Roo.Element} The new node
4434      */
4435     insertFirst : function(el, o, returnElement){
4436         return this.doInsert(el, o, returnElement, "afterBegin");
4437     },
4438
4439     // private
4440     doInsert : function(el, o, returnElement, pos, sibling){
4441         el = Roo.getDom(el);
4442         var newNode;
4443         if(this.useDom || o.ns){
4444             newNode = createDom(o, null);
4445             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4446         }else{
4447             var html = createHtml(o);
4448             newNode = this.insertHtml(pos, el, html);
4449         }
4450         return returnElement ? Roo.get(newNode, true) : newNode;
4451     },
4452
4453     /**
4454      * Creates new Dom element(s) and appends them to el
4455      * @param {String/HTMLElement/Element} el The context element
4456      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4457      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4458      * @return {HTMLElement/Roo.Element} The new node
4459      */
4460     append : function(el, o, returnElement){
4461         el = Roo.getDom(el);
4462         var newNode;
4463         if(this.useDom || o.ns){
4464             newNode = createDom(o, null);
4465             el.appendChild(newNode);
4466         }else{
4467             var html = createHtml(o);
4468             newNode = this.insertHtml("beforeEnd", el, html);
4469         }
4470         return returnElement ? Roo.get(newNode, true) : newNode;
4471     },
4472
4473     /**
4474      * Creates new Dom element(s) and overwrites the contents of el with them
4475      * @param {String/HTMLElement/Element} el The context element
4476      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4477      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4478      * @return {HTMLElement/Roo.Element} The new node
4479      */
4480     overwrite : function(el, o, returnElement){
4481         el = Roo.getDom(el);
4482         if (o.ns) {
4483           
4484             while (el.childNodes.length) {
4485                 el.removeChild(el.firstChild);
4486             }
4487             createDom(o, el);
4488         } else {
4489             el.innerHTML = createHtml(o);   
4490         }
4491         
4492         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4493     },
4494
4495     /**
4496      * Creates a new Roo.DomHelper.Template from the Dom object spec
4497      * @param {Object} o The Dom object spec (and children)
4498      * @return {Roo.DomHelper.Template} The new template
4499      */
4500     createTemplate : function(o){
4501         var html = createHtml(o);
4502         return new Roo.Template(html);
4503     }
4504     };
4505 }();
4506 /*
4507  * Based on:
4508  * Ext JS Library 1.1.1
4509  * Copyright(c) 2006-2007, Ext JS, LLC.
4510  *
4511  * Originally Released Under LGPL - original licence link has changed is not relivant.
4512  *
4513  * Fork - LGPL
4514  * <script type="text/javascript">
4515  */
4516  
4517 /**
4518 * @class Roo.Template
4519 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4520 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4521 * Usage:
4522 <pre><code>
4523 var t = new Roo.Template({
4524     html :  '&lt;div name="{id}"&gt;' + 
4525         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4526         '&lt;/div&gt;',
4527     myformat: function (value, allValues) {
4528         return 'XX' + value;
4529     }
4530 });
4531 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4532 </code></pre>
4533 * 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>. 
4534 * @constructor
4535 * @param {Object} cfg - Configuration object.
4536 */
4537 Roo.Template = function(cfg){
4538     // BC!
4539     if(cfg instanceof Array){
4540         cfg = cfg.join("");
4541     }else if(arguments.length > 1){
4542         cfg = Array.prototype.join.call(arguments, "");
4543     }
4544     
4545     
4546     if (typeof(cfg) == 'object') {
4547         Roo.apply(this,cfg)
4548     } else {
4549         // bc
4550         this.html = cfg;
4551     }
4552     
4553     
4554 };
4555 Roo.Template.prototype = {
4556     
4557     /**
4558      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4559      */
4560     html : '',
4561     /**
4562      * Returns an HTML fragment of this template with the specified values applied.
4563      * @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'})
4564      * @return {String} The HTML fragment
4565      */
4566     applyTemplate : function(values){
4567         try {
4568             
4569             if(this.compiled){
4570                 return this.compiled(values);
4571             }
4572             var useF = this.disableFormats !== true;
4573             var fm = Roo.util.Format, tpl = this;
4574             var fn = function(m, name, format, args){
4575                 if(format && useF){
4576                     if(format.substr(0, 5) == "this."){
4577                         return tpl.call(format.substr(5), values[name], values);
4578                     }else{
4579                         if(args){
4580                             // quoted values are required for strings in compiled templates, 
4581                             // but for non compiled we need to strip them
4582                             // quoted reversed for jsmin
4583                             var re = /^\s*['"](.*)["']\s*$/;
4584                             args = args.split(',');
4585                             for(var i = 0, len = args.length; i < len; i++){
4586                                 args[i] = args[i].replace(re, "$1");
4587                             }
4588                             args = [values[name]].concat(args);
4589                         }else{
4590                             args = [values[name]];
4591                         }
4592                         return fm[format].apply(fm, args);
4593                     }
4594                 }else{
4595                     return values[name] !== undefined ? values[name] : "";
4596                 }
4597             };
4598             return this.html.replace(this.re, fn);
4599         } catch (e) {
4600             Roo.log(e);
4601             throw e;
4602         }
4603          
4604     },
4605     
4606     /**
4607      * Sets the HTML used as the template and optionally compiles it.
4608      * @param {String} html
4609      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4610      * @return {Roo.Template} this
4611      */
4612     set : function(html, compile){
4613         this.html = html;
4614         this.compiled = null;
4615         if(compile){
4616             this.compile();
4617         }
4618         return this;
4619     },
4620     
4621     /**
4622      * True to disable format functions (defaults to false)
4623      * @type Boolean
4624      */
4625     disableFormats : false,
4626     
4627     /**
4628     * The regular expression used to match template variables 
4629     * @type RegExp
4630     * @property 
4631     */
4632     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4633     
4634     /**
4635      * Compiles the template into an internal function, eliminating the RegEx overhead.
4636      * @return {Roo.Template} this
4637      */
4638     compile : function(){
4639         var fm = Roo.util.Format;
4640         var useF = this.disableFormats !== true;
4641         var sep = Roo.isGecko ? "+" : ",";
4642         var fn = function(m, name, format, args){
4643             if(format && useF){
4644                 args = args ? ',' + args : "";
4645                 if(format.substr(0, 5) != "this."){
4646                     format = "fm." + format + '(';
4647                 }else{
4648                     format = 'this.call("'+ format.substr(5) + '", ';
4649                     args = ", values";
4650                 }
4651             }else{
4652                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4653             }
4654             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4655         };
4656         var body;
4657         // branched to use + in gecko and [].join() in others
4658         if(Roo.isGecko){
4659             body = "this.compiled = function(values){ return '" +
4660                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4661                     "';};";
4662         }else{
4663             body = ["this.compiled = function(values){ return ['"];
4664             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4665             body.push("'].join('');};");
4666             body = body.join('');
4667         }
4668         /**
4669          * eval:var:values
4670          * eval:var:fm
4671          */
4672         eval(body);
4673         return this;
4674     },
4675     
4676     // private function used to call members
4677     call : function(fnName, value, allValues){
4678         return this[fnName](value, allValues);
4679     },
4680     
4681     /**
4682      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4683      * @param {String/HTMLElement/Roo.Element} el The context element
4684      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4685      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4686      * @return {HTMLElement/Roo.Element} The new node or Element
4687      */
4688     insertFirst: function(el, values, returnElement){
4689         return this.doInsert('afterBegin', el, values, returnElement);
4690     },
4691
4692     /**
4693      * Applies the supplied values to the template and inserts the new node(s) before el.
4694      * @param {String/HTMLElement/Roo.Element} el The context element
4695      * @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'})
4696      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4697      * @return {HTMLElement/Roo.Element} The new node or Element
4698      */
4699     insertBefore: function(el, values, returnElement){
4700         return this.doInsert('beforeBegin', el, values, returnElement);
4701     },
4702
4703     /**
4704      * Applies the supplied values to the template and inserts the new node(s) after el.
4705      * @param {String/HTMLElement/Roo.Element} el The context element
4706      * @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'})
4707      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4708      * @return {HTMLElement/Roo.Element} The new node or Element
4709      */
4710     insertAfter : function(el, values, returnElement){
4711         return this.doInsert('afterEnd', el, values, returnElement);
4712     },
4713     
4714     /**
4715      * Applies the supplied values to the template and appends the new node(s) to el.
4716      * @param {String/HTMLElement/Roo.Element} el The context element
4717      * @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'})
4718      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4719      * @return {HTMLElement/Roo.Element} The new node or Element
4720      */
4721     append : function(el, values, returnElement){
4722         return this.doInsert('beforeEnd', el, values, returnElement);
4723     },
4724
4725     doInsert : function(where, el, values, returnEl){
4726         el = Roo.getDom(el);
4727         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4728         return returnEl ? Roo.get(newNode, true) : newNode;
4729     },
4730
4731     /**
4732      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4733      * @param {String/HTMLElement/Roo.Element} el The context element
4734      * @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'})
4735      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4736      * @return {HTMLElement/Roo.Element} The new node or Element
4737      */
4738     overwrite : function(el, values, returnElement){
4739         el = Roo.getDom(el);
4740         el.innerHTML = this.applyTemplate(values);
4741         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4742     }
4743 };
4744 /**
4745  * Alias for {@link #applyTemplate}
4746  * @method
4747  */
4748 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4749
4750 // backwards compat
4751 Roo.DomHelper.Template = Roo.Template;
4752
4753 /**
4754  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4755  * @param {String/HTMLElement} el A DOM element or its id
4756  * @returns {Roo.Template} The created template
4757  * @static
4758  */
4759 Roo.Template.from = function(el){
4760     el = Roo.getDom(el);
4761     return new Roo.Template(el.value || el.innerHTML);
4762 };/*
4763  * Based on:
4764  * Ext JS Library 1.1.1
4765  * Copyright(c) 2006-2007, Ext JS, LLC.
4766  *
4767  * Originally Released Under LGPL - original licence link has changed is not relivant.
4768  *
4769  * Fork - LGPL
4770  * <script type="text/javascript">
4771  */
4772  
4773
4774 /*
4775  * This is code is also distributed under MIT license for use
4776  * with jQuery and prototype JavaScript libraries.
4777  */
4778 /**
4779  * @class Roo.DomQuery
4780 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).
4781 <p>
4782 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>
4783
4784 <p>
4785 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.
4786 </p>
4787 <h4>Element Selectors:</h4>
4788 <ul class="list">
4789     <li> <b>*</b> any element</li>
4790     <li> <b>E</b> an element with the tag E</li>
4791     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4792     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4793     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4794     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4795 </ul>
4796 <h4>Attribute Selectors:</h4>
4797 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4798 <ul class="list">
4799     <li> <b>E[foo]</b> has an attribute "foo"</li>
4800     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4801     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4802     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4803     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4804     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4805     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4806 </ul>
4807 <h4>Pseudo Classes:</h4>
4808 <ul class="list">
4809     <li> <b>E:first-child</b> E is the first child of its parent</li>
4810     <li> <b>E:last-child</b> E is the last child of its parent</li>
4811     <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>
4812     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4813     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4814     <li> <b>E:only-child</b> E is the only child of its parent</li>
4815     <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>
4816     <li> <b>E:first</b> the first E in the resultset</li>
4817     <li> <b>E:last</b> the last E in the resultset</li>
4818     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4819     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4820     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4821     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4822     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4823     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4824     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4825     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4826     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4827 </ul>
4828 <h4>CSS Value Selectors:</h4>
4829 <ul class="list">
4830     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4831     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4832     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4833     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4834     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4835     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4836 </ul>
4837  * @singleton
4838  */
4839 Roo.DomQuery = function(){
4840     var cache = {}, simpleCache = {}, valueCache = {};
4841     var nonSpace = /\S/;
4842     var trimRe = /^\s+|\s+$/g;
4843     var tplRe = /\{(\d+)\}/g;
4844     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4845     var tagTokenRe = /^(#)?([\w-\*]+)/;
4846     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4847
4848     function child(p, index){
4849         var i = 0;
4850         var n = p.firstChild;
4851         while(n){
4852             if(n.nodeType == 1){
4853                if(++i == index){
4854                    return n;
4855                }
4856             }
4857             n = n.nextSibling;
4858         }
4859         return null;
4860     };
4861
4862     function next(n){
4863         while((n = n.nextSibling) && n.nodeType != 1);
4864         return n;
4865     };
4866
4867     function prev(n){
4868         while((n = n.previousSibling) && n.nodeType != 1);
4869         return n;
4870     };
4871
4872     function children(d){
4873         var n = d.firstChild, ni = -1;
4874             while(n){
4875                 var nx = n.nextSibling;
4876                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4877                     d.removeChild(n);
4878                 }else{
4879                     n.nodeIndex = ++ni;
4880                 }
4881                 n = nx;
4882             }
4883             return this;
4884         };
4885
4886     function byClassName(c, a, v){
4887         if(!v){
4888             return c;
4889         }
4890         var r = [], ri = -1, cn;
4891         for(var i = 0, ci; ci = c[i]; i++){
4892             if((' '+ci.className+' ').indexOf(v) != -1){
4893                 r[++ri] = ci;
4894             }
4895         }
4896         return r;
4897     };
4898
4899     function attrValue(n, attr){
4900         if(!n.tagName && typeof n.length != "undefined"){
4901             n = n[0];
4902         }
4903         if(!n){
4904             return null;
4905         }
4906         if(attr == "for"){
4907             return n.htmlFor;
4908         }
4909         if(attr == "class" || attr == "className"){
4910             return n.className;
4911         }
4912         return n.getAttribute(attr) || n[attr];
4913
4914     };
4915
4916     function getNodes(ns, mode, tagName){
4917         var result = [], ri = -1, cs;
4918         if(!ns){
4919             return result;
4920         }
4921         tagName = tagName || "*";
4922         if(typeof ns.getElementsByTagName != "undefined"){
4923             ns = [ns];
4924         }
4925         if(!mode){
4926             for(var i = 0, ni; ni = ns[i]; i++){
4927                 cs = ni.getElementsByTagName(tagName);
4928                 for(var j = 0, ci; ci = cs[j]; j++){
4929                     result[++ri] = ci;
4930                 }
4931             }
4932         }else if(mode == "/" || mode == ">"){
4933             var utag = tagName.toUpperCase();
4934             for(var i = 0, ni, cn; ni = ns[i]; i++){
4935                 cn = ni.children || ni.childNodes;
4936                 for(var j = 0, cj; cj = cn[j]; j++){
4937                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4938                         result[++ri] = cj;
4939                     }
4940                 }
4941             }
4942         }else if(mode == "+"){
4943             var utag = tagName.toUpperCase();
4944             for(var i = 0, n; n = ns[i]; i++){
4945                 while((n = n.nextSibling) && n.nodeType != 1);
4946                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4947                     result[++ri] = n;
4948                 }
4949             }
4950         }else if(mode == "~"){
4951             for(var i = 0, n; n = ns[i]; i++){
4952                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4953                 if(n){
4954                     result[++ri] = n;
4955                 }
4956             }
4957         }
4958         return result;
4959     };
4960
4961     function concat(a, b){
4962         if(b.slice){
4963             return a.concat(b);
4964         }
4965         for(var i = 0, l = b.length; i < l; i++){
4966             a[a.length] = b[i];
4967         }
4968         return a;
4969     }
4970
4971     function byTag(cs, tagName){
4972         if(cs.tagName || cs == document){
4973             cs = [cs];
4974         }
4975         if(!tagName){
4976             return cs;
4977         }
4978         var r = [], ri = -1;
4979         tagName = tagName.toLowerCase();
4980         for(var i = 0, ci; ci = cs[i]; i++){
4981             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4982                 r[++ri] = ci;
4983             }
4984         }
4985         return r;
4986     };
4987
4988     function byId(cs, attr, id){
4989         if(cs.tagName || cs == document){
4990             cs = [cs];
4991         }
4992         if(!id){
4993             return cs;
4994         }
4995         var r = [], ri = -1;
4996         for(var i = 0,ci; ci = cs[i]; i++){
4997             if(ci && ci.id == id){
4998                 r[++ri] = ci;
4999                 return r;
5000             }
5001         }
5002         return r;
5003     };
5004
5005     function byAttribute(cs, attr, value, op, custom){
5006         var r = [], ri = -1, st = custom=="{";
5007         var f = Roo.DomQuery.operators[op];
5008         for(var i = 0, ci; ci = cs[i]; i++){
5009             var a;
5010             if(st){
5011                 a = Roo.DomQuery.getStyle(ci, attr);
5012             }
5013             else if(attr == "class" || attr == "className"){
5014                 a = ci.className;
5015             }else if(attr == "for"){
5016                 a = ci.htmlFor;
5017             }else if(attr == "href"){
5018                 a = ci.getAttribute("href", 2);
5019             }else{
5020                 a = ci.getAttribute(attr);
5021             }
5022             if((f && f(a, value)) || (!f && a)){
5023                 r[++ri] = ci;
5024             }
5025         }
5026         return r;
5027     };
5028
5029     function byPseudo(cs, name, value){
5030         return Roo.DomQuery.pseudos[name](cs, value);
5031     };
5032
5033     // This is for IE MSXML which does not support expandos.
5034     // IE runs the same speed using setAttribute, however FF slows way down
5035     // and Safari completely fails so they need to continue to use expandos.
5036     var isIE = window.ActiveXObject ? true : false;
5037
5038     // this eval is stop the compressor from
5039     // renaming the variable to something shorter
5040     
5041     /** eval:var:batch */
5042     var batch = 30803; 
5043
5044     var key = 30803;
5045
5046     function nodupIEXml(cs){
5047         var d = ++key;
5048         cs[0].setAttribute("_nodup", d);
5049         var r = [cs[0]];
5050         for(var i = 1, len = cs.length; i < len; i++){
5051             var c = cs[i];
5052             if(!c.getAttribute("_nodup") != d){
5053                 c.setAttribute("_nodup", d);
5054                 r[r.length] = c;
5055             }
5056         }
5057         for(var i = 0, len = cs.length; i < len; i++){
5058             cs[i].removeAttribute("_nodup");
5059         }
5060         return r;
5061     }
5062
5063     function nodup(cs){
5064         if(!cs){
5065             return [];
5066         }
5067         var len = cs.length, c, i, r = cs, cj, ri = -1;
5068         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5069             return cs;
5070         }
5071         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5072             return nodupIEXml(cs);
5073         }
5074         var d = ++key;
5075         cs[0]._nodup = d;
5076         for(i = 1; c = cs[i]; i++){
5077             if(c._nodup != d){
5078                 c._nodup = d;
5079             }else{
5080                 r = [];
5081                 for(var j = 0; j < i; j++){
5082                     r[++ri] = cs[j];
5083                 }
5084                 for(j = i+1; cj = cs[j]; j++){
5085                     if(cj._nodup != d){
5086                         cj._nodup = d;
5087                         r[++ri] = cj;
5088                     }
5089                 }
5090                 return r;
5091             }
5092         }
5093         return r;
5094     }
5095
5096     function quickDiffIEXml(c1, c2){
5097         var d = ++key;
5098         for(var i = 0, len = c1.length; i < len; i++){
5099             c1[i].setAttribute("_qdiff", d);
5100         }
5101         var r = [];
5102         for(var i = 0, len = c2.length; i < len; i++){
5103             if(c2[i].getAttribute("_qdiff") != d){
5104                 r[r.length] = c2[i];
5105             }
5106         }
5107         for(var i = 0, len = c1.length; i < len; i++){
5108            c1[i].removeAttribute("_qdiff");
5109         }
5110         return r;
5111     }
5112
5113     function quickDiff(c1, c2){
5114         var len1 = c1.length;
5115         if(!len1){
5116             return c2;
5117         }
5118         if(isIE && c1[0].selectSingleNode){
5119             return quickDiffIEXml(c1, c2);
5120         }
5121         var d = ++key;
5122         for(var i = 0; i < len1; i++){
5123             c1[i]._qdiff = d;
5124         }
5125         var r = [];
5126         for(var i = 0, len = c2.length; i < len; i++){
5127             if(c2[i]._qdiff != d){
5128                 r[r.length] = c2[i];
5129             }
5130         }
5131         return r;
5132     }
5133
5134     function quickId(ns, mode, root, id){
5135         if(ns == root){
5136            var d = root.ownerDocument || root;
5137            return d.getElementById(id);
5138         }
5139         ns = getNodes(ns, mode, "*");
5140         return byId(ns, null, id);
5141     }
5142
5143     return {
5144         getStyle : function(el, name){
5145             return Roo.fly(el).getStyle(name);
5146         },
5147         /**
5148          * Compiles a selector/xpath query into a reusable function. The returned function
5149          * takes one parameter "root" (optional), which is the context node from where the query should start.
5150          * @param {String} selector The selector/xpath query
5151          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5152          * @return {Function}
5153          */
5154         compile : function(path, type){
5155             type = type || "select";
5156             
5157             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5158             var q = path, mode, lq;
5159             var tk = Roo.DomQuery.matchers;
5160             var tklen = tk.length;
5161             var mm;
5162
5163             // accept leading mode switch
5164             var lmode = q.match(modeRe);
5165             if(lmode && lmode[1]){
5166                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5167                 q = q.replace(lmode[1], "");
5168             }
5169             // strip leading slashes
5170             while(path.substr(0, 1)=="/"){
5171                 path = path.substr(1);
5172             }
5173
5174             while(q && lq != q){
5175                 lq = q;
5176                 var tm = q.match(tagTokenRe);
5177                 if(type == "select"){
5178                     if(tm){
5179                         if(tm[1] == "#"){
5180                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5181                         }else{
5182                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5183                         }
5184                         q = q.replace(tm[0], "");
5185                     }else if(q.substr(0, 1) != '@'){
5186                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5187                     }
5188                 }else{
5189                     if(tm){
5190                         if(tm[1] == "#"){
5191                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5192                         }else{
5193                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5194                         }
5195                         q = q.replace(tm[0], "");
5196                     }
5197                 }
5198                 while(!(mm = q.match(modeRe))){
5199                     var matched = false;
5200                     for(var j = 0; j < tklen; j++){
5201                         var t = tk[j];
5202                         var m = q.match(t.re);
5203                         if(m){
5204                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5205                                                     return m[i];
5206                                                 });
5207                             q = q.replace(m[0], "");
5208                             matched = true;
5209                             break;
5210                         }
5211                     }
5212                     // prevent infinite loop on bad selector
5213                     if(!matched){
5214                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5215                     }
5216                 }
5217                 if(mm[1]){
5218                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5219                     q = q.replace(mm[1], "");
5220                 }
5221             }
5222             fn[fn.length] = "return nodup(n);\n}";
5223             
5224              /** 
5225               * list of variables that need from compression as they are used by eval.
5226              *  eval:var:batch 
5227              *  eval:var:nodup
5228              *  eval:var:byTag
5229              *  eval:var:ById
5230              *  eval:var:getNodes
5231              *  eval:var:quickId
5232              *  eval:var:mode
5233              *  eval:var:root
5234              *  eval:var:n
5235              *  eval:var:byClassName
5236              *  eval:var:byPseudo
5237              *  eval:var:byAttribute
5238              *  eval:var:attrValue
5239              * 
5240              **/ 
5241             eval(fn.join(""));
5242             return f;
5243         },
5244
5245         /**
5246          * Selects a group of elements.
5247          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5248          * @param {Node} root (optional) The start of the query (defaults to document).
5249          * @return {Array}
5250          */
5251         select : function(path, root, type){
5252             if(!root || root == document){
5253                 root = document;
5254             }
5255             if(typeof root == "string"){
5256                 root = document.getElementById(root);
5257             }
5258             var paths = path.split(",");
5259             var results = [];
5260             for(var i = 0, len = paths.length; i < len; i++){
5261                 var p = paths[i].replace(trimRe, "");
5262                 if(!cache[p]){
5263                     cache[p] = Roo.DomQuery.compile(p);
5264                     if(!cache[p]){
5265                         throw p + " is not a valid selector";
5266                     }
5267                 }
5268                 var result = cache[p](root);
5269                 if(result && result != document){
5270                     results = results.concat(result);
5271                 }
5272             }
5273             if(paths.length > 1){
5274                 return nodup(results);
5275             }
5276             return results;
5277         },
5278
5279         /**
5280          * Selects a single element.
5281          * @param {String} selector The selector/xpath query
5282          * @param {Node} root (optional) The start of the query (defaults to document).
5283          * @return {Element}
5284          */
5285         selectNode : function(path, root){
5286             return Roo.DomQuery.select(path, root)[0];
5287         },
5288
5289         /**
5290          * Selects the value of a node, optionally replacing null with the defaultValue.
5291          * @param {String} selector The selector/xpath query
5292          * @param {Node} root (optional) The start of the query (defaults to document).
5293          * @param {String} defaultValue
5294          */
5295         selectValue : function(path, root, defaultValue){
5296             path = path.replace(trimRe, "");
5297             if(!valueCache[path]){
5298                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5299             }
5300             var n = valueCache[path](root);
5301             n = n[0] ? n[0] : n;
5302             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5303             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5304         },
5305
5306         /**
5307          * Selects the value of a node, parsing integers and floats.
5308          * @param {String} selector The selector/xpath query
5309          * @param {Node} root (optional) The start of the query (defaults to document).
5310          * @param {Number} defaultValue
5311          * @return {Number}
5312          */
5313         selectNumber : function(path, root, defaultValue){
5314             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5315             return parseFloat(v);
5316         },
5317
5318         /**
5319          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5320          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5321          * @param {String} selector The simple selector to test
5322          * @return {Boolean}
5323          */
5324         is : function(el, ss){
5325             if(typeof el == "string"){
5326                 el = document.getElementById(el);
5327             }
5328             var isArray = (el instanceof Array);
5329             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5330             return isArray ? (result.length == el.length) : (result.length > 0);
5331         },
5332
5333         /**
5334          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5335          * @param {Array} el An array of elements to filter
5336          * @param {String} selector The simple selector to test
5337          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5338          * the selector instead of the ones that match
5339          * @return {Array}
5340          */
5341         filter : function(els, ss, nonMatches){
5342             ss = ss.replace(trimRe, "");
5343             if(!simpleCache[ss]){
5344                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5345             }
5346             var result = simpleCache[ss](els);
5347             return nonMatches ? quickDiff(result, els) : result;
5348         },
5349
5350         /**
5351          * Collection of matching regular expressions and code snippets.
5352          */
5353         matchers : [{
5354                 re: /^\.([\w-]+)/,
5355                 select: 'n = byClassName(n, null, " {1} ");'
5356             }, {
5357                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5358                 select: 'n = byPseudo(n, "{1}", "{2}");'
5359             },{
5360                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5361                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5362             }, {
5363                 re: /^#([\w-]+)/,
5364                 select: 'n = byId(n, null, "{1}");'
5365             },{
5366                 re: /^@([\w-]+)/,
5367                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5368             }
5369         ],
5370
5371         /**
5372          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5373          * 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;.
5374          */
5375         operators : {
5376             "=" : function(a, v){
5377                 return a == v;
5378             },
5379             "!=" : function(a, v){
5380                 return a != v;
5381             },
5382             "^=" : function(a, v){
5383                 return a && a.substr(0, v.length) == v;
5384             },
5385             "$=" : function(a, v){
5386                 return a && a.substr(a.length-v.length) == v;
5387             },
5388             "*=" : function(a, v){
5389                 return a && a.indexOf(v) !== -1;
5390             },
5391             "%=" : function(a, v){
5392                 return (a % v) == 0;
5393             },
5394             "|=" : function(a, v){
5395                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5396             },
5397             "~=" : function(a, v){
5398                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5399             }
5400         },
5401
5402         /**
5403          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5404          * and the argument (if any) supplied in the selector.
5405          */
5406         pseudos : {
5407             "first-child" : function(c){
5408                 var r = [], ri = -1, n;
5409                 for(var i = 0, ci; ci = n = c[i]; i++){
5410                     while((n = n.previousSibling) && n.nodeType != 1);
5411                     if(!n){
5412                         r[++ri] = ci;
5413                     }
5414                 }
5415                 return r;
5416             },
5417
5418             "last-child" : function(c){
5419                 var r = [], ri = -1, n;
5420                 for(var i = 0, ci; ci = n = c[i]; i++){
5421                     while((n = n.nextSibling) && n.nodeType != 1);
5422                     if(!n){
5423                         r[++ri] = ci;
5424                     }
5425                 }
5426                 return r;
5427             },
5428
5429             "nth-child" : function(c, a) {
5430                 var r = [], ri = -1;
5431                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5432                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5433                 for(var i = 0, n; n = c[i]; i++){
5434                     var pn = n.parentNode;
5435                     if (batch != pn._batch) {
5436                         var j = 0;
5437                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5438                             if(cn.nodeType == 1){
5439                                cn.nodeIndex = ++j;
5440                             }
5441                         }
5442                         pn._batch = batch;
5443                     }
5444                     if (f == 1) {
5445                         if (l == 0 || n.nodeIndex == l){
5446                             r[++ri] = n;
5447                         }
5448                     } else if ((n.nodeIndex + l) % f == 0){
5449                         r[++ri] = n;
5450                     }
5451                 }
5452
5453                 return r;
5454             },
5455
5456             "only-child" : function(c){
5457                 var r = [], ri = -1;;
5458                 for(var i = 0, ci; ci = c[i]; i++){
5459                     if(!prev(ci) && !next(ci)){
5460                         r[++ri] = ci;
5461                     }
5462                 }
5463                 return r;
5464             },
5465
5466             "empty" : function(c){
5467                 var r = [], ri = -1;
5468                 for(var i = 0, ci; ci = c[i]; i++){
5469                     var cns = ci.childNodes, j = 0, cn, empty = true;
5470                     while(cn = cns[j]){
5471                         ++j;
5472                         if(cn.nodeType == 1 || cn.nodeType == 3){
5473                             empty = false;
5474                             break;
5475                         }
5476                     }
5477                     if(empty){
5478                         r[++ri] = ci;
5479                     }
5480                 }
5481                 return r;
5482             },
5483
5484             "contains" : function(c, v){
5485                 var r = [], ri = -1;
5486                 for(var i = 0, ci; ci = c[i]; i++){
5487                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5488                         r[++ri] = ci;
5489                     }
5490                 }
5491                 return r;
5492             },
5493
5494             "nodeValue" : function(c, v){
5495                 var r = [], ri = -1;
5496                 for(var i = 0, ci; ci = c[i]; i++){
5497                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5498                         r[++ri] = ci;
5499                     }
5500                 }
5501                 return r;
5502             },
5503
5504             "checked" : function(c){
5505                 var r = [], ri = -1;
5506                 for(var i = 0, ci; ci = c[i]; i++){
5507                     if(ci.checked == true){
5508                         r[++ri] = ci;
5509                     }
5510                 }
5511                 return r;
5512             },
5513
5514             "not" : function(c, ss){
5515                 return Roo.DomQuery.filter(c, ss, true);
5516             },
5517
5518             "odd" : function(c){
5519                 return this["nth-child"](c, "odd");
5520             },
5521
5522             "even" : function(c){
5523                 return this["nth-child"](c, "even");
5524             },
5525
5526             "nth" : function(c, a){
5527                 return c[a-1] || [];
5528             },
5529
5530             "first" : function(c){
5531                 return c[0] || [];
5532             },
5533
5534             "last" : function(c){
5535                 return c[c.length-1] || [];
5536             },
5537
5538             "has" : function(c, ss){
5539                 var s = Roo.DomQuery.select;
5540                 var r = [], ri = -1;
5541                 for(var i = 0, ci; ci = c[i]; i++){
5542                     if(s(ss, ci).length > 0){
5543                         r[++ri] = ci;
5544                     }
5545                 }
5546                 return r;
5547             },
5548
5549             "next" : function(c, ss){
5550                 var is = Roo.DomQuery.is;
5551                 var r = [], ri = -1;
5552                 for(var i = 0, ci; ci = c[i]; i++){
5553                     var n = next(ci);
5554                     if(n && is(n, ss)){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "prev" : function(c, ss){
5562                 var is = Roo.DomQuery.is;
5563                 var r = [], ri = -1;
5564                 for(var i = 0, ci; ci = c[i]; i++){
5565                     var n = prev(ci);
5566                     if(n && is(n, ss)){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             }
5572         }
5573     };
5574 }();
5575
5576 /**
5577  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5578  * @param {String} path The selector/xpath query
5579  * @param {Node} root (optional) The start of the query (defaults to document).
5580  * @return {Array}
5581  * @member Roo
5582  * @method query
5583  */
5584 Roo.query = Roo.DomQuery.select;
5585 /*
5586  * Based on:
5587  * Ext JS Library 1.1.1
5588  * Copyright(c) 2006-2007, Ext JS, LLC.
5589  *
5590  * Originally Released Under LGPL - original licence link has changed is not relivant.
5591  *
5592  * Fork - LGPL
5593  * <script type="text/javascript">
5594  */
5595
5596 /**
5597  * @class Roo.util.Observable
5598  * Base class that provides a common interface for publishing events. Subclasses are expected to
5599  * to have a property "events" with all the events defined.<br>
5600  * For example:
5601  * <pre><code>
5602  Employee = function(name){
5603     this.name = name;
5604     this.addEvents({
5605         "fired" : true,
5606         "quit" : true
5607     });
5608  }
5609  Roo.extend(Employee, Roo.util.Observable);
5610 </code></pre>
5611  * @param {Object} config properties to use (incuding events / listeners)
5612  */
5613
5614 Roo.util.Observable = function(cfg){
5615     
5616     cfg = cfg|| {};
5617     this.addEvents(cfg.events || {});
5618     if (cfg.events) {
5619         delete cfg.events; // make sure
5620     }
5621      
5622     Roo.apply(this, cfg);
5623     
5624     if(this.listeners){
5625         this.on(this.listeners);
5626         delete this.listeners;
5627     }
5628 };
5629 Roo.util.Observable.prototype = {
5630     /** 
5631  * @cfg {Object} listeners  list of events and functions to call for this object, 
5632  * For example :
5633  * <pre><code>
5634     listeners :  { 
5635        'click' : function(e) {
5636            ..... 
5637         } ,
5638         .... 
5639     } 
5640   </code></pre>
5641  */
5642     
5643     
5644     /**
5645      * Fires the specified event with the passed parameters (minus the event name).
5646      * @param {String} eventName
5647      * @param {Object...} args Variable number of parameters are passed to handlers
5648      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5649      */
5650     fireEvent : function(){
5651         var ce = this.events[arguments[0].toLowerCase()];
5652         if(typeof ce == "object"){
5653             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5654         }else{
5655             return true;
5656         }
5657     },
5658
5659     // private
5660     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5661
5662     /**
5663      * Appends an event handler to this component
5664      * @param {String}   eventName The type of event to listen for
5665      * @param {Function} handler The method the event invokes
5666      * @param {Object}   scope (optional) The scope in which to execute the handler
5667      * function. The handler function's "this" context.
5668      * @param {Object}   options (optional) An object containing handler configuration
5669      * properties. This may contain any of the following properties:<ul>
5670      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5671      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5672      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5673      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5674      * by the specified number of milliseconds. If the event fires again within that time, the original
5675      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5676      * </ul><br>
5677      * <p>
5678      * <b>Combining Options</b><br>
5679      * Using the options argument, it is possible to combine different types of listeners:<br>
5680      * <br>
5681      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5682                 <pre><code>
5683                 el.on('click', this.onClick, this, {
5684                         single: true,
5685                 delay: 100,
5686                 forumId: 4
5687                 });
5688                 </code></pre>
5689      * <p>
5690      * <b>Attaching multiple handlers in 1 call</b><br>
5691      * The method also allows for a single argument to be passed which is a config object containing properties
5692      * which specify multiple handlers.
5693      * <pre><code>
5694                 el.on({
5695                         'click': {
5696                         fn: this.onClick,
5697                         scope: this,
5698                         delay: 100
5699                 }, 
5700                 'mouseover': {
5701                         fn: this.onMouseOver,
5702                         scope: this
5703                 },
5704                 'mouseout': {
5705                         fn: this.onMouseOut,
5706                         scope: this
5707                 }
5708                 });
5709                 </code></pre>
5710      * <p>
5711      * Or a shorthand syntax which passes the same scope object to all handlers:
5712         <pre><code>
5713                 el.on({
5714                         'click': this.onClick,
5715                 'mouseover': this.onMouseOver,
5716                 'mouseout': this.onMouseOut,
5717                 scope: this
5718                 });
5719                 </code></pre>
5720      */
5721     addListener : function(eventName, fn, scope, o){
5722         if(typeof eventName == "object"){
5723             o = eventName;
5724             for(var e in o){
5725                 if(this.filterOptRe.test(e)){
5726                     continue;
5727                 }
5728                 if(typeof o[e] == "function"){
5729                     // shared options
5730                     this.addListener(e, o[e], o.scope,  o);
5731                 }else{
5732                     // individual options
5733                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5734                 }
5735             }
5736             return;
5737         }
5738         o = (!o || typeof o == "boolean") ? {} : o;
5739         eventName = eventName.toLowerCase();
5740         var ce = this.events[eventName] || true;
5741         if(typeof ce == "boolean"){
5742             ce = new Roo.util.Event(this, eventName);
5743             this.events[eventName] = ce;
5744         }
5745         ce.addListener(fn, scope, o);
5746     },
5747
5748     /**
5749      * Removes a listener
5750      * @param {String}   eventName     The type of event to listen for
5751      * @param {Function} handler        The handler to remove
5752      * @param {Object}   scope  (optional) The scope (this object) for the handler
5753      */
5754     removeListener : function(eventName, fn, scope){
5755         var ce = this.events[eventName.toLowerCase()];
5756         if(typeof ce == "object"){
5757             ce.removeListener(fn, scope);
5758         }
5759     },
5760
5761     /**
5762      * Removes all listeners for this object
5763      */
5764     purgeListeners : function(){
5765         for(var evt in this.events){
5766             if(typeof this.events[evt] == "object"){
5767                  this.events[evt].clearListeners();
5768             }
5769         }
5770     },
5771
5772     relayEvents : function(o, events){
5773         var createHandler = function(ename){
5774             return function(){
5775                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5776             };
5777         };
5778         for(var i = 0, len = events.length; i < len; i++){
5779             var ename = events[i];
5780             if(!this.events[ename]){ this.events[ename] = true; };
5781             o.on(ename, createHandler(ename), this);
5782         }
5783     },
5784
5785     /**
5786      * Used to define events on this Observable
5787      * @param {Object} object The object with the events defined
5788      */
5789     addEvents : function(o){
5790         if(!this.events){
5791             this.events = {};
5792         }
5793         Roo.applyIf(this.events, o);
5794     },
5795
5796     /**
5797      * Checks to see if this object has any listeners for a specified event
5798      * @param {String} eventName The name of the event to check for
5799      * @return {Boolean} True if the event is being listened for, else false
5800      */
5801     hasListener : function(eventName){
5802         var e = this.events[eventName];
5803         return typeof e == "object" && e.listeners.length > 0;
5804     }
5805 };
5806 /**
5807  * Appends an event handler to this element (shorthand for addListener)
5808  * @param {String}   eventName     The type of event to listen for
5809  * @param {Function} handler        The method the event invokes
5810  * @param {Object}   scope (optional) The scope in which to execute the handler
5811  * function. The handler function's "this" context.
5812  * @param {Object}   options  (optional)
5813  * @method
5814  */
5815 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5816 /**
5817  * Removes a listener (shorthand for removeListener)
5818  * @param {String}   eventName     The type of event to listen for
5819  * @param {Function} handler        The handler to remove
5820  * @param {Object}   scope  (optional) The scope (this object) for the handler
5821  * @method
5822  */
5823 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5824
5825 /**
5826  * Starts capture on the specified Observable. All events will be passed
5827  * to the supplied function with the event name + standard signature of the event
5828  * <b>before</b> the event is fired. If the supplied function returns false,
5829  * the event will not fire.
5830  * @param {Observable} o The Observable to capture
5831  * @param {Function} fn The function to call
5832  * @param {Object} scope (optional) The scope (this object) for the fn
5833  * @static
5834  */
5835 Roo.util.Observable.capture = function(o, fn, scope){
5836     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5837 };
5838
5839 /**
5840  * Removes <b>all</b> added captures from the Observable.
5841  * @param {Observable} o The Observable to release
5842  * @static
5843  */
5844 Roo.util.Observable.releaseCapture = function(o){
5845     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5846 };
5847
5848 (function(){
5849
5850     var createBuffered = function(h, o, scope){
5851         var task = new Roo.util.DelayedTask();
5852         return function(){
5853             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5854         };
5855     };
5856
5857     var createSingle = function(h, e, fn, scope){
5858         return function(){
5859             e.removeListener(fn, scope);
5860             return h.apply(scope, arguments);
5861         };
5862     };
5863
5864     var createDelayed = function(h, o, scope){
5865         return function(){
5866             var args = Array.prototype.slice.call(arguments, 0);
5867             setTimeout(function(){
5868                 h.apply(scope, args);
5869             }, o.delay || 10);
5870         };
5871     };
5872
5873     Roo.util.Event = function(obj, name){
5874         this.name = name;
5875         this.obj = obj;
5876         this.listeners = [];
5877     };
5878
5879     Roo.util.Event.prototype = {
5880         addListener : function(fn, scope, options){
5881             var o = options || {};
5882             scope = scope || this.obj;
5883             if(!this.isListening(fn, scope)){
5884                 var l = {fn: fn, scope: scope, options: o};
5885                 var h = fn;
5886                 if(o.delay){
5887                     h = createDelayed(h, o, scope);
5888                 }
5889                 if(o.single){
5890                     h = createSingle(h, this, fn, scope);
5891                 }
5892                 if(o.buffer){
5893                     h = createBuffered(h, o, scope);
5894                 }
5895                 l.fireFn = h;
5896                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5897                     this.listeners.push(l);
5898                 }else{
5899                     this.listeners = this.listeners.slice(0);
5900                     this.listeners.push(l);
5901                 }
5902             }
5903         },
5904
5905         findListener : function(fn, scope){
5906             scope = scope || this.obj;
5907             var ls = this.listeners;
5908             for(var i = 0, len = ls.length; i < len; i++){
5909                 var l = ls[i];
5910                 if(l.fn == fn && l.scope == scope){
5911                     return i;
5912                 }
5913             }
5914             return -1;
5915         },
5916
5917         isListening : function(fn, scope){
5918             return this.findListener(fn, scope) != -1;
5919         },
5920
5921         removeListener : function(fn, scope){
5922             var index;
5923             if((index = this.findListener(fn, scope)) != -1){
5924                 if(!this.firing){
5925                     this.listeners.splice(index, 1);
5926                 }else{
5927                     this.listeners = this.listeners.slice(0);
5928                     this.listeners.splice(index, 1);
5929                 }
5930                 return true;
5931             }
5932             return false;
5933         },
5934
5935         clearListeners : function(){
5936             this.listeners = [];
5937         },
5938
5939         fire : function(){
5940             var ls = this.listeners, scope, len = ls.length;
5941             if(len > 0){
5942                 this.firing = true;
5943                 var args = Array.prototype.slice.call(arguments, 0);
5944                 for(var i = 0; i < len; i++){
5945                     var l = ls[i];
5946                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5947                         this.firing = false;
5948                         return false;
5949                     }
5950                 }
5951                 this.firing = false;
5952             }
5953             return true;
5954         }
5955     };
5956 })();/*
5957  * Based on:
5958  * Ext JS Library 1.1.1
5959  * Copyright(c) 2006-2007, Ext JS, LLC.
5960  *
5961  * Originally Released Under LGPL - original licence link has changed is not relivant.
5962  *
5963  * Fork - LGPL
5964  * <script type="text/javascript">
5965  */
5966
5967 /**
5968  * @class Roo.EventManager
5969  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5970  * several useful events directly.
5971  * See {@link Roo.EventObject} for more details on normalized event objects.
5972  * @singleton
5973  */
5974 Roo.EventManager = function(){
5975     var docReadyEvent, docReadyProcId, docReadyState = false;
5976     var resizeEvent, resizeTask, textEvent, textSize;
5977     var E = Roo.lib.Event;
5978     var D = Roo.lib.Dom;
5979
5980
5981     var fireDocReady = function(){
5982         if(!docReadyState){
5983             docReadyState = true;
5984             Roo.isReady = true;
5985             if(docReadyProcId){
5986                 clearInterval(docReadyProcId);
5987             }
5988             if(Roo.isGecko || Roo.isOpera) {
5989                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5990             }
5991             if(Roo.isIE){
5992                 var defer = document.getElementById("ie-deferred-loader");
5993                 if(defer){
5994                     defer.onreadystatechange = null;
5995                     defer.parentNode.removeChild(defer);
5996                 }
5997             }
5998             if(docReadyEvent){
5999                 docReadyEvent.fire();
6000                 docReadyEvent.clearListeners();
6001             }
6002         }
6003     };
6004     
6005     var initDocReady = function(){
6006         docReadyEvent = new Roo.util.Event();
6007         if(Roo.isGecko || Roo.isOpera) {
6008             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6009         }else if(Roo.isIE){
6010             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6011             var defer = document.getElementById("ie-deferred-loader");
6012             defer.onreadystatechange = function(){
6013                 if(this.readyState == "complete"){
6014                     fireDocReady();
6015                 }
6016             };
6017         }else if(Roo.isSafari){ 
6018             docReadyProcId = setInterval(function(){
6019                 var rs = document.readyState;
6020                 if(rs == "complete") {
6021                     fireDocReady();     
6022                  }
6023             }, 10);
6024         }
6025         // no matter what, make sure it fires on load
6026         E.on(window, "load", fireDocReady);
6027     };
6028
6029     var createBuffered = function(h, o){
6030         var task = new Roo.util.DelayedTask(h);
6031         return function(e){
6032             // create new event object impl so new events don't wipe out properties
6033             e = new Roo.EventObjectImpl(e);
6034             task.delay(o.buffer, h, null, [e]);
6035         };
6036     };
6037
6038     var createSingle = function(h, el, ename, fn){
6039         return function(e){
6040             Roo.EventManager.removeListener(el, ename, fn);
6041             h(e);
6042         };
6043     };
6044
6045     var createDelayed = function(h, o){
6046         return function(e){
6047             // create new event object impl so new events don't wipe out properties
6048             e = new Roo.EventObjectImpl(e);
6049             setTimeout(function(){
6050                 h(e);
6051             }, o.delay || 10);
6052         };
6053     };
6054
6055     var listen = function(element, ename, opt, fn, scope){
6056         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6057         fn = fn || o.fn; scope = scope || o.scope;
6058         var el = Roo.getDom(element);
6059         if(!el){
6060             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6061         }
6062         var h = function(e){
6063             e = Roo.EventObject.setEvent(e);
6064             var t;
6065             if(o.delegate){
6066                 t = e.getTarget(o.delegate, el);
6067                 if(!t){
6068                     return;
6069                 }
6070             }else{
6071                 t = e.target;
6072             }
6073             if(o.stopEvent === true){
6074                 e.stopEvent();
6075             }
6076             if(o.preventDefault === true){
6077                e.preventDefault();
6078             }
6079             if(o.stopPropagation === true){
6080                 e.stopPropagation();
6081             }
6082
6083             if(o.normalized === false){
6084                 e = e.browserEvent;
6085             }
6086
6087             fn.call(scope || el, e, t, o);
6088         };
6089         if(o.delay){
6090             h = createDelayed(h, o);
6091         }
6092         if(o.single){
6093             h = createSingle(h, el, ename, fn);
6094         }
6095         if(o.buffer){
6096             h = createBuffered(h, o);
6097         }
6098         fn._handlers = fn._handlers || [];
6099         fn._handlers.push([Roo.id(el), ename, h]);
6100
6101         E.on(el, ename, h);
6102         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6103             el.addEventListener("DOMMouseScroll", h, false);
6104             E.on(window, 'unload', function(){
6105                 el.removeEventListener("DOMMouseScroll", h, false);
6106             });
6107         }
6108         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6109             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6110         }
6111         return h;
6112     };
6113
6114     var stopListening = function(el, ename, fn){
6115         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6116         if(hds){
6117             for(var i = 0, len = hds.length; i < len; i++){
6118                 var h = hds[i];
6119                 if(h[0] == id && h[1] == ename){
6120                     hd = h[2];
6121                     hds.splice(i, 1);
6122                     break;
6123                 }
6124             }
6125         }
6126         E.un(el, ename, hd);
6127         el = Roo.getDom(el);
6128         if(ename == "mousewheel" && el.addEventListener){
6129             el.removeEventListener("DOMMouseScroll", hd, false);
6130         }
6131         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6132             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6133         }
6134     };
6135
6136     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6137     
6138     var pub = {
6139         
6140         
6141         /** 
6142          * Fix for doc tools
6143          * @scope Roo.EventManager
6144          */
6145         
6146         
6147         /** 
6148          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6149          * object with a Roo.EventObject
6150          * @param {Function} fn        The method the event invokes
6151          * @param {Object}   scope    An object that becomes the scope of the handler
6152          * @param {boolean}  override If true, the obj passed in becomes
6153          *                             the execution scope of the listener
6154          * @return {Function} The wrapped function
6155          * @deprecated
6156          */
6157         wrap : function(fn, scope, override){
6158             return function(e){
6159                 Roo.EventObject.setEvent(e);
6160                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6161             };
6162         },
6163         
6164         /**
6165      * Appends an event handler to an element (shorthand for addListener)
6166      * @param {String/HTMLElement}   element        The html element or id to assign the
6167      * @param {String}   eventName The type of event to listen for
6168      * @param {Function} handler The method the event invokes
6169      * @param {Object}   scope (optional) The scope in which to execute the handler
6170      * function. The handler function's "this" context.
6171      * @param {Object}   options (optional) An object containing handler configuration
6172      * properties. This may contain any of the following properties:<ul>
6173      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6174      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6175      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6176      * <li>preventDefault {Boolean} True to prevent the default action</li>
6177      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6178      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6179      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6180      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6181      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6182      * by the specified number of milliseconds. If the event fires again within that time, the original
6183      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6184      * </ul><br>
6185      * <p>
6186      * <b>Combining Options</b><br>
6187      * Using the options argument, it is possible to combine different types of listeners:<br>
6188      * <br>
6189      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6190      * Code:<pre><code>
6191 el.on('click', this.onClick, this, {
6192     single: true,
6193     delay: 100,
6194     stopEvent : true,
6195     forumId: 4
6196 });</code></pre>
6197      * <p>
6198      * <b>Attaching multiple handlers in 1 call</b><br>
6199       * The method also allows for a single argument to be passed which is a config object containing properties
6200      * which specify multiple handlers.
6201      * <p>
6202      * Code:<pre><code>
6203 el.on({
6204     'click' : {
6205         fn: this.onClick
6206         scope: this,
6207         delay: 100
6208     },
6209     'mouseover' : {
6210         fn: this.onMouseOver
6211         scope: this
6212     },
6213     'mouseout' : {
6214         fn: this.onMouseOut
6215         scope: this
6216     }
6217 });</code></pre>
6218      * <p>
6219      * Or a shorthand syntax:<br>
6220      * Code:<pre><code>
6221 el.on({
6222     'click' : this.onClick,
6223     'mouseover' : this.onMouseOver,
6224     'mouseout' : this.onMouseOut
6225     scope: this
6226 });</code></pre>
6227      */
6228         addListener : function(element, eventName, fn, scope, options){
6229             if(typeof eventName == "object"){
6230                 var o = eventName;
6231                 for(var e in o){
6232                     if(propRe.test(e)){
6233                         continue;
6234                     }
6235                     if(typeof o[e] == "function"){
6236                         // shared options
6237                         listen(element, e, o, o[e], o.scope);
6238                     }else{
6239                         // individual options
6240                         listen(element, e, o[e]);
6241                     }
6242                 }
6243                 return;
6244             }
6245             return listen(element, eventName, options, fn, scope);
6246         },
6247         
6248         /**
6249          * Removes an event handler
6250          *
6251          * @param {String/HTMLElement}   element        The id or html element to remove the 
6252          *                             event from
6253          * @param {String}   eventName     The type of event
6254          * @param {Function} fn
6255          * @return {Boolean} True if a listener was actually removed
6256          */
6257         removeListener : function(element, eventName, fn){
6258             return stopListening(element, eventName, fn);
6259         },
6260         
6261         /**
6262          * Fires when the document is ready (before onload and before images are loaded). Can be 
6263          * accessed shorthanded Roo.onReady().
6264          * @param {Function} fn        The method the event invokes
6265          * @param {Object}   scope    An  object that becomes the scope of the handler
6266          * @param {boolean}  options
6267          */
6268         onDocumentReady : function(fn, scope, options){
6269             if(docReadyState){ // if it already fired
6270                 docReadyEvent.addListener(fn, scope, options);
6271                 docReadyEvent.fire();
6272                 docReadyEvent.clearListeners();
6273                 return;
6274             }
6275             if(!docReadyEvent){
6276                 initDocReady();
6277             }
6278             docReadyEvent.addListener(fn, scope, options);
6279         },
6280         
6281         /**
6282          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6283          * @param {Function} fn        The method the event invokes
6284          * @param {Object}   scope    An object that becomes the scope of the handler
6285          * @param {boolean}  options
6286          */
6287         onWindowResize : function(fn, scope, options){
6288             if(!resizeEvent){
6289                 resizeEvent = new Roo.util.Event();
6290                 resizeTask = new Roo.util.DelayedTask(function(){
6291                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6292                 });
6293                 E.on(window, "resize", function(){
6294                     if(Roo.isIE){
6295                         resizeTask.delay(50);
6296                     }else{
6297                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6298                     }
6299                 });
6300             }
6301             resizeEvent.addListener(fn, scope, options);
6302         },
6303
6304         /**
6305          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6306          * @param {Function} fn        The method the event invokes
6307          * @param {Object}   scope    An object that becomes the scope of the handler
6308          * @param {boolean}  options
6309          */
6310         onTextResize : function(fn, scope, options){
6311             if(!textEvent){
6312                 textEvent = new Roo.util.Event();
6313                 var textEl = new Roo.Element(document.createElement('div'));
6314                 textEl.dom.className = 'x-text-resize';
6315                 textEl.dom.innerHTML = 'X';
6316                 textEl.appendTo(document.body);
6317                 textSize = textEl.dom.offsetHeight;
6318                 setInterval(function(){
6319                     if(textEl.dom.offsetHeight != textSize){
6320                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6321                     }
6322                 }, this.textResizeInterval);
6323             }
6324             textEvent.addListener(fn, scope, options);
6325         },
6326
6327         /**
6328          * Removes the passed window resize listener.
6329          * @param {Function} fn        The method the event invokes
6330          * @param {Object}   scope    The scope of handler
6331          */
6332         removeResizeListener : function(fn, scope){
6333             if(resizeEvent){
6334                 resizeEvent.removeListener(fn, scope);
6335             }
6336         },
6337
6338         // private
6339         fireResize : function(){
6340             if(resizeEvent){
6341                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6342             }   
6343         },
6344         /**
6345          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6346          */
6347         ieDeferSrc : false,
6348         /**
6349          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6350          */
6351         textResizeInterval : 50
6352     };
6353     
6354     /**
6355      * Fix for doc tools
6356      * @scopeAlias pub=Roo.EventManager
6357      */
6358     
6359      /**
6360      * Appends an event handler to an element (shorthand for addListener)
6361      * @param {String/HTMLElement}   element        The html element or id to assign the
6362      * @param {String}   eventName The type of event to listen for
6363      * @param {Function} handler The method the event invokes
6364      * @param {Object}   scope (optional) The scope in which to execute the handler
6365      * function. The handler function's "this" context.
6366      * @param {Object}   options (optional) An object containing handler configuration
6367      * properties. This may contain any of the following properties:<ul>
6368      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6369      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6370      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6371      * <li>preventDefault {Boolean} True to prevent the default action</li>
6372      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6373      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6374      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6375      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6376      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6377      * by the specified number of milliseconds. If the event fires again within that time, the original
6378      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6379      * </ul><br>
6380      * <p>
6381      * <b>Combining Options</b><br>
6382      * Using the options argument, it is possible to combine different types of listeners:<br>
6383      * <br>
6384      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6385      * Code:<pre><code>
6386 el.on('click', this.onClick, this, {
6387     single: true,
6388     delay: 100,
6389     stopEvent : true,
6390     forumId: 4
6391 });</code></pre>
6392      * <p>
6393      * <b>Attaching multiple handlers in 1 call</b><br>
6394       * The method also allows for a single argument to be passed which is a config object containing properties
6395      * which specify multiple handlers.
6396      * <p>
6397      * Code:<pre><code>
6398 el.on({
6399     'click' : {
6400         fn: this.onClick
6401         scope: this,
6402         delay: 100
6403     },
6404     'mouseover' : {
6405         fn: this.onMouseOver
6406         scope: this
6407     },
6408     'mouseout' : {
6409         fn: this.onMouseOut
6410         scope: this
6411     }
6412 });</code></pre>
6413      * <p>
6414      * Or a shorthand syntax:<br>
6415      * Code:<pre><code>
6416 el.on({
6417     'click' : this.onClick,
6418     'mouseover' : this.onMouseOver,
6419     'mouseout' : this.onMouseOut
6420     scope: this
6421 });</code></pre>
6422      */
6423     pub.on = pub.addListener;
6424     pub.un = pub.removeListener;
6425
6426     pub.stoppedMouseDownEvent = new Roo.util.Event();
6427     return pub;
6428 }();
6429 /**
6430   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6431   * @param {Function} fn        The method the event invokes
6432   * @param {Object}   scope    An  object that becomes the scope of the handler
6433   * @param {boolean}  override If true, the obj passed in becomes
6434   *                             the execution scope of the listener
6435   * @member Roo
6436   * @method onReady
6437  */
6438 Roo.onReady = Roo.EventManager.onDocumentReady;
6439
6440 Roo.onReady(function(){
6441     var bd = Roo.get(document.body);
6442     if(!bd){ return; }
6443
6444     var cls = [
6445             Roo.isIE ? "roo-ie"
6446             : Roo.isGecko ? "roo-gecko"
6447             : Roo.isOpera ? "roo-opera"
6448             : Roo.isSafari ? "roo-safari" : ""];
6449
6450     if(Roo.isMac){
6451         cls.push("roo-mac");
6452     }
6453     if(Roo.isLinux){
6454         cls.push("roo-linux");
6455     }
6456     if(Roo.isBorderBox){
6457         cls.push('roo-border-box');
6458     }
6459     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6460         var p = bd.dom.parentNode;
6461         if(p){
6462             p.className += ' roo-strict';
6463         }
6464     }
6465     bd.addClass(cls.join(' '));
6466 });
6467
6468 /**
6469  * @class Roo.EventObject
6470  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6471  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6472  * Example:
6473  * <pre><code>
6474  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6475     e.preventDefault();
6476     var target = e.getTarget();
6477     ...
6478  }
6479  var myDiv = Roo.get("myDiv");
6480  myDiv.on("click", handleClick);
6481  //or
6482  Roo.EventManager.on("myDiv", 'click', handleClick);
6483  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6484  </code></pre>
6485  * @singleton
6486  */
6487 Roo.EventObject = function(){
6488     
6489     var E = Roo.lib.Event;
6490     
6491     // safari keypress events for special keys return bad keycodes
6492     var safariKeys = {
6493         63234 : 37, // left
6494         63235 : 39, // right
6495         63232 : 38, // up
6496         63233 : 40, // down
6497         63276 : 33, // page up
6498         63277 : 34, // page down
6499         63272 : 46, // delete
6500         63273 : 36, // home
6501         63275 : 35  // end
6502     };
6503
6504     // normalize button clicks
6505     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6506                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6507
6508     Roo.EventObjectImpl = function(e){
6509         if(e){
6510             this.setEvent(e.browserEvent || e);
6511         }
6512     };
6513     Roo.EventObjectImpl.prototype = {
6514         /**
6515          * Used to fix doc tools.
6516          * @scope Roo.EventObject.prototype
6517          */
6518             
6519
6520         
6521         
6522         /** The normal browser event */
6523         browserEvent : null,
6524         /** The button pressed in a mouse event */
6525         button : -1,
6526         /** True if the shift key was down during the event */
6527         shiftKey : false,
6528         /** True if the control key was down during the event */
6529         ctrlKey : false,
6530         /** True if the alt key was down during the event */
6531         altKey : false,
6532
6533         /** Key constant 
6534         * @type Number */
6535         BACKSPACE : 8,
6536         /** Key constant 
6537         * @type Number */
6538         TAB : 9,
6539         /** Key constant 
6540         * @type Number */
6541         RETURN : 13,
6542         /** Key constant 
6543         * @type Number */
6544         ENTER : 13,
6545         /** Key constant 
6546         * @type Number */
6547         SHIFT : 16,
6548         /** Key constant 
6549         * @type Number */
6550         CONTROL : 17,
6551         /** Key constant 
6552         * @type Number */
6553         ESC : 27,
6554         /** Key constant 
6555         * @type Number */
6556         SPACE : 32,
6557         /** Key constant 
6558         * @type Number */
6559         PAGEUP : 33,
6560         /** Key constant 
6561         * @type Number */
6562         PAGEDOWN : 34,
6563         /** Key constant 
6564         * @type Number */
6565         END : 35,
6566         /** Key constant 
6567         * @type Number */
6568         HOME : 36,
6569         /** Key constant 
6570         * @type Number */
6571         LEFT : 37,
6572         /** Key constant 
6573         * @type Number */
6574         UP : 38,
6575         /** Key constant 
6576         * @type Number */
6577         RIGHT : 39,
6578         /** Key constant 
6579         * @type Number */
6580         DOWN : 40,
6581         /** Key constant 
6582         * @type Number */
6583         DELETE : 46,
6584         /** Key constant 
6585         * @type Number */
6586         F5 : 116,
6587
6588            /** @private */
6589         setEvent : function(e){
6590             if(e == this || (e && e.browserEvent)){ // already wrapped
6591                 return e;
6592             }
6593             this.browserEvent = e;
6594             if(e){
6595                 // normalize buttons
6596                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6597                 if(e.type == 'click' && this.button == -1){
6598                     this.button = 0;
6599                 }
6600                 this.type = e.type;
6601                 this.shiftKey = e.shiftKey;
6602                 // mac metaKey behaves like ctrlKey
6603                 this.ctrlKey = e.ctrlKey || e.metaKey;
6604                 this.altKey = e.altKey;
6605                 // in getKey these will be normalized for the mac
6606                 this.keyCode = e.keyCode;
6607                 // keyup warnings on firefox.
6608                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6609                 // cache the target for the delayed and or buffered events
6610                 this.target = E.getTarget(e);
6611                 // same for XY
6612                 this.xy = E.getXY(e);
6613             }else{
6614                 this.button = -1;
6615                 this.shiftKey = false;
6616                 this.ctrlKey = false;
6617                 this.altKey = false;
6618                 this.keyCode = 0;
6619                 this.charCode =0;
6620                 this.target = null;
6621                 this.xy = [0, 0];
6622             }
6623             return this;
6624         },
6625
6626         /**
6627          * Stop the event (preventDefault and stopPropagation)
6628          */
6629         stopEvent : function(){
6630             if(this.browserEvent){
6631                 if(this.browserEvent.type == 'mousedown'){
6632                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6633                 }
6634                 E.stopEvent(this.browserEvent);
6635             }
6636         },
6637
6638         /**
6639          * Prevents the browsers default handling of the event.
6640          */
6641         preventDefault : function(){
6642             if(this.browserEvent){
6643                 E.preventDefault(this.browserEvent);
6644             }
6645         },
6646
6647         /** @private */
6648         isNavKeyPress : function(){
6649             var k = this.keyCode;
6650             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6651             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6652         },
6653
6654         isSpecialKey : function(){
6655             var k = this.keyCode;
6656             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6657             (k == 16) || (k == 17) ||
6658             (k >= 18 && k <= 20) ||
6659             (k >= 33 && k <= 35) ||
6660             (k >= 36 && k <= 39) ||
6661             (k >= 44 && k <= 45);
6662         },
6663         /**
6664          * Cancels bubbling of the event.
6665          */
6666         stopPropagation : function(){
6667             if(this.browserEvent){
6668                 if(this.type == 'mousedown'){
6669                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6670                 }
6671                 E.stopPropagation(this.browserEvent);
6672             }
6673         },
6674
6675         /**
6676          * Gets the key code for the event.
6677          * @return {Number}
6678          */
6679         getCharCode : function(){
6680             return this.charCode || this.keyCode;
6681         },
6682
6683         /**
6684          * Returns a normalized keyCode for the event.
6685          * @return {Number} The key code
6686          */
6687         getKey : function(){
6688             var k = this.keyCode || this.charCode;
6689             return Roo.isSafari ? (safariKeys[k] || k) : k;
6690         },
6691
6692         /**
6693          * Gets the x coordinate of the event.
6694          * @return {Number}
6695          */
6696         getPageX : function(){
6697             return this.xy[0];
6698         },
6699
6700         /**
6701          * Gets the y coordinate of the event.
6702          * @return {Number}
6703          */
6704         getPageY : function(){
6705             return this.xy[1];
6706         },
6707
6708         /**
6709          * Gets the time of the event.
6710          * @return {Number}
6711          */
6712         getTime : function(){
6713             if(this.browserEvent){
6714                 return E.getTime(this.browserEvent);
6715             }
6716             return null;
6717         },
6718
6719         /**
6720          * Gets the page coordinates of the event.
6721          * @return {Array} The xy values like [x, y]
6722          */
6723         getXY : function(){
6724             return this.xy;
6725         },
6726
6727         /**
6728          * Gets the target for the event.
6729          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6730          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6731                 search as a number or element (defaults to 10 || document.body)
6732          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6733          * @return {HTMLelement}
6734          */
6735         getTarget : function(selector, maxDepth, returnEl){
6736             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6737         },
6738         /**
6739          * Gets the related target.
6740          * @return {HTMLElement}
6741          */
6742         getRelatedTarget : function(){
6743             if(this.browserEvent){
6744                 return E.getRelatedTarget(this.browserEvent);
6745             }
6746             return null;
6747         },
6748
6749         /**
6750          * Normalizes mouse wheel delta across browsers
6751          * @return {Number} The delta
6752          */
6753         getWheelDelta : function(){
6754             var e = this.browserEvent;
6755             var delta = 0;
6756             if(e.wheelDelta){ /* IE/Opera. */
6757                 delta = e.wheelDelta/120;
6758             }else if(e.detail){ /* Mozilla case. */
6759                 delta = -e.detail/3;
6760             }
6761             return delta;
6762         },
6763
6764         /**
6765          * Returns true if the control, meta, shift or alt key was pressed during this event.
6766          * @return {Boolean}
6767          */
6768         hasModifier : function(){
6769             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6770         },
6771
6772         /**
6773          * Returns true if the target of this event equals el or is a child of el
6774          * @param {String/HTMLElement/Element} el
6775          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6776          * @return {Boolean}
6777          */
6778         within : function(el, related){
6779             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6780             return t && Roo.fly(el).contains(t);
6781         },
6782
6783         getPoint : function(){
6784             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6785         }
6786     };
6787
6788     return new Roo.EventObjectImpl();
6789 }();
6790             
6791     /*
6792  * Based on:
6793  * Ext JS Library 1.1.1
6794  * Copyright(c) 2006-2007, Ext JS, LLC.
6795  *
6796  * Originally Released Under LGPL - original licence link has changed is not relivant.
6797  *
6798  * Fork - LGPL
6799  * <script type="text/javascript">
6800  */
6801
6802  
6803 // was in Composite Element!??!?!
6804  
6805 (function(){
6806     var D = Roo.lib.Dom;
6807     var E = Roo.lib.Event;
6808     var A = Roo.lib.Anim;
6809
6810     // local style camelizing for speed
6811     var propCache = {};
6812     var camelRe = /(-[a-z])/gi;
6813     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6814     var view = document.defaultView;
6815
6816 /**
6817  * @class Roo.Element
6818  * Represents an Element in the DOM.<br><br>
6819  * Usage:<br>
6820 <pre><code>
6821 var el = Roo.get("my-div");
6822
6823 // or with getEl
6824 var el = getEl("my-div");
6825
6826 // or with a DOM element
6827 var el = Roo.get(myDivElement);
6828 </code></pre>
6829  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6830  * each call instead of constructing a new one.<br><br>
6831  * <b>Animations</b><br />
6832  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6833  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6834 <pre>
6835 Option    Default   Description
6836 --------- --------  ---------------------------------------------
6837 duration  .35       The duration of the animation in seconds
6838 easing    easeOut   The YUI easing method
6839 callback  none      A function to execute when the anim completes
6840 scope     this      The scope (this) of the callback function
6841 </pre>
6842 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6843 * manipulate the animation. Here's an example:
6844 <pre><code>
6845 var el = Roo.get("my-div");
6846
6847 // no animation
6848 el.setWidth(100);
6849
6850 // default animation
6851 el.setWidth(100, true);
6852
6853 // animation with some options set
6854 el.setWidth(100, {
6855     duration: 1,
6856     callback: this.foo,
6857     scope: this
6858 });
6859
6860 // using the "anim" property to get the Anim object
6861 var opt = {
6862     duration: 1,
6863     callback: this.foo,
6864     scope: this
6865 };
6866 el.setWidth(100, opt);
6867 ...
6868 if(opt.anim.isAnimated()){
6869     opt.anim.stop();
6870 }
6871 </code></pre>
6872 * <b> Composite (Collections of) Elements</b><br />
6873  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6874  * @constructor Create a new Element directly.
6875  * @param {String/HTMLElement} element
6876  * @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).
6877  */
6878     Roo.Element = function(element, forceNew){
6879         var dom = typeof element == "string" ?
6880                 document.getElementById(element) : element;
6881         if(!dom){ // invalid id/element
6882             return null;
6883         }
6884         var id = dom.id;
6885         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6886             return Roo.Element.cache[id];
6887         }
6888
6889         /**
6890          * The DOM element
6891          * @type HTMLElement
6892          */
6893         this.dom = dom;
6894
6895         /**
6896          * The DOM element ID
6897          * @type String
6898          */
6899         this.id = id || Roo.id(dom);
6900     };
6901
6902     var El = Roo.Element;
6903
6904     El.prototype = {
6905         /**
6906          * The element's default display mode  (defaults to "")
6907          * @type String
6908          */
6909         originalDisplay : "",
6910
6911         visibilityMode : 1,
6912         /**
6913          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6914          * @type String
6915          */
6916         defaultUnit : "px",
6917         /**
6918          * Sets the element's visibility mode. When setVisible() is called it
6919          * will use this to determine whether to set the visibility or the display property.
6920          * @param visMode Element.VISIBILITY or Element.DISPLAY
6921          * @return {Roo.Element} this
6922          */
6923         setVisibilityMode : function(visMode){
6924             this.visibilityMode = visMode;
6925             return this;
6926         },
6927         /**
6928          * Convenience method for setVisibilityMode(Element.DISPLAY)
6929          * @param {String} display (optional) What to set display to when visible
6930          * @return {Roo.Element} this
6931          */
6932         enableDisplayMode : function(display){
6933             this.setVisibilityMode(El.DISPLAY);
6934             if(typeof display != "undefined") this.originalDisplay = display;
6935             return this;
6936         },
6937
6938         /**
6939          * 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)
6940          * @param {String} selector The simple selector to test
6941          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6942                 search as a number or element (defaults to 10 || document.body)
6943          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6944          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6945          */
6946         findParent : function(simpleSelector, maxDepth, returnEl){
6947             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6948             maxDepth = maxDepth || 50;
6949             if(typeof maxDepth != "number"){
6950                 stopEl = Roo.getDom(maxDepth);
6951                 maxDepth = 10;
6952             }
6953             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6954                 if(dq.is(p, simpleSelector)){
6955                     return returnEl ? Roo.get(p) : p;
6956                 }
6957                 depth++;
6958                 p = p.parentNode;
6959             }
6960             return null;
6961         },
6962
6963
6964         /**
6965          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6966          * @param {String} selector The simple selector to test
6967          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6968                 search as a number or element (defaults to 10 || document.body)
6969          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6970          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6971          */
6972         findParentNode : function(simpleSelector, maxDepth, returnEl){
6973             var p = Roo.fly(this.dom.parentNode, '_internal');
6974             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6975         },
6976
6977         /**
6978          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6979          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6980          * @param {String} selector The simple selector to test
6981          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6982                 search as a number or element (defaults to 10 || document.body)
6983          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6984          */
6985         up : function(simpleSelector, maxDepth){
6986             return this.findParentNode(simpleSelector, maxDepth, true);
6987         },
6988
6989
6990
6991         /**
6992          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6993          * @param {String} selector The simple selector to test
6994          * @return {Boolean} True if this element matches the selector, else false
6995          */
6996         is : function(simpleSelector){
6997             return Roo.DomQuery.is(this.dom, simpleSelector);
6998         },
6999
7000         /**
7001          * Perform animation on this element.
7002          * @param {Object} args The YUI animation control args
7003          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7004          * @param {Function} onComplete (optional) Function to call when animation completes
7005          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7006          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7007          * @return {Roo.Element} this
7008          */
7009         animate : function(args, duration, onComplete, easing, animType){
7010             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7011             return this;
7012         },
7013
7014         /*
7015          * @private Internal animation call
7016          */
7017         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7018             animType = animType || 'run';
7019             opt = opt || {};
7020             var anim = Roo.lib.Anim[animType](
7021                 this.dom, args,
7022                 (opt.duration || defaultDur) || .35,
7023                 (opt.easing || defaultEase) || 'easeOut',
7024                 function(){
7025                     Roo.callback(cb, this);
7026                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7027                 },
7028                 this
7029             );
7030             opt.anim = anim;
7031             return anim;
7032         },
7033
7034         // private legacy anim prep
7035         preanim : function(a, i){
7036             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7037         },
7038
7039         /**
7040          * Removes worthless text nodes
7041          * @param {Boolean} forceReclean (optional) By default the element
7042          * keeps track if it has been cleaned already so
7043          * you can call this over and over. However, if you update the element and
7044          * need to force a reclean, you can pass true.
7045          */
7046         clean : function(forceReclean){
7047             if(this.isCleaned && forceReclean !== true){
7048                 return this;
7049             }
7050             var ns = /\S/;
7051             var d = this.dom, n = d.firstChild, ni = -1;
7052             while(n){
7053                 var nx = n.nextSibling;
7054                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7055                     d.removeChild(n);
7056                 }else{
7057                     n.nodeIndex = ++ni;
7058                 }
7059                 n = nx;
7060             }
7061             this.isCleaned = true;
7062             return this;
7063         },
7064
7065         // private
7066         calcOffsetsTo : function(el){
7067             el = Roo.get(el);
7068             var d = el.dom;
7069             var restorePos = false;
7070             if(el.getStyle('position') == 'static'){
7071                 el.position('relative');
7072                 restorePos = true;
7073             }
7074             var x = 0, y =0;
7075             var op = this.dom;
7076             while(op && op != d && op.tagName != 'HTML'){
7077                 x+= op.offsetLeft;
7078                 y+= op.offsetTop;
7079                 op = op.offsetParent;
7080             }
7081             if(restorePos){
7082                 el.position('static');
7083             }
7084             return [x, y];
7085         },
7086
7087         /**
7088          * Scrolls this element into view within the passed container.
7089          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7090          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7091          * @return {Roo.Element} this
7092          */
7093         scrollIntoView : function(container, hscroll){
7094             var c = Roo.getDom(container) || document.body;
7095             var el = this.dom;
7096
7097             var o = this.calcOffsetsTo(c),
7098                 l = o[0],
7099                 t = o[1],
7100                 b = t+el.offsetHeight,
7101                 r = l+el.offsetWidth;
7102
7103             var ch = c.clientHeight;
7104             var ct = parseInt(c.scrollTop, 10);
7105             var cl = parseInt(c.scrollLeft, 10);
7106             var cb = ct + ch;
7107             var cr = cl + c.clientWidth;
7108
7109             if(t < ct){
7110                 c.scrollTop = t;
7111             }else if(b > cb){
7112                 c.scrollTop = b-ch;
7113             }
7114
7115             if(hscroll !== false){
7116                 if(l < cl){
7117                     c.scrollLeft = l;
7118                 }else if(r > cr){
7119                     c.scrollLeft = r-c.clientWidth;
7120                 }
7121             }
7122             return this;
7123         },
7124
7125         // private
7126         scrollChildIntoView : function(child, hscroll){
7127             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7128         },
7129
7130         /**
7131          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7132          * the new height may not be available immediately.
7133          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7134          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7135          * @param {Function} onComplete (optional) Function to call when animation completes
7136          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7137          * @return {Roo.Element} this
7138          */
7139         autoHeight : function(animate, duration, onComplete, easing){
7140             var oldHeight = this.getHeight();
7141             this.clip();
7142             this.setHeight(1); // force clipping
7143             setTimeout(function(){
7144                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7145                 if(!animate){
7146                     this.setHeight(height);
7147                     this.unclip();
7148                     if(typeof onComplete == "function"){
7149                         onComplete();
7150                     }
7151                 }else{
7152                     this.setHeight(oldHeight); // restore original height
7153                     this.setHeight(height, animate, duration, function(){
7154                         this.unclip();
7155                         if(typeof onComplete == "function") onComplete();
7156                     }.createDelegate(this), easing);
7157                 }
7158             }.createDelegate(this), 0);
7159             return this;
7160         },
7161
7162         /**
7163          * Returns true if this element is an ancestor of the passed element
7164          * @param {HTMLElement/String} el The element to check
7165          * @return {Boolean} True if this element is an ancestor of el, else false
7166          */
7167         contains : function(el){
7168             if(!el){return false;}
7169             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7170         },
7171
7172         /**
7173          * Checks whether the element is currently visible using both visibility and display properties.
7174          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7175          * @return {Boolean} True if the element is currently visible, else false
7176          */
7177         isVisible : function(deep) {
7178             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7179             if(deep !== true || !vis){
7180                 return vis;
7181             }
7182             var p = this.dom.parentNode;
7183             while(p && p.tagName.toLowerCase() != "body"){
7184                 if(!Roo.fly(p, '_isVisible').isVisible()){
7185                     return false;
7186                 }
7187                 p = p.parentNode;
7188             }
7189             return true;
7190         },
7191
7192         /**
7193          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7194          * @param {String} selector The CSS selector
7195          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7196          * @return {CompositeElement/CompositeElementLite} The composite element
7197          */
7198         select : function(selector, unique){
7199             return El.select(selector, unique, this.dom);
7200         },
7201
7202         /**
7203          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7204          * @param {String} selector The CSS selector
7205          * @return {Array} An array of the matched nodes
7206          */
7207         query : function(selector, unique){
7208             return Roo.DomQuery.select(selector, this.dom);
7209         },
7210
7211         /**
7212          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7213          * @param {String} selector The CSS selector
7214          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7215          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7216          */
7217         child : function(selector, returnDom){
7218             var n = Roo.DomQuery.selectNode(selector, this.dom);
7219             return returnDom ? n : Roo.get(n);
7220         },
7221
7222         /**
7223          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7224          * @param {String} selector The CSS selector
7225          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7226          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7227          */
7228         down : function(selector, returnDom){
7229             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7230             return returnDom ? n : Roo.get(n);
7231         },
7232
7233         /**
7234          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7235          * @param {String} group The group the DD object is member of
7236          * @param {Object} config The DD config object
7237          * @param {Object} overrides An object containing methods to override/implement on the DD object
7238          * @return {Roo.dd.DD} The DD object
7239          */
7240         initDD : function(group, config, overrides){
7241             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7242             return Roo.apply(dd, overrides);
7243         },
7244
7245         /**
7246          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7247          * @param {String} group The group the DDProxy object is member of
7248          * @param {Object} config The DDProxy config object
7249          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7250          * @return {Roo.dd.DDProxy} The DDProxy object
7251          */
7252         initDDProxy : function(group, config, overrides){
7253             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7254             return Roo.apply(dd, overrides);
7255         },
7256
7257         /**
7258          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7259          * @param {String} group The group the DDTarget object is member of
7260          * @param {Object} config The DDTarget config object
7261          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7262          * @return {Roo.dd.DDTarget} The DDTarget object
7263          */
7264         initDDTarget : function(group, config, overrides){
7265             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7266             return Roo.apply(dd, overrides);
7267         },
7268
7269         /**
7270          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7271          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7272          * @param {Boolean} visible Whether the element is visible
7273          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7274          * @return {Roo.Element} this
7275          */
7276          setVisible : function(visible, animate){
7277             if(!animate || !A){
7278                 if(this.visibilityMode == El.DISPLAY){
7279                     this.setDisplayed(visible);
7280                 }else{
7281                     this.fixDisplay();
7282                     this.dom.style.visibility = visible ? "visible" : "hidden";
7283                 }
7284             }else{
7285                 // closure for composites
7286                 var dom = this.dom;
7287                 var visMode = this.visibilityMode;
7288                 if(visible){
7289                     this.setOpacity(.01);
7290                     this.setVisible(true);
7291                 }
7292                 this.anim({opacity: { to: (visible?1:0) }},
7293                       this.preanim(arguments, 1),
7294                       null, .35, 'easeIn', function(){
7295                          if(!visible){
7296                              if(visMode == El.DISPLAY){
7297                                  dom.style.display = "none";
7298                              }else{
7299                                  dom.style.visibility = "hidden";
7300                              }
7301                              Roo.get(dom).setOpacity(1);
7302                          }
7303                      });
7304             }
7305             return this;
7306         },
7307
7308         /**
7309          * Returns true if display is not "none"
7310          * @return {Boolean}
7311          */
7312         isDisplayed : function() {
7313             return this.getStyle("display") != "none";
7314         },
7315
7316         /**
7317          * Toggles the element's visibility or display, depending on visibility mode.
7318          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7319          * @return {Roo.Element} this
7320          */
7321         toggle : function(animate){
7322             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7323             return this;
7324         },
7325
7326         /**
7327          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7328          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7329          * @return {Roo.Element} this
7330          */
7331         setDisplayed : function(value) {
7332             if(typeof value == "boolean"){
7333                value = value ? this.originalDisplay : "none";
7334             }
7335             this.setStyle("display", value);
7336             return this;
7337         },
7338
7339         /**
7340          * Tries to focus the element. Any exceptions are caught and ignored.
7341          * @return {Roo.Element} this
7342          */
7343         focus : function() {
7344             try{
7345                 this.dom.focus();
7346             }catch(e){}
7347             return this;
7348         },
7349
7350         /**
7351          * Tries to blur the element. Any exceptions are caught and ignored.
7352          * @return {Roo.Element} this
7353          */
7354         blur : function() {
7355             try{
7356                 this.dom.blur();
7357             }catch(e){}
7358             return this;
7359         },
7360
7361         /**
7362          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7363          * @param {String/Array} className The CSS class to add, or an array of classes
7364          * @return {Roo.Element} this
7365          */
7366         addClass : function(className){
7367             if(className instanceof Array){
7368                 for(var i = 0, len = className.length; i < len; i++) {
7369                     this.addClass(className[i]);
7370                 }
7371             }else{
7372                 if(className && !this.hasClass(className)){
7373                     this.dom.className = this.dom.className + " " + className;
7374                 }
7375             }
7376             return this;
7377         },
7378
7379         /**
7380          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7381          * @param {String/Array} className The CSS class to add, or an array of classes
7382          * @return {Roo.Element} this
7383          */
7384         radioClass : function(className){
7385             var siblings = this.dom.parentNode.childNodes;
7386             for(var i = 0; i < siblings.length; i++) {
7387                 var s = siblings[i];
7388                 if(s.nodeType == 1){
7389                     Roo.get(s).removeClass(className);
7390                 }
7391             }
7392             this.addClass(className);
7393             return this;
7394         },
7395
7396         /**
7397          * Removes one or more CSS classes from the element.
7398          * @param {String/Array} className The CSS class to remove, or an array of classes
7399          * @return {Roo.Element} this
7400          */
7401         removeClass : function(className){
7402             if(!className || !this.dom.className){
7403                 return this;
7404             }
7405             if(className instanceof Array){
7406                 for(var i = 0, len = className.length; i < len; i++) {
7407                     this.removeClass(className[i]);
7408                 }
7409             }else{
7410                 if(this.hasClass(className)){
7411                     var re = this.classReCache[className];
7412                     if (!re) {
7413                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7414                        this.classReCache[className] = re;
7415                     }
7416                     this.dom.className =
7417                         this.dom.className.replace(re, " ");
7418                 }
7419             }
7420             return this;
7421         },
7422
7423         // private
7424         classReCache: {},
7425
7426         /**
7427          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7428          * @param {String} className The CSS class to toggle
7429          * @return {Roo.Element} this
7430          */
7431         toggleClass : function(className){
7432             if(this.hasClass(className)){
7433                 this.removeClass(className);
7434             }else{
7435                 this.addClass(className);
7436             }
7437             return this;
7438         },
7439
7440         /**
7441          * Checks if the specified CSS class exists on this element's DOM node.
7442          * @param {String} className The CSS class to check for
7443          * @return {Boolean} True if the class exists, else false
7444          */
7445         hasClass : function(className){
7446             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7447         },
7448
7449         /**
7450          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7451          * @param {String} oldClassName The CSS class to replace
7452          * @param {String} newClassName The replacement CSS class
7453          * @return {Roo.Element} this
7454          */
7455         replaceClass : function(oldClassName, newClassName){
7456             this.removeClass(oldClassName);
7457             this.addClass(newClassName);
7458             return this;
7459         },
7460
7461         /**
7462          * Returns an object with properties matching the styles requested.
7463          * For example, el.getStyles('color', 'font-size', 'width') might return
7464          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7465          * @param {String} style1 A style name
7466          * @param {String} style2 A style name
7467          * @param {String} etc.
7468          * @return {Object} The style object
7469          */
7470         getStyles : function(){
7471             var a = arguments, len = a.length, r = {};
7472             for(var i = 0; i < len; i++){
7473                 r[a[i]] = this.getStyle(a[i]);
7474             }
7475             return r;
7476         },
7477
7478         /**
7479          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7480          * @param {String} property The style property whose value is returned.
7481          * @return {String} The current value of the style property for this element.
7482          */
7483         getStyle : function(){
7484             return view && view.getComputedStyle ?
7485                 function(prop){
7486                     var el = this.dom, v, cs, camel;
7487                     if(prop == 'float'){
7488                         prop = "cssFloat";
7489                     }
7490                     if(el.style && (v = el.style[prop])){
7491                         return v;
7492                     }
7493                     if(cs = view.getComputedStyle(el, "")){
7494                         if(!(camel = propCache[prop])){
7495                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7496                         }
7497                         return cs[camel];
7498                     }
7499                     return null;
7500                 } :
7501                 function(prop){
7502                     var el = this.dom, v, cs, camel;
7503                     if(prop == 'opacity'){
7504                         if(typeof el.style.filter == 'string'){
7505                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7506                             if(m){
7507                                 var fv = parseFloat(m[1]);
7508                                 if(!isNaN(fv)){
7509                                     return fv ? fv / 100 : 0;
7510                                 }
7511                             }
7512                         }
7513                         return 1;
7514                     }else if(prop == 'float'){
7515                         prop = "styleFloat";
7516                     }
7517                     if(!(camel = propCache[prop])){
7518                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7519                     }
7520                     if(v = el.style[camel]){
7521                         return v;
7522                     }
7523                     if(cs = el.currentStyle){
7524                         return cs[camel];
7525                     }
7526                     return null;
7527                 };
7528         }(),
7529
7530         /**
7531          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7532          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7533          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7534          * @return {Roo.Element} this
7535          */
7536         setStyle : function(prop, value){
7537             if(typeof prop == "string"){
7538                 
7539                 if (prop == 'float') {
7540                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7541                     return this;
7542                 }
7543                 
7544                 var camel;
7545                 if(!(camel = propCache[prop])){
7546                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7547                 }
7548                 
7549                 if(camel == 'opacity') {
7550                     this.setOpacity(value);
7551                 }else{
7552                     this.dom.style[camel] = value;
7553                 }
7554             }else{
7555                 for(var style in prop){
7556                     if(typeof prop[style] != "function"){
7557                        this.setStyle(style, prop[style]);
7558                     }
7559                 }
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * More flexible version of {@link #setStyle} for setting style properties.
7566          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7567          * a function which returns such a specification.
7568          * @return {Roo.Element} this
7569          */
7570         applyStyles : function(style){
7571             Roo.DomHelper.applyStyles(this.dom, style);
7572             return this;
7573         },
7574
7575         /**
7576           * 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).
7577           * @return {Number} The X position of the element
7578           */
7579         getX : function(){
7580             return D.getX(this.dom);
7581         },
7582
7583         /**
7584           * 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).
7585           * @return {Number} The Y position of the element
7586           */
7587         getY : function(){
7588             return D.getY(this.dom);
7589         },
7590
7591         /**
7592           * 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).
7593           * @return {Array} The XY position of the element
7594           */
7595         getXY : function(){
7596             return D.getXY(this.dom);
7597         },
7598
7599         /**
7600          * 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).
7601          * @param {Number} The X position of the element
7602          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7603          * @return {Roo.Element} this
7604          */
7605         setX : function(x, animate){
7606             if(!animate || !A){
7607                 D.setX(this.dom, x);
7608             }else{
7609                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7610             }
7611             return this;
7612         },
7613
7614         /**
7615          * 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).
7616          * @param {Number} The Y position of the element
7617          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7618          * @return {Roo.Element} this
7619          */
7620         setY : function(y, animate){
7621             if(!animate || !A){
7622                 D.setY(this.dom, y);
7623             }else{
7624                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7625             }
7626             return this;
7627         },
7628
7629         /**
7630          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7631          * @param {String} left The left CSS property value
7632          * @return {Roo.Element} this
7633          */
7634         setLeft : function(left){
7635             this.setStyle("left", this.addUnits(left));
7636             return this;
7637         },
7638
7639         /**
7640          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7641          * @param {String} top The top CSS property value
7642          * @return {Roo.Element} this
7643          */
7644         setTop : function(top){
7645             this.setStyle("top", this.addUnits(top));
7646             return this;
7647         },
7648
7649         /**
7650          * Sets the element's CSS right style.
7651          * @param {String} right The right CSS property value
7652          * @return {Roo.Element} this
7653          */
7654         setRight : function(right){
7655             this.setStyle("right", this.addUnits(right));
7656             return this;
7657         },
7658
7659         /**
7660          * Sets the element's CSS bottom style.
7661          * @param {String} bottom The bottom CSS property value
7662          * @return {Roo.Element} this
7663          */
7664         setBottom : function(bottom){
7665             this.setStyle("bottom", this.addUnits(bottom));
7666             return this;
7667         },
7668
7669         /**
7670          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7671          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7672          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setXY : function(pos, animate){
7677             if(!animate || !A){
7678                 D.setXY(this.dom, pos);
7679             }else{
7680                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7687          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7688          * @param {Number} x X value for new position (coordinates are page-based)
7689          * @param {Number} y Y value for new position (coordinates are page-based)
7690          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7691          * @return {Roo.Element} this
7692          */
7693         setLocation : function(x, y, animate){
7694             this.setXY([x, y], this.preanim(arguments, 2));
7695             return this;
7696         },
7697
7698         /**
7699          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7700          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7701          * @param {Number} x X value for new position (coordinates are page-based)
7702          * @param {Number} y Y value for new position (coordinates are page-based)
7703          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7704          * @return {Roo.Element} this
7705          */
7706         moveTo : function(x, y, animate){
7707             this.setXY([x, y], this.preanim(arguments, 2));
7708             return this;
7709         },
7710
7711         /**
7712          * Returns the region of the given element.
7713          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7714          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7715          */
7716         getRegion : function(){
7717             return D.getRegion(this.dom);
7718         },
7719
7720         /**
7721          * Returns the offset height of the element
7722          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7723          * @return {Number} The element's height
7724          */
7725         getHeight : function(contentHeight){
7726             var h = this.dom.offsetHeight || 0;
7727             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7728         },
7729
7730         /**
7731          * Returns the offset width of the element
7732          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7733          * @return {Number} The element's width
7734          */
7735         getWidth : function(contentWidth){
7736             var w = this.dom.offsetWidth || 0;
7737             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7738         },
7739
7740         /**
7741          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7742          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7743          * if a height has not been set using CSS.
7744          * @return {Number}
7745          */
7746         getComputedHeight : function(){
7747             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7748             if(!h){
7749                 h = parseInt(this.getStyle('height'), 10) || 0;
7750                 if(!this.isBorderBox()){
7751                     h += this.getFrameWidth('tb');
7752                 }
7753             }
7754             return h;
7755         },
7756
7757         /**
7758          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7759          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7760          * if a width has not been set using CSS.
7761          * @return {Number}
7762          */
7763         getComputedWidth : function(){
7764             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7765             if(!w){
7766                 w = parseInt(this.getStyle('width'), 10) || 0;
7767                 if(!this.isBorderBox()){
7768                     w += this.getFrameWidth('lr');
7769                 }
7770             }
7771             return w;
7772         },
7773
7774         /**
7775          * Returns the size of the element.
7776          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7777          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7778          */
7779         getSize : function(contentSize){
7780             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7781         },
7782
7783         /**
7784          * Returns the width and height of the viewport.
7785          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7786          */
7787         getViewSize : function(){
7788             var d = this.dom, doc = document, aw = 0, ah = 0;
7789             if(d == doc || d == doc.body){
7790                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7791             }else{
7792                 return {
7793                     width : d.clientWidth,
7794                     height: d.clientHeight
7795                 };
7796             }
7797         },
7798
7799         /**
7800          * Returns the value of the "value" attribute
7801          * @param {Boolean} asNumber true to parse the value as a number
7802          * @return {String/Number}
7803          */
7804         getValue : function(asNumber){
7805             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7806         },
7807
7808         // private
7809         adjustWidth : function(width){
7810             if(typeof width == "number"){
7811                 if(this.autoBoxAdjust && !this.isBorderBox()){
7812                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7813                 }
7814                 if(width < 0){
7815                     width = 0;
7816                 }
7817             }
7818             return width;
7819         },
7820
7821         // private
7822         adjustHeight : function(height){
7823             if(typeof height == "number"){
7824                if(this.autoBoxAdjust && !this.isBorderBox()){
7825                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7826                }
7827                if(height < 0){
7828                    height = 0;
7829                }
7830             }
7831             return height;
7832         },
7833
7834         /**
7835          * Set the width of the element
7836          * @param {Number} width The new width
7837          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7838          * @return {Roo.Element} this
7839          */
7840         setWidth : function(width, animate){
7841             width = this.adjustWidth(width);
7842             if(!animate || !A){
7843                 this.dom.style.width = this.addUnits(width);
7844             }else{
7845                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7846             }
7847             return this;
7848         },
7849
7850         /**
7851          * Set the height of the element
7852          * @param {Number} height The new height
7853          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7854          * @return {Roo.Element} this
7855          */
7856          setHeight : function(height, animate){
7857             height = this.adjustHeight(height);
7858             if(!animate || !A){
7859                 this.dom.style.height = this.addUnits(height);
7860             }else{
7861                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7862             }
7863             return this;
7864         },
7865
7866         /**
7867          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7868          * @param {Number} width The new width
7869          * @param {Number} height The new height
7870          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7871          * @return {Roo.Element} this
7872          */
7873          setSize : function(width, height, animate){
7874             if(typeof width == "object"){ // in case of object from getSize()
7875                 height = width.height; width = width.width;
7876             }
7877             width = this.adjustWidth(width); height = this.adjustHeight(height);
7878             if(!animate || !A){
7879                 this.dom.style.width = this.addUnits(width);
7880                 this.dom.style.height = this.addUnits(height);
7881             }else{
7882                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7889          * @param {Number} x X value for new position (coordinates are page-based)
7890          * @param {Number} y Y value for new position (coordinates are page-based)
7891          * @param {Number} width The new width
7892          * @param {Number} height The new height
7893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7894          * @return {Roo.Element} this
7895          */
7896         setBounds : function(x, y, width, height, animate){
7897             if(!animate || !A){
7898                 this.setSize(width, height);
7899                 this.setLocation(x, y);
7900             }else{
7901                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7902                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7903                               this.preanim(arguments, 4), 'motion');
7904             }
7905             return this;
7906         },
7907
7908         /**
7909          * 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.
7910          * @param {Roo.lib.Region} region The region to fill
7911          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7912          * @return {Roo.Element} this
7913          */
7914         setRegion : function(region, animate){
7915             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7916             return this;
7917         },
7918
7919         /**
7920          * Appends an event handler
7921          *
7922          * @param {String}   eventName     The type of event to append
7923          * @param {Function} fn        The method the event invokes
7924          * @param {Object} scope       (optional) The scope (this object) of the fn
7925          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7926          */
7927         addListener : function(eventName, fn, scope, options){
7928             if (this.dom) {
7929                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7930             }
7931         },
7932
7933         /**
7934          * Removes an event handler from this element
7935          * @param {String} eventName the type of event to remove
7936          * @param {Function} fn the method the event invokes
7937          * @return {Roo.Element} this
7938          */
7939         removeListener : function(eventName, fn){
7940             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7941             return this;
7942         },
7943
7944         /**
7945          * Removes all previous added listeners from this element
7946          * @return {Roo.Element} this
7947          */
7948         removeAllListeners : function(){
7949             E.purgeElement(this.dom);
7950             return this;
7951         },
7952
7953         relayEvent : function(eventName, observable){
7954             this.on(eventName, function(e){
7955                 observable.fireEvent(eventName, e);
7956             });
7957         },
7958
7959         /**
7960          * Set the opacity of the element
7961          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7962          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7963          * @return {Roo.Element} this
7964          */
7965          setOpacity : function(opacity, animate){
7966             if(!animate || !A){
7967                 var s = this.dom.style;
7968                 if(Roo.isIE){
7969                     s.zoom = 1;
7970                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7971                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7972                 }else{
7973                     s.opacity = opacity;
7974                 }
7975             }else{
7976                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7977             }
7978             return this;
7979         },
7980
7981         /**
7982          * Gets the left X coordinate
7983          * @param {Boolean} local True to get the local css position instead of page coordinate
7984          * @return {Number}
7985          */
7986         getLeft : function(local){
7987             if(!local){
7988                 return this.getX();
7989             }else{
7990                 return parseInt(this.getStyle("left"), 10) || 0;
7991             }
7992         },
7993
7994         /**
7995          * Gets the right X coordinate of the element (element X position + element width)
7996          * @param {Boolean} local True to get the local css position instead of page coordinate
7997          * @return {Number}
7998          */
7999         getRight : function(local){
8000             if(!local){
8001                 return this.getX() + this.getWidth();
8002             }else{
8003                 return (this.getLeft(true) + this.getWidth()) || 0;
8004             }
8005         },
8006
8007         /**
8008          * Gets the top Y coordinate
8009          * @param {Boolean} local True to get the local css position instead of page coordinate
8010          * @return {Number}
8011          */
8012         getTop : function(local) {
8013             if(!local){
8014                 return this.getY();
8015             }else{
8016                 return parseInt(this.getStyle("top"), 10) || 0;
8017             }
8018         },
8019
8020         /**
8021          * Gets the bottom Y coordinate of the element (element Y position + element height)
8022          * @param {Boolean} local True to get the local css position instead of page coordinate
8023          * @return {Number}
8024          */
8025         getBottom : function(local){
8026             if(!local){
8027                 return this.getY() + this.getHeight();
8028             }else{
8029                 return (this.getTop(true) + this.getHeight()) || 0;
8030             }
8031         },
8032
8033         /**
8034         * Initializes positioning on this element. If a desired position is not passed, it will make the
8035         * the element positioned relative IF it is not already positioned.
8036         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8037         * @param {Number} zIndex (optional) The zIndex to apply
8038         * @param {Number} x (optional) Set the page X position
8039         * @param {Number} y (optional) Set the page Y position
8040         */
8041         position : function(pos, zIndex, x, y){
8042             if(!pos){
8043                if(this.getStyle('position') == 'static'){
8044                    this.setStyle('position', 'relative');
8045                }
8046             }else{
8047                 this.setStyle("position", pos);
8048             }
8049             if(zIndex){
8050                 this.setStyle("z-index", zIndex);
8051             }
8052             if(x !== undefined && y !== undefined){
8053                 this.setXY([x, y]);
8054             }else if(x !== undefined){
8055                 this.setX(x);
8056             }else if(y !== undefined){
8057                 this.setY(y);
8058             }
8059         },
8060
8061         /**
8062         * Clear positioning back to the default when the document was loaded
8063         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8064         * @return {Roo.Element} this
8065          */
8066         clearPositioning : function(value){
8067             value = value ||'';
8068             this.setStyle({
8069                 "left": value,
8070                 "right": value,
8071                 "top": value,
8072                 "bottom": value,
8073                 "z-index": "",
8074                 "position" : "static"
8075             });
8076             return this;
8077         },
8078
8079         /**
8080         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8081         * snapshot before performing an update and then restoring the element.
8082         * @return {Object}
8083         */
8084         getPositioning : function(){
8085             var l = this.getStyle("left");
8086             var t = this.getStyle("top");
8087             return {
8088                 "position" : this.getStyle("position"),
8089                 "left" : l,
8090                 "right" : l ? "" : this.getStyle("right"),
8091                 "top" : t,
8092                 "bottom" : t ? "" : this.getStyle("bottom"),
8093                 "z-index" : this.getStyle("z-index")
8094             };
8095         },
8096
8097         /**
8098          * Gets the width of the border(s) for the specified side(s)
8099          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8100          * passing lr would get the border (l)eft width + the border (r)ight width.
8101          * @return {Number} The width of the sides passed added together
8102          */
8103         getBorderWidth : function(side){
8104             return this.addStyles(side, El.borders);
8105         },
8106
8107         /**
8108          * Gets the width of the padding(s) for the specified side(s)
8109          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8110          * passing lr would get the padding (l)eft + the padding (r)ight.
8111          * @return {Number} The padding of the sides passed added together
8112          */
8113         getPadding : function(side){
8114             return this.addStyles(side, El.paddings);
8115         },
8116
8117         /**
8118         * Set positioning with an object returned by getPositioning().
8119         * @param {Object} posCfg
8120         * @return {Roo.Element} this
8121          */
8122         setPositioning : function(pc){
8123             this.applyStyles(pc);
8124             if(pc.right == "auto"){
8125                 this.dom.style.right = "";
8126             }
8127             if(pc.bottom == "auto"){
8128                 this.dom.style.bottom = "";
8129             }
8130             return this;
8131         },
8132
8133         // private
8134         fixDisplay : function(){
8135             if(this.getStyle("display") == "none"){
8136                 this.setStyle("visibility", "hidden");
8137                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8138                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8139                     this.setStyle("display", "block");
8140                 }
8141             }
8142         },
8143
8144         /**
8145          * Quick set left and top adding default units
8146          * @param {String} left The left CSS property value
8147          * @param {String} top The top CSS property value
8148          * @return {Roo.Element} this
8149          */
8150          setLeftTop : function(left, top){
8151             this.dom.style.left = this.addUnits(left);
8152             this.dom.style.top = this.addUnits(top);
8153             return this;
8154         },
8155
8156         /**
8157          * Move this element relative to its current position.
8158          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8159          * @param {Number} distance How far to move the element in pixels
8160          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8161          * @return {Roo.Element} this
8162          */
8163          move : function(direction, distance, animate){
8164             var xy = this.getXY();
8165             direction = direction.toLowerCase();
8166             switch(direction){
8167                 case "l":
8168                 case "left":
8169                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8170                     break;
8171                case "r":
8172                case "right":
8173                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8174                     break;
8175                case "t":
8176                case "top":
8177                case "up":
8178                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8179                     break;
8180                case "b":
8181                case "bottom":
8182                case "down":
8183                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8184                     break;
8185             }
8186             return this;
8187         },
8188
8189         /**
8190          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8191          * @return {Roo.Element} this
8192          */
8193         clip : function(){
8194             if(!this.isClipped){
8195                this.isClipped = true;
8196                this.originalClip = {
8197                    "o": this.getStyle("overflow"),
8198                    "x": this.getStyle("overflow-x"),
8199                    "y": this.getStyle("overflow-y")
8200                };
8201                this.setStyle("overflow", "hidden");
8202                this.setStyle("overflow-x", "hidden");
8203                this.setStyle("overflow-y", "hidden");
8204             }
8205             return this;
8206         },
8207
8208         /**
8209          *  Return clipping (overflow) to original clipping before clip() was called
8210          * @return {Roo.Element} this
8211          */
8212         unclip : function(){
8213             if(this.isClipped){
8214                 this.isClipped = false;
8215                 var o = this.originalClip;
8216                 if(o.o){this.setStyle("overflow", o.o);}
8217                 if(o.x){this.setStyle("overflow-x", o.x);}
8218                 if(o.y){this.setStyle("overflow-y", o.y);}
8219             }
8220             return this;
8221         },
8222
8223
8224         /**
8225          * Gets the x,y coordinates specified by the anchor position on the element.
8226          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8227          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8228          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8229          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8230          * @return {Array} [x, y] An array containing the element's x and y coordinates
8231          */
8232         getAnchorXY : function(anchor, local, s){
8233             //Passing a different size is useful for pre-calculating anchors,
8234             //especially for anchored animations that change the el size.
8235
8236             var w, h, vp = false;
8237             if(!s){
8238                 var d = this.dom;
8239                 if(d == document.body || d == document){
8240                     vp = true;
8241                     w = D.getViewWidth(); h = D.getViewHeight();
8242                 }else{
8243                     w = this.getWidth(); h = this.getHeight();
8244                 }
8245             }else{
8246                 w = s.width;  h = s.height;
8247             }
8248             var x = 0, y = 0, r = Math.round;
8249             switch((anchor || "tl").toLowerCase()){
8250                 case "c":
8251                     x = r(w*.5);
8252                     y = r(h*.5);
8253                 break;
8254                 case "t":
8255                     x = r(w*.5);
8256                     y = 0;
8257                 break;
8258                 case "l":
8259                     x = 0;
8260                     y = r(h*.5);
8261                 break;
8262                 case "r":
8263                     x = w;
8264                     y = r(h*.5);
8265                 break;
8266                 case "b":
8267                     x = r(w*.5);
8268                     y = h;
8269                 break;
8270                 case "tl":
8271                     x = 0;
8272                     y = 0;
8273                 break;
8274                 case "bl":
8275                     x = 0;
8276                     y = h;
8277                 break;
8278                 case "br":
8279                     x = w;
8280                     y = h;
8281                 break;
8282                 case "tr":
8283                     x = w;
8284                     y = 0;
8285                 break;
8286             }
8287             if(local === true){
8288                 return [x, y];
8289             }
8290             if(vp){
8291                 var sc = this.getScroll();
8292                 return [x + sc.left, y + sc.top];
8293             }
8294             //Add the element's offset xy
8295             var o = this.getXY();
8296             return [x+o[0], y+o[1]];
8297         },
8298
8299         /**
8300          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8301          * supported position values.
8302          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8303          * @param {String} position The position to align to.
8304          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8305          * @return {Array} [x, y]
8306          */
8307         getAlignToXY : function(el, p, o){
8308             el = Roo.get(el);
8309             var d = this.dom;
8310             if(!el.dom){
8311                 throw "Element.alignTo with an element that doesn't exist";
8312             }
8313             var c = false; //constrain to viewport
8314             var p1 = "", p2 = "";
8315             o = o || [0,0];
8316
8317             if(!p){
8318                 p = "tl-bl";
8319             }else if(p == "?"){
8320                 p = "tl-bl?";
8321             }else if(p.indexOf("-") == -1){
8322                 p = "tl-" + p;
8323             }
8324             p = p.toLowerCase();
8325             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8326             if(!m){
8327                throw "Element.alignTo with an invalid alignment " + p;
8328             }
8329             p1 = m[1]; p2 = m[2]; c = !!m[3];
8330
8331             //Subtract the aligned el's internal xy from the target's offset xy
8332             //plus custom offset to get the aligned el's new offset xy
8333             var a1 = this.getAnchorXY(p1, true);
8334             var a2 = el.getAnchorXY(p2, false);
8335             var x = a2[0] - a1[0] + o[0];
8336             var y = a2[1] - a1[1] + o[1];
8337             if(c){
8338                 //constrain the aligned el to viewport if necessary
8339                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8340                 // 5px of margin for ie
8341                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8342
8343                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8344                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8345                 //otherwise swap the aligned el to the opposite border of the target.
8346                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8347                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8348                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8349                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8350
8351                var doc = document;
8352                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8353                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8354
8355                if((x+w) > dw + scrollX){
8356                     x = swapX ? r.left-w : dw+scrollX-w;
8357                 }
8358                if(x < scrollX){
8359                    x = swapX ? r.right : scrollX;
8360                }
8361                if((y+h) > dh + scrollY){
8362                     y = swapY ? r.top-h : dh+scrollY-h;
8363                 }
8364                if (y < scrollY){
8365                    y = swapY ? r.bottom : scrollY;
8366                }
8367             }
8368             return [x,y];
8369         },
8370
8371         // private
8372         getConstrainToXY : function(){
8373             var os = {top:0, left:0, bottom:0, right: 0};
8374
8375             return function(el, local, offsets, proposedXY){
8376                 el = Roo.get(el);
8377                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8378
8379                 var vw, vh, vx = 0, vy = 0;
8380                 if(el.dom == document.body || el.dom == document){
8381                     vw = Roo.lib.Dom.getViewWidth();
8382                     vh = Roo.lib.Dom.getViewHeight();
8383                 }else{
8384                     vw = el.dom.clientWidth;
8385                     vh = el.dom.clientHeight;
8386                     if(!local){
8387                         var vxy = el.getXY();
8388                         vx = vxy[0];
8389                         vy = vxy[1];
8390                     }
8391                 }
8392
8393                 var s = el.getScroll();
8394
8395                 vx += offsets.left + s.left;
8396                 vy += offsets.top + s.top;
8397
8398                 vw -= offsets.right;
8399                 vh -= offsets.bottom;
8400
8401                 var vr = vx+vw;
8402                 var vb = vy+vh;
8403
8404                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8405                 var x = xy[0], y = xy[1];
8406                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8407
8408                 // only move it if it needs it
8409                 var moved = false;
8410
8411                 // first validate right/bottom
8412                 if((x + w) > vr){
8413                     x = vr - w;
8414                     moved = true;
8415                 }
8416                 if((y + h) > vb){
8417                     y = vb - h;
8418                     moved = true;
8419                 }
8420                 // then make sure top/left isn't negative
8421                 if(x < vx){
8422                     x = vx;
8423                     moved = true;
8424                 }
8425                 if(y < vy){
8426                     y = vy;
8427                     moved = true;
8428                 }
8429                 return moved ? [x, y] : false;
8430             };
8431         }(),
8432
8433         // private
8434         adjustForConstraints : function(xy, parent, offsets){
8435             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8436         },
8437
8438         /**
8439          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8440          * document it aligns it to the viewport.
8441          * The position parameter is optional, and can be specified in any one of the following formats:
8442          * <ul>
8443          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8444          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8445          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8446          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8447          *   <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
8448          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8449          * </ul>
8450          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8451          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8452          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8453          * that specified in order to enforce the viewport constraints.
8454          * Following are all of the supported anchor positions:
8455     <pre>
8456     Value  Description
8457     -----  -----------------------------
8458     tl     The top left corner (default)
8459     t      The center of the top edge
8460     tr     The top right corner
8461     l      The center of the left edge
8462     c      In the center of the element
8463     r      The center of the right edge
8464     bl     The bottom left corner
8465     b      The center of the bottom edge
8466     br     The bottom right corner
8467     </pre>
8468     Example Usage:
8469     <pre><code>
8470     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8471     el.alignTo("other-el");
8472
8473     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8474     el.alignTo("other-el", "tr?");
8475
8476     // align the bottom right corner of el with the center left edge of other-el
8477     el.alignTo("other-el", "br-l?");
8478
8479     // align the center of el with the bottom left corner of other-el and
8480     // adjust the x position by -6 pixels (and the y position by 0)
8481     el.alignTo("other-el", "c-bl", [-6, 0]);
8482     </code></pre>
8483          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8484          * @param {String} position The position to align to.
8485          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8486          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8487          * @return {Roo.Element} this
8488          */
8489         alignTo : function(element, position, offsets, animate){
8490             var xy = this.getAlignToXY(element, position, offsets);
8491             this.setXY(xy, this.preanim(arguments, 3));
8492             return this;
8493         },
8494
8495         /**
8496          * Anchors an element to another element and realigns it when the window is resized.
8497          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8498          * @param {String} position The position to align to.
8499          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8500          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8501          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8502          * is a number, it is used as the buffer delay (defaults to 50ms).
8503          * @param {Function} callback The function to call after the animation finishes
8504          * @return {Roo.Element} this
8505          */
8506         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8507             var action = function(){
8508                 this.alignTo(el, alignment, offsets, animate);
8509                 Roo.callback(callback, this);
8510             };
8511             Roo.EventManager.onWindowResize(action, this);
8512             var tm = typeof monitorScroll;
8513             if(tm != 'undefined'){
8514                 Roo.EventManager.on(window, 'scroll', action, this,
8515                     {buffer: tm == 'number' ? monitorScroll : 50});
8516             }
8517             action.call(this); // align immediately
8518             return this;
8519         },
8520         /**
8521          * Clears any opacity settings from this element. Required in some cases for IE.
8522          * @return {Roo.Element} this
8523          */
8524         clearOpacity : function(){
8525             if (window.ActiveXObject) {
8526                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8527                     this.dom.style.filter = "";
8528                 }
8529             } else {
8530                 this.dom.style.opacity = "";
8531                 this.dom.style["-moz-opacity"] = "";
8532                 this.dom.style["-khtml-opacity"] = "";
8533             }
8534             return this;
8535         },
8536
8537         /**
8538          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8539          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8540          * @return {Roo.Element} this
8541          */
8542         hide : function(animate){
8543             this.setVisible(false, this.preanim(arguments, 0));
8544             return this;
8545         },
8546
8547         /**
8548         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8549         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8550          * @return {Roo.Element} this
8551          */
8552         show : function(animate){
8553             this.setVisible(true, this.preanim(arguments, 0));
8554             return this;
8555         },
8556
8557         /**
8558          * @private Test if size has a unit, otherwise appends the default
8559          */
8560         addUnits : function(size){
8561             return Roo.Element.addUnits(size, this.defaultUnit);
8562         },
8563
8564         /**
8565          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8566          * @return {Roo.Element} this
8567          */
8568         beginMeasure : function(){
8569             var el = this.dom;
8570             if(el.offsetWidth || el.offsetHeight){
8571                 return this; // offsets work already
8572             }
8573             var changed = [];
8574             var p = this.dom, b = document.body; // start with this element
8575             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8576                 var pe = Roo.get(p);
8577                 if(pe.getStyle('display') == 'none'){
8578                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8579                     p.style.visibility = "hidden";
8580                     p.style.display = "block";
8581                 }
8582                 p = p.parentNode;
8583             }
8584             this._measureChanged = changed;
8585             return this;
8586
8587         },
8588
8589         /**
8590          * Restores displays to before beginMeasure was called
8591          * @return {Roo.Element} this
8592          */
8593         endMeasure : function(){
8594             var changed = this._measureChanged;
8595             if(changed){
8596                 for(var i = 0, len = changed.length; i < len; i++) {
8597                     var r = changed[i];
8598                     r.el.style.visibility = r.visibility;
8599                     r.el.style.display = "none";
8600                 }
8601                 this._measureChanged = null;
8602             }
8603             return this;
8604         },
8605
8606         /**
8607         * Update the innerHTML of this element, optionally searching for and processing scripts
8608         * @param {String} html The new HTML
8609         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8610         * @param {Function} callback For async script loading you can be noticed when the update completes
8611         * @return {Roo.Element} this
8612          */
8613         update : function(html, loadScripts, callback){
8614             if(typeof html == "undefined"){
8615                 html = "";
8616             }
8617             if(loadScripts !== true){
8618                 this.dom.innerHTML = html;
8619                 if(typeof callback == "function"){
8620                     callback();
8621                 }
8622                 return this;
8623             }
8624             var id = Roo.id();
8625             var dom = this.dom;
8626
8627             html += '<span id="' + id + '"></span>';
8628
8629             E.onAvailable(id, function(){
8630                 var hd = document.getElementsByTagName("head")[0];
8631                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8632                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8633                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8634
8635                 var match;
8636                 while(match = re.exec(html)){
8637                     var attrs = match[1];
8638                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8639                     if(srcMatch && srcMatch[2]){
8640                        var s = document.createElement("script");
8641                        s.src = srcMatch[2];
8642                        var typeMatch = attrs.match(typeRe);
8643                        if(typeMatch && typeMatch[2]){
8644                            s.type = typeMatch[2];
8645                        }
8646                        hd.appendChild(s);
8647                     }else if(match[2] && match[2].length > 0){
8648                         if(window.execScript) {
8649                            window.execScript(match[2]);
8650                         } else {
8651                             /**
8652                              * eval:var:id
8653                              * eval:var:dom
8654                              * eval:var:html
8655                              * 
8656                              */
8657                            window.eval(match[2]);
8658                         }
8659                     }
8660                 }
8661                 var el = document.getElementById(id);
8662                 if(el){el.parentNode.removeChild(el);}
8663                 if(typeof callback == "function"){
8664                     callback();
8665                 }
8666             });
8667             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8668             return this;
8669         },
8670
8671         /**
8672          * Direct access to the UpdateManager update() method (takes the same parameters).
8673          * @param {String/Function} url The url for this request or a function to call to get the url
8674          * @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}
8675          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8676          * @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.
8677          * @return {Roo.Element} this
8678          */
8679         load : function(){
8680             var um = this.getUpdateManager();
8681             um.update.apply(um, arguments);
8682             return this;
8683         },
8684
8685         /**
8686         * Gets this element's UpdateManager
8687         * @return {Roo.UpdateManager} The UpdateManager
8688         */
8689         getUpdateManager : function(){
8690             if(!this.updateManager){
8691                 this.updateManager = new Roo.UpdateManager(this);
8692             }
8693             return this.updateManager;
8694         },
8695
8696         /**
8697          * Disables text selection for this element (normalized across browsers)
8698          * @return {Roo.Element} this
8699          */
8700         unselectable : function(){
8701             this.dom.unselectable = "on";
8702             this.swallowEvent("selectstart", true);
8703             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8704             this.addClass("x-unselectable");
8705             return this;
8706         },
8707
8708         /**
8709         * Calculates the x, y to center this element on the screen
8710         * @return {Array} The x, y values [x, y]
8711         */
8712         getCenterXY : function(){
8713             return this.getAlignToXY(document, 'c-c');
8714         },
8715
8716         /**
8717         * Centers the Element in either the viewport, or another Element.
8718         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8719         */
8720         center : function(centerIn){
8721             this.alignTo(centerIn || document, 'c-c');
8722             return this;
8723         },
8724
8725         /**
8726          * Tests various css rules/browsers to determine if this element uses a border box
8727          * @return {Boolean}
8728          */
8729         isBorderBox : function(){
8730             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8731         },
8732
8733         /**
8734          * Return a box {x, y, width, height} that can be used to set another elements
8735          * size/location to match this element.
8736          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8737          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8738          * @return {Object} box An object in the format {x, y, width, height}
8739          */
8740         getBox : function(contentBox, local){
8741             var xy;
8742             if(!local){
8743                 xy = this.getXY();
8744             }else{
8745                 var left = parseInt(this.getStyle("left"), 10) || 0;
8746                 var top = parseInt(this.getStyle("top"), 10) || 0;
8747                 xy = [left, top];
8748             }
8749             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8750             if(!contentBox){
8751                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8752             }else{
8753                 var l = this.getBorderWidth("l")+this.getPadding("l");
8754                 var r = this.getBorderWidth("r")+this.getPadding("r");
8755                 var t = this.getBorderWidth("t")+this.getPadding("t");
8756                 var b = this.getBorderWidth("b")+this.getPadding("b");
8757                 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)};
8758             }
8759             bx.right = bx.x + bx.width;
8760             bx.bottom = bx.y + bx.height;
8761             return bx;
8762         },
8763
8764         /**
8765          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8766          for more information about the sides.
8767          * @param {String} sides
8768          * @return {Number}
8769          */
8770         getFrameWidth : function(sides, onlyContentBox){
8771             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8772         },
8773
8774         /**
8775          * 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.
8776          * @param {Object} box The box to fill {x, y, width, height}
8777          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8778          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8779          * @return {Roo.Element} this
8780          */
8781         setBox : function(box, adjust, animate){
8782             var w = box.width, h = box.height;
8783             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8784                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8785                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8786             }
8787             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8788             return this;
8789         },
8790
8791         /**
8792          * Forces the browser to repaint this element
8793          * @return {Roo.Element} this
8794          */
8795          repaint : function(){
8796             var dom = this.dom;
8797             this.addClass("x-repaint");
8798             setTimeout(function(){
8799                 Roo.get(dom).removeClass("x-repaint");
8800             }, 1);
8801             return this;
8802         },
8803
8804         /**
8805          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8806          * then it returns the calculated width of the sides (see getPadding)
8807          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8808          * @return {Object/Number}
8809          */
8810         getMargins : function(side){
8811             if(!side){
8812                 return {
8813                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8814                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8815                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8816                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8817                 };
8818             }else{
8819                 return this.addStyles(side, El.margins);
8820              }
8821         },
8822
8823         // private
8824         addStyles : function(sides, styles){
8825             var val = 0, v, w;
8826             for(var i = 0, len = sides.length; i < len; i++){
8827                 v = this.getStyle(styles[sides.charAt(i)]);
8828                 if(v){
8829                      w = parseInt(v, 10);
8830                      if(w){ val += w; }
8831                 }
8832             }
8833             return val;
8834         },
8835
8836         /**
8837          * Creates a proxy element of this element
8838          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8839          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8840          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8841          * @return {Roo.Element} The new proxy element
8842          */
8843         createProxy : function(config, renderTo, matchBox){
8844             if(renderTo){
8845                 renderTo = Roo.getDom(renderTo);
8846             }else{
8847                 renderTo = document.body;
8848             }
8849             config = typeof config == "object" ?
8850                 config : {tag : "div", cls: config};
8851             var proxy = Roo.DomHelper.append(renderTo, config, true);
8852             if(matchBox){
8853                proxy.setBox(this.getBox());
8854             }
8855             return proxy;
8856         },
8857
8858         /**
8859          * Puts a mask over this element to disable user interaction. Requires core.css.
8860          * This method can only be applied to elements which accept child nodes.
8861          * @param {String} msg (optional) A message to display in the mask
8862          * @param {String} msgCls (optional) A css class to apply to the msg element
8863          * @return {Element} The mask  element
8864          */
8865         mask : function(msg, msgCls)
8866         {
8867             if(this.getStyle("position") == "static"){
8868                 this.setStyle("position", "relative");
8869             }
8870             if(!this._mask){
8871                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8872             }
8873             this.addClass("x-masked");
8874             this._mask.setDisplayed(true);
8875             
8876             // we wander
8877             var z = 0;
8878             var dom = this.dom
8879             while (dom && dom.style) {
8880                 if (!isNaN(parseInt(dom.style.zIndex))) {
8881                     z = Math.max(z, parseInt(dom.style.zIndex));
8882                 }
8883                 dom = dom.parentNode;
8884             }
8885             // if we are masking the body - then it hides everything..
8886             if (this.dom == document.body) {
8887                 z = 1000000;
8888                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8889                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8890             }
8891            
8892             if(typeof msg == 'string'){
8893                 if(!this._maskMsg){
8894                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8895                 }
8896                 var mm = this._maskMsg;
8897                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8898                 mm.dom.firstChild.innerHTML = msg;
8899                 mm.setDisplayed(true);
8900                 mm.center(this);
8901                 mm.setStyle('z-index', z + 102);
8902             }
8903             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8904                 this._mask.setHeight(this.getHeight());
8905             }
8906             this._mask.setStyle('z-index', z + 100);
8907             
8908             return this._mask;
8909         },
8910
8911         /**
8912          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8913          * it is cached for reuse.
8914          */
8915         unmask : function(removeEl){
8916             if(this._mask){
8917                 if(removeEl === true){
8918                     this._mask.remove();
8919                     delete this._mask;
8920                     if(this._maskMsg){
8921                         this._maskMsg.remove();
8922                         delete this._maskMsg;
8923                     }
8924                 }else{
8925                     this._mask.setDisplayed(false);
8926                     if(this._maskMsg){
8927                         this._maskMsg.setDisplayed(false);
8928                     }
8929                 }
8930             }
8931             this.removeClass("x-masked");
8932         },
8933
8934         /**
8935          * Returns true if this element is masked
8936          * @return {Boolean}
8937          */
8938         isMasked : function(){
8939             return this._mask && this._mask.isVisible();
8940         },
8941
8942         /**
8943          * Creates an iframe shim for this element to keep selects and other windowed objects from
8944          * showing through.
8945          * @return {Roo.Element} The new shim element
8946          */
8947         createShim : function(){
8948             var el = document.createElement('iframe');
8949             el.frameBorder = 'no';
8950             el.className = 'roo-shim';
8951             if(Roo.isIE && Roo.isSecure){
8952                 el.src = Roo.SSL_SECURE_URL;
8953             }
8954             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8955             shim.autoBoxAdjust = false;
8956             return shim;
8957         },
8958
8959         /**
8960          * Removes this element from the DOM and deletes it from the cache
8961          */
8962         remove : function(){
8963             if(this.dom.parentNode){
8964                 this.dom.parentNode.removeChild(this.dom);
8965             }
8966             delete El.cache[this.dom.id];
8967         },
8968
8969         /**
8970          * Sets up event handlers to add and remove a css class when the mouse is over this element
8971          * @param {String} className
8972          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8973          * mouseout events for children elements
8974          * @return {Roo.Element} this
8975          */
8976         addClassOnOver : function(className, preventFlicker){
8977             this.on("mouseover", function(){
8978                 Roo.fly(this, '_internal').addClass(className);
8979             }, this.dom);
8980             var removeFn = function(e){
8981                 if(preventFlicker !== true || !e.within(this, true)){
8982                     Roo.fly(this, '_internal').removeClass(className);
8983                 }
8984             };
8985             this.on("mouseout", removeFn, this.dom);
8986             return this;
8987         },
8988
8989         /**
8990          * Sets up event handlers to add and remove a css class when this element has the focus
8991          * @param {String} className
8992          * @return {Roo.Element} this
8993          */
8994         addClassOnFocus : function(className){
8995             this.on("focus", function(){
8996                 Roo.fly(this, '_internal').addClass(className);
8997             }, this.dom);
8998             this.on("blur", function(){
8999                 Roo.fly(this, '_internal').removeClass(className);
9000             }, this.dom);
9001             return this;
9002         },
9003         /**
9004          * 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)
9005          * @param {String} className
9006          * @return {Roo.Element} this
9007          */
9008         addClassOnClick : function(className){
9009             var dom = this.dom;
9010             this.on("mousedown", function(){
9011                 Roo.fly(dom, '_internal').addClass(className);
9012                 var d = Roo.get(document);
9013                 var fn = function(){
9014                     Roo.fly(dom, '_internal').removeClass(className);
9015                     d.removeListener("mouseup", fn);
9016                 };
9017                 d.on("mouseup", fn);
9018             });
9019             return this;
9020         },
9021
9022         /**
9023          * Stops the specified event from bubbling and optionally prevents the default action
9024          * @param {String} eventName
9025          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9026          * @return {Roo.Element} this
9027          */
9028         swallowEvent : function(eventName, preventDefault){
9029             var fn = function(e){
9030                 e.stopPropagation();
9031                 if(preventDefault){
9032                     e.preventDefault();
9033                 }
9034             };
9035             if(eventName instanceof Array){
9036                 for(var i = 0, len = eventName.length; i < len; i++){
9037                      this.on(eventName[i], fn);
9038                 }
9039                 return this;
9040             }
9041             this.on(eventName, fn);
9042             return this;
9043         },
9044
9045         /**
9046          * @private
9047          */
9048       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9049
9050         /**
9051          * Sizes this element to its parent element's dimensions performing
9052          * neccessary box adjustments.
9053          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9054          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9055          * @return {Roo.Element} this
9056          */
9057         fitToParent : function(monitorResize, targetParent) {
9058           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9059           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9060           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9061             return;
9062           }
9063           var p = Roo.get(targetParent || this.dom.parentNode);
9064           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9065           if (monitorResize === true) {
9066             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9067             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9068           }
9069           return this;
9070         },
9071
9072         /**
9073          * Gets the next sibling, skipping text nodes
9074          * @return {HTMLElement} The next sibling or null
9075          */
9076         getNextSibling : function(){
9077             var n = this.dom.nextSibling;
9078             while(n && n.nodeType != 1){
9079                 n = n.nextSibling;
9080             }
9081             return n;
9082         },
9083
9084         /**
9085          * Gets the previous sibling, skipping text nodes
9086          * @return {HTMLElement} The previous sibling or null
9087          */
9088         getPrevSibling : function(){
9089             var n = this.dom.previousSibling;
9090             while(n && n.nodeType != 1){
9091                 n = n.previousSibling;
9092             }
9093             return n;
9094         },
9095
9096
9097         /**
9098          * Appends the passed element(s) to this element
9099          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9100          * @return {Roo.Element} this
9101          */
9102         appendChild: function(el){
9103             el = Roo.get(el);
9104             el.appendTo(this);
9105             return this;
9106         },
9107
9108         /**
9109          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9110          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9111          * automatically generated with the specified attributes.
9112          * @param {HTMLElement} insertBefore (optional) a child element of this element
9113          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9114          * @return {Roo.Element} The new child element
9115          */
9116         createChild: function(config, insertBefore, returnDom){
9117             config = config || {tag:'div'};
9118             if(insertBefore){
9119                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9120             }
9121             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9122         },
9123
9124         /**
9125          * Appends this element to the passed element
9126          * @param {String/HTMLElement/Element} el The new parent element
9127          * @return {Roo.Element} this
9128          */
9129         appendTo: function(el){
9130             el = Roo.getDom(el);
9131             el.appendChild(this.dom);
9132             return this;
9133         },
9134
9135         /**
9136          * Inserts this element before the passed element in the DOM
9137          * @param {String/HTMLElement/Element} el The element to insert before
9138          * @return {Roo.Element} this
9139          */
9140         insertBefore: function(el){
9141             el = Roo.getDom(el);
9142             el.parentNode.insertBefore(this.dom, el);
9143             return this;
9144         },
9145
9146         /**
9147          * Inserts this element after the passed element in the DOM
9148          * @param {String/HTMLElement/Element} el The element to insert after
9149          * @return {Roo.Element} this
9150          */
9151         insertAfter: function(el){
9152             el = Roo.getDom(el);
9153             el.parentNode.insertBefore(this.dom, el.nextSibling);
9154             return this;
9155         },
9156
9157         /**
9158          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9159          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9160          * @return {Roo.Element} The new child
9161          */
9162         insertFirst: function(el, returnDom){
9163             el = el || {};
9164             if(typeof el == 'object' && !el.nodeType){ // dh config
9165                 return this.createChild(el, this.dom.firstChild, returnDom);
9166             }else{
9167                 el = Roo.getDom(el);
9168                 this.dom.insertBefore(el, this.dom.firstChild);
9169                 return !returnDom ? Roo.get(el) : el;
9170             }
9171         },
9172
9173         /**
9174          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9175          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9176          * @param {String} where (optional) 'before' or 'after' defaults to before
9177          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9178          * @return {Roo.Element} the inserted Element
9179          */
9180         insertSibling: function(el, where, returnDom){
9181             where = where ? where.toLowerCase() : 'before';
9182             el = el || {};
9183             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9184
9185             if(typeof el == 'object' && !el.nodeType){ // dh config
9186                 if(where == 'after' && !this.dom.nextSibling){
9187                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9188                 }else{
9189                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9190                 }
9191
9192             }else{
9193                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9194                             where == 'before' ? this.dom : this.dom.nextSibling);
9195                 if(!returnDom){
9196                     rt = Roo.get(rt);
9197                 }
9198             }
9199             return rt;
9200         },
9201
9202         /**
9203          * Creates and wraps this element with another element
9204          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9205          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9206          * @return {HTMLElement/Element} The newly created wrapper element
9207          */
9208         wrap: function(config, returnDom){
9209             if(!config){
9210                 config = {tag: "div"};
9211             }
9212             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9213             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9214             return newEl;
9215         },
9216
9217         /**
9218          * Replaces the passed element with this element
9219          * @param {String/HTMLElement/Element} el The element to replace
9220          * @return {Roo.Element} this
9221          */
9222         replace: function(el){
9223             el = Roo.get(el);
9224             this.insertBefore(el);
9225             el.remove();
9226             return this;
9227         },
9228
9229         /**
9230          * Inserts an html fragment into this element
9231          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9232          * @param {String} html The HTML fragment
9233          * @param {Boolean} returnEl True to return an Roo.Element
9234          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9235          */
9236         insertHtml : function(where, html, returnEl){
9237             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9238             return returnEl ? Roo.get(el) : el;
9239         },
9240
9241         /**
9242          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9243          * @param {Object} o The object with the attributes
9244          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9245          * @return {Roo.Element} this
9246          */
9247         set : function(o, useSet){
9248             var el = this.dom;
9249             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9250             for(var attr in o){
9251                 if(attr == "style" || typeof o[attr] == "function") continue;
9252                 if(attr=="cls"){
9253                     el.className = o["cls"];
9254                 }else{
9255                     if(useSet) el.setAttribute(attr, o[attr]);
9256                     else el[attr] = o[attr];
9257                 }
9258             }
9259             if(o.style){
9260                 Roo.DomHelper.applyStyles(el, o.style);
9261             }
9262             return this;
9263         },
9264
9265         /**
9266          * Convenience method for constructing a KeyMap
9267          * @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:
9268          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9269          * @param {Function} fn The function to call
9270          * @param {Object} scope (optional) The scope of the function
9271          * @return {Roo.KeyMap} The KeyMap created
9272          */
9273         addKeyListener : function(key, fn, scope){
9274             var config;
9275             if(typeof key != "object" || key instanceof Array){
9276                 config = {
9277                     key: key,
9278                     fn: fn,
9279                     scope: scope
9280                 };
9281             }else{
9282                 config = {
9283                     key : key.key,
9284                     shift : key.shift,
9285                     ctrl : key.ctrl,
9286                     alt : key.alt,
9287                     fn: fn,
9288                     scope: scope
9289                 };
9290             }
9291             return new Roo.KeyMap(this, config);
9292         },
9293
9294         /**
9295          * Creates a KeyMap for this element
9296          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9297          * @return {Roo.KeyMap} The KeyMap created
9298          */
9299         addKeyMap : function(config){
9300             return new Roo.KeyMap(this, config);
9301         },
9302
9303         /**
9304          * Returns true if this element is scrollable.
9305          * @return {Boolean}
9306          */
9307          isScrollable : function(){
9308             var dom = this.dom;
9309             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9310         },
9311
9312         /**
9313          * 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().
9314          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9315          * @param {Number} value The new scroll value
9316          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9317          * @return {Element} this
9318          */
9319
9320         scrollTo : function(side, value, animate){
9321             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9322             if(!animate || !A){
9323                 this.dom[prop] = value;
9324             }else{
9325                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9326                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9327             }
9328             return this;
9329         },
9330
9331         /**
9332          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9333          * within this element's scrollable range.
9334          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9335          * @param {Number} distance How far to scroll the element in pixels
9336          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9337          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9338          * was scrolled as far as it could go.
9339          */
9340          scroll : function(direction, distance, animate){
9341              if(!this.isScrollable()){
9342                  return;
9343              }
9344              var el = this.dom;
9345              var l = el.scrollLeft, t = el.scrollTop;
9346              var w = el.scrollWidth, h = el.scrollHeight;
9347              var cw = el.clientWidth, ch = el.clientHeight;
9348              direction = direction.toLowerCase();
9349              var scrolled = false;
9350              var a = this.preanim(arguments, 2);
9351              switch(direction){
9352                  case "l":
9353                  case "left":
9354                      if(w - l > cw){
9355                          var v = Math.min(l + distance, w-cw);
9356                          this.scrollTo("left", v, a);
9357                          scrolled = true;
9358                      }
9359                      break;
9360                 case "r":
9361                 case "right":
9362                      if(l > 0){
9363                          var v = Math.max(l - distance, 0);
9364                          this.scrollTo("left", v, a);
9365                          scrolled = true;
9366                      }
9367                      break;
9368                 case "t":
9369                 case "top":
9370                 case "up":
9371                      if(t > 0){
9372                          var v = Math.max(t - distance, 0);
9373                          this.scrollTo("top", v, a);
9374                          scrolled = true;
9375                      }
9376                      break;
9377                 case "b":
9378                 case "bottom":
9379                 case "down":
9380                      if(h - t > ch){
9381                          var v = Math.min(t + distance, h-ch);
9382                          this.scrollTo("top", v, a);
9383                          scrolled = true;
9384                      }
9385                      break;
9386              }
9387              return scrolled;
9388         },
9389
9390         /**
9391          * Translates the passed page coordinates into left/top css values for this element
9392          * @param {Number/Array} x The page x or an array containing [x, y]
9393          * @param {Number} y The page y
9394          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9395          */
9396         translatePoints : function(x, y){
9397             if(typeof x == 'object' || x instanceof Array){
9398                 y = x[1]; x = x[0];
9399             }
9400             var p = this.getStyle('position');
9401             var o = this.getXY();
9402
9403             var l = parseInt(this.getStyle('left'), 10);
9404             var t = parseInt(this.getStyle('top'), 10);
9405
9406             if(isNaN(l)){
9407                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9408             }
9409             if(isNaN(t)){
9410                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9411             }
9412
9413             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9414         },
9415
9416         /**
9417          * Returns the current scroll position of the element.
9418          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9419          */
9420         getScroll : function(){
9421             var d = this.dom, doc = document;
9422             if(d == doc || d == doc.body){
9423                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9424                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9425                 return {left: l, top: t};
9426             }else{
9427                 return {left: d.scrollLeft, top: d.scrollTop};
9428             }
9429         },
9430
9431         /**
9432          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9433          * are convert to standard 6 digit hex color.
9434          * @param {String} attr The css attribute
9435          * @param {String} defaultValue The default value to use when a valid color isn't found
9436          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9437          * YUI color anims.
9438          */
9439         getColor : function(attr, defaultValue, prefix){
9440             var v = this.getStyle(attr);
9441             if(!v || v == "transparent" || v == "inherit") {
9442                 return defaultValue;
9443             }
9444             var color = typeof prefix == "undefined" ? "#" : prefix;
9445             if(v.substr(0, 4) == "rgb("){
9446                 var rvs = v.slice(4, v.length -1).split(",");
9447                 for(var i = 0; i < 3; i++){
9448                     var h = parseInt(rvs[i]).toString(16);
9449                     if(h < 16){
9450                         h = "0" + h;
9451                     }
9452                     color += h;
9453                 }
9454             } else {
9455                 if(v.substr(0, 1) == "#"){
9456                     if(v.length == 4) {
9457                         for(var i = 1; i < 4; i++){
9458                             var c = v.charAt(i);
9459                             color +=  c + c;
9460                         }
9461                     }else if(v.length == 7){
9462                         color += v.substr(1);
9463                     }
9464                 }
9465             }
9466             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9467         },
9468
9469         /**
9470          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9471          * gradient background, rounded corners and a 4-way shadow.
9472          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9473          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9474          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9475          * @return {Roo.Element} this
9476          */
9477         boxWrap : function(cls){
9478             cls = cls || 'x-box';
9479             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9480             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9481             return el;
9482         },
9483
9484         /**
9485          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9486          * @param {String} namespace The namespace in which to look for the attribute
9487          * @param {String} name The attribute name
9488          * @return {String} The attribute value
9489          */
9490         getAttributeNS : Roo.isIE ? function(ns, name){
9491             var d = this.dom;
9492             var type = typeof d[ns+":"+name];
9493             if(type != 'undefined' && type != 'unknown'){
9494                 return d[ns+":"+name];
9495             }
9496             return d[name];
9497         } : function(ns, name){
9498             var d = this.dom;
9499             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9500         }
9501     };
9502
9503     var ep = El.prototype;
9504
9505     /**
9506      * Appends an event handler (Shorthand for addListener)
9507      * @param {String}   eventName     The type of event to append
9508      * @param {Function} fn        The method the event invokes
9509      * @param {Object} scope       (optional) The scope (this object) of the fn
9510      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9511      * @method
9512      */
9513     ep.on = ep.addListener;
9514         // backwards compat
9515     ep.mon = ep.addListener;
9516
9517     /**
9518      * Removes an event handler from this element (shorthand for removeListener)
9519      * @param {String} eventName the type of event to remove
9520      * @param {Function} fn the method the event invokes
9521      * @return {Roo.Element} this
9522      * @method
9523      */
9524     ep.un = ep.removeListener;
9525
9526     /**
9527      * true to automatically adjust width and height settings for box-model issues (default to true)
9528      */
9529     ep.autoBoxAdjust = true;
9530
9531     // private
9532     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9533
9534     // private
9535     El.addUnits = function(v, defaultUnit){
9536         if(v === "" || v == "auto"){
9537             return v;
9538         }
9539         if(v === undefined){
9540             return '';
9541         }
9542         if(typeof v == "number" || !El.unitPattern.test(v)){
9543             return v + (defaultUnit || 'px');
9544         }
9545         return v;
9546     };
9547
9548     // special markup used throughout Roo when box wrapping elements
9549     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>';
9550     /**
9551      * Visibility mode constant - Use visibility to hide element
9552      * @static
9553      * @type Number
9554      */
9555     El.VISIBILITY = 1;
9556     /**
9557      * Visibility mode constant - Use display to hide element
9558      * @static
9559      * @type Number
9560      */
9561     El.DISPLAY = 2;
9562
9563     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9564     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9565     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9566
9567
9568
9569     /**
9570      * @private
9571      */
9572     El.cache = {};
9573
9574     var docEl;
9575
9576     /**
9577      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9578      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9579      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9580      * @return {Element} The Element object
9581      * @static
9582      */
9583     El.get = function(el){
9584         var ex, elm, id;
9585         if(!el){ return null; }
9586         if(typeof el == "string"){ // element id
9587             if(!(elm = document.getElementById(el))){
9588                 return null;
9589             }
9590             if(ex = El.cache[el]){
9591                 ex.dom = elm;
9592             }else{
9593                 ex = El.cache[el] = new El(elm);
9594             }
9595             return ex;
9596         }else if(el.tagName){ // dom element
9597             if(!(id = el.id)){
9598                 id = Roo.id(el);
9599             }
9600             if(ex = El.cache[id]){
9601                 ex.dom = el;
9602             }else{
9603                 ex = El.cache[id] = new El(el);
9604             }
9605             return ex;
9606         }else if(el instanceof El){
9607             if(el != docEl){
9608                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9609                                                               // catch case where it hasn't been appended
9610                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9611             }
9612             return el;
9613         }else if(el.isComposite){
9614             return el;
9615         }else if(el instanceof Array){
9616             return El.select(el);
9617         }else if(el == document){
9618             // create a bogus element object representing the document object
9619             if(!docEl){
9620                 var f = function(){};
9621                 f.prototype = El.prototype;
9622                 docEl = new f();
9623                 docEl.dom = document;
9624             }
9625             return docEl;
9626         }
9627         return null;
9628     };
9629
9630     // private
9631     El.uncache = function(el){
9632         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9633             if(a[i]){
9634                 delete El.cache[a[i].id || a[i]];
9635             }
9636         }
9637     };
9638
9639     // private
9640     // Garbage collection - uncache elements/purge listeners on orphaned elements
9641     // so we don't hold a reference and cause the browser to retain them
9642     El.garbageCollect = function(){
9643         if(!Roo.enableGarbageCollector){
9644             clearInterval(El.collectorThread);
9645             return;
9646         }
9647         for(var eid in El.cache){
9648             var el = El.cache[eid], d = el.dom;
9649             // -------------------------------------------------------
9650             // Determining what is garbage:
9651             // -------------------------------------------------------
9652             // !d
9653             // dom node is null, definitely garbage
9654             // -------------------------------------------------------
9655             // !d.parentNode
9656             // no parentNode == direct orphan, definitely garbage
9657             // -------------------------------------------------------
9658             // !d.offsetParent && !document.getElementById(eid)
9659             // display none elements have no offsetParent so we will
9660             // also try to look it up by it's id. However, check
9661             // offsetParent first so we don't do unneeded lookups.
9662             // This enables collection of elements that are not orphans
9663             // directly, but somewhere up the line they have an orphan
9664             // parent.
9665             // -------------------------------------------------------
9666             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9667                 delete El.cache[eid];
9668                 if(d && Roo.enableListenerCollection){
9669                     E.purgeElement(d);
9670                 }
9671             }
9672         }
9673     }
9674     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9675
9676
9677     // dom is optional
9678     El.Flyweight = function(dom){
9679         this.dom = dom;
9680     };
9681     El.Flyweight.prototype = El.prototype;
9682
9683     El._flyweights = {};
9684     /**
9685      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9686      * the dom node can be overwritten by other code.
9687      * @param {String/HTMLElement} el The dom node or id
9688      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9689      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9690      * @static
9691      * @return {Element} The shared Element object
9692      */
9693     El.fly = function(el, named){
9694         named = named || '_global';
9695         el = Roo.getDom(el);
9696         if(!el){
9697             return null;
9698         }
9699         if(!El._flyweights[named]){
9700             El._flyweights[named] = new El.Flyweight();
9701         }
9702         El._flyweights[named].dom = el;
9703         return El._flyweights[named];
9704     };
9705
9706     /**
9707      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9708      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9709      * Shorthand of {@link Roo.Element#get}
9710      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9711      * @return {Element} The Element object
9712      * @member Roo
9713      * @method get
9714      */
9715     Roo.get = El.get;
9716     /**
9717      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9718      * the dom node can be overwritten by other code.
9719      * Shorthand of {@link Roo.Element#fly}
9720      * @param {String/HTMLElement} el The dom node or id
9721      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9722      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9723      * @static
9724      * @return {Element} The shared Element object
9725      * @member Roo
9726      * @method fly
9727      */
9728     Roo.fly = El.fly;
9729
9730     // speedy lookup for elements never to box adjust
9731     var noBoxAdjust = Roo.isStrict ? {
9732         select:1
9733     } : {
9734         input:1, select:1, textarea:1
9735     };
9736     if(Roo.isIE || Roo.isGecko){
9737         noBoxAdjust['button'] = 1;
9738     }
9739
9740
9741     Roo.EventManager.on(window, 'unload', function(){
9742         delete El.cache;
9743         delete El._flyweights;
9744     });
9745 })();
9746
9747
9748
9749
9750 if(Roo.DomQuery){
9751     Roo.Element.selectorFunction = Roo.DomQuery.select;
9752 }
9753
9754 Roo.Element.select = function(selector, unique, root){
9755     var els;
9756     if(typeof selector == "string"){
9757         els = Roo.Element.selectorFunction(selector, root);
9758     }else if(selector.length !== undefined){
9759         els = selector;
9760     }else{
9761         throw "Invalid selector";
9762     }
9763     if(unique === true){
9764         return new Roo.CompositeElement(els);
9765     }else{
9766         return new Roo.CompositeElementLite(els);
9767     }
9768 };
9769 /**
9770  * Selects elements based on the passed CSS selector to enable working on them as 1.
9771  * @param {String/Array} selector The CSS selector or an array of elements
9772  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9773  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9774  * @return {CompositeElementLite/CompositeElement}
9775  * @member Roo
9776  * @method select
9777  */
9778 Roo.select = Roo.Element.select;
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793 /*
9794  * Based on:
9795  * Ext JS Library 1.1.1
9796  * Copyright(c) 2006-2007, Ext JS, LLC.
9797  *
9798  * Originally Released Under LGPL - original licence link has changed is not relivant.
9799  *
9800  * Fork - LGPL
9801  * <script type="text/javascript">
9802  */
9803
9804
9805
9806 //Notifies Element that fx methods are available
9807 Roo.enableFx = true;
9808
9809 /**
9810  * @class Roo.Fx
9811  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9812  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9813  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9814  * Element effects to work.</p><br/>
9815  *
9816  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9817  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9818  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9819  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9820  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9821  * expected results and should be done with care.</p><br/>
9822  *
9823  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9824  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9825 <pre>
9826 Value  Description
9827 -----  -----------------------------
9828 tl     The top left corner
9829 t      The center of the top edge
9830 tr     The top right corner
9831 l      The center of the left edge
9832 r      The center of the right edge
9833 bl     The bottom left corner
9834 b      The center of the bottom edge
9835 br     The bottom right corner
9836 </pre>
9837  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9838  * below are common options that can be passed to any Fx method.</b>
9839  * @cfg {Function} callback A function called when the effect is finished
9840  * @cfg {Object} scope The scope of the effect function
9841  * @cfg {String} easing A valid Easing value for the effect
9842  * @cfg {String} afterCls A css class to apply after the effect
9843  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9844  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9845  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9846  * effects that end with the element being visually hidden, ignored otherwise)
9847  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9848  * a function which returns such a specification that will be applied to the Element after the effect finishes
9849  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9850  * @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
9851  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9852  */
9853 Roo.Fx = {
9854         /**
9855          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9856          * origin for the slide effect.  This function automatically handles wrapping the element with
9857          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9858          * Usage:
9859          *<pre><code>
9860 // default: slide the element in from the top
9861 el.slideIn();
9862
9863 // custom: slide the element in from the right with a 2-second duration
9864 el.slideIn('r', { duration: 2 });
9865
9866 // common config options shown with default values
9867 el.slideIn('t', {
9868     easing: 'easeOut',
9869     duration: .5
9870 });
9871 </code></pre>
9872          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9873          * @param {Object} options (optional) Object literal with any of the Fx config options
9874          * @return {Roo.Element} The Element
9875          */
9876     slideIn : function(anchor, o){
9877         var el = this.getFxEl();
9878         o = o || {};
9879
9880         el.queueFx(o, function(){
9881
9882             anchor = anchor || "t";
9883
9884             // fix display to visibility
9885             this.fixDisplay();
9886
9887             // restore values after effect
9888             var r = this.getFxRestore();
9889             var b = this.getBox();
9890             // fixed size for slide
9891             this.setSize(b);
9892
9893             // wrap if needed
9894             var wrap = this.fxWrap(r.pos, o, "hidden");
9895
9896             var st = this.dom.style;
9897             st.visibility = "visible";
9898             st.position = "absolute";
9899
9900             // clear out temp styles after slide and unwrap
9901             var after = function(){
9902                 el.fxUnwrap(wrap, r.pos, o);
9903                 st.width = r.width;
9904                 st.height = r.height;
9905                 el.afterFx(o);
9906             };
9907             // time to calc the positions
9908             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9909
9910             switch(anchor.toLowerCase()){
9911                 case "t":
9912                     wrap.setSize(b.width, 0);
9913                     st.left = st.bottom = "0";
9914                     a = {height: bh};
9915                 break;
9916                 case "l":
9917                     wrap.setSize(0, b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw};
9920                 break;
9921                 case "r":
9922                     wrap.setSize(0, b.height);
9923                     wrap.setX(b.right);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, points: pt};
9926                 break;
9927                 case "b":
9928                     wrap.setSize(b.width, 0);
9929                     wrap.setY(b.bottom);
9930                     st.left = st.top = "0";
9931                     a = {height: bh, points: pt};
9932                 break;
9933                 case "tl":
9934                     wrap.setSize(0, 0);
9935                     st.right = st.bottom = "0";
9936                     a = {width: bw, height: bh};
9937                 break;
9938                 case "bl":
9939                     wrap.setSize(0, 0);
9940                     wrap.setY(b.y+b.height);
9941                     st.right = st.top = "0";
9942                     a = {width: bw, height: bh, points: pt};
9943                 break;
9944                 case "br":
9945                     wrap.setSize(0, 0);
9946                     wrap.setXY([b.right, b.bottom]);
9947                     st.left = st.top = "0";
9948                     a = {width: bw, height: bh, points: pt};
9949                 break;
9950                 case "tr":
9951                     wrap.setSize(0, 0);
9952                     wrap.setX(b.x+b.width);
9953                     st.left = st.bottom = "0";
9954                     a = {width: bw, height: bh, points: pt};
9955                 break;
9956             }
9957             this.dom.style.visibility = "visible";
9958             wrap.show();
9959
9960             arguments.callee.anim = wrap.fxanim(a,
9961                 o,
9962                 'motion',
9963                 .5,
9964                 'easeOut', after);
9965         });
9966         return this;
9967     },
9968     
9969         /**
9970          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9971          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9972          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9973          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9974          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9975          * Usage:
9976          *<pre><code>
9977 // default: slide the element out to the top
9978 el.slideOut();
9979
9980 // custom: slide the element out to the right with a 2-second duration
9981 el.slideOut('r', { duration: 2 });
9982
9983 // common config options shown with default values
9984 el.slideOut('t', {
9985     easing: 'easeOut',
9986     duration: .5,
9987     remove: false,
9988     useDisplay: false
9989 });
9990 </code></pre>
9991          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9992          * @param {Object} options (optional) Object literal with any of the Fx config options
9993          * @return {Roo.Element} The Element
9994          */
9995     slideOut : function(anchor, o){
9996         var el = this.getFxEl();
9997         o = o || {};
9998
9999         el.queueFx(o, function(){
10000
10001             anchor = anchor || "t";
10002
10003             // restore values after effect
10004             var r = this.getFxRestore();
10005             
10006             var b = this.getBox();
10007             // fixed size for slide
10008             this.setSize(b);
10009
10010             // wrap if needed
10011             var wrap = this.fxWrap(r.pos, o, "visible");
10012
10013             var st = this.dom.style;
10014             st.visibility = "visible";
10015             st.position = "absolute";
10016
10017             wrap.setSize(b);
10018
10019             var after = function(){
10020                 if(o.useDisplay){
10021                     el.setDisplayed(false);
10022                 }else{
10023                     el.hide();
10024                 }
10025
10026                 el.fxUnwrap(wrap, r.pos, o);
10027
10028                 st.width = r.width;
10029                 st.height = r.height;
10030
10031                 el.afterFx(o);
10032             };
10033
10034             var a, zero = {to: 0};
10035             switch(anchor.toLowerCase()){
10036                 case "t":
10037                     st.left = st.bottom = "0";
10038                     a = {height: zero};
10039                 break;
10040                 case "l":
10041                     st.right = st.top = "0";
10042                     a = {width: zero};
10043                 break;
10044                 case "r":
10045                     st.left = st.top = "0";
10046                     a = {width: zero, points: {to:[b.right, b.y]}};
10047                 break;
10048                 case "b":
10049                     st.left = st.top = "0";
10050                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10051                 break;
10052                 case "tl":
10053                     st.right = st.bottom = "0";
10054                     a = {width: zero, height: zero};
10055                 break;
10056                 case "bl":
10057                     st.right = st.top = "0";
10058                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10059                 break;
10060                 case "br":
10061                     st.left = st.top = "0";
10062                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10063                 break;
10064                 case "tr":
10065                     st.left = st.bottom = "0";
10066                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10067                 break;
10068             }
10069
10070             arguments.callee.anim = wrap.fxanim(a,
10071                 o,
10072                 'motion',
10073                 .5,
10074                 "easeOut", after);
10075         });
10076         return this;
10077     },
10078
10079         /**
10080          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10081          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10082          * The element must be removed from the DOM using the 'remove' config option if desired.
10083          * Usage:
10084          *<pre><code>
10085 // default
10086 el.puff();
10087
10088 // common config options shown with default values
10089 el.puff({
10090     easing: 'easeOut',
10091     duration: .5,
10092     remove: false,
10093     useDisplay: false
10094 });
10095 </code></pre>
10096          * @param {Object} options (optional) Object literal with any of the Fx config options
10097          * @return {Roo.Element} The Element
10098          */
10099     puff : function(o){
10100         var el = this.getFxEl();
10101         o = o || {};
10102
10103         el.queueFx(o, function(){
10104             this.clearOpacity();
10105             this.show();
10106
10107             // restore values after effect
10108             var r = this.getFxRestore();
10109             var st = this.dom.style;
10110
10111             var after = function(){
10112                 if(o.useDisplay){
10113                     el.setDisplayed(false);
10114                 }else{
10115                     el.hide();
10116                 }
10117
10118                 el.clearOpacity();
10119
10120                 el.setPositioning(r.pos);
10121                 st.width = r.width;
10122                 st.height = r.height;
10123                 st.fontSize = '';
10124                 el.afterFx(o);
10125             };
10126
10127             var width = this.getWidth();
10128             var height = this.getHeight();
10129
10130             arguments.callee.anim = this.fxanim({
10131                     width : {to: this.adjustWidth(width * 2)},
10132                     height : {to: this.adjustHeight(height * 2)},
10133                     points : {by: [-(width * .5), -(height * .5)]},
10134                     opacity : {to: 0},
10135                     fontSize: {to:200, unit: "%"}
10136                 },
10137                 o,
10138                 'motion',
10139                 .5,
10140                 "easeOut", after);
10141         });
10142         return this;
10143     },
10144
10145         /**
10146          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10147          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10148          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10149          * Usage:
10150          *<pre><code>
10151 // default
10152 el.switchOff();
10153
10154 // all config options shown with default values
10155 el.switchOff({
10156     easing: 'easeIn',
10157     duration: .3,
10158     remove: false,
10159     useDisplay: false
10160 });
10161 </code></pre>
10162          * @param {Object} options (optional) Object literal with any of the Fx config options
10163          * @return {Roo.Element} The Element
10164          */
10165     switchOff : function(o){
10166         var el = this.getFxEl();
10167         o = o || {};
10168
10169         el.queueFx(o, function(){
10170             this.clearOpacity();
10171             this.clip();
10172
10173             // restore values after effect
10174             var r = this.getFxRestore();
10175             var st = this.dom.style;
10176
10177             var after = function(){
10178                 if(o.useDisplay){
10179                     el.setDisplayed(false);
10180                 }else{
10181                     el.hide();
10182                 }
10183
10184                 el.clearOpacity();
10185                 el.setPositioning(r.pos);
10186                 st.width = r.width;
10187                 st.height = r.height;
10188
10189                 el.afterFx(o);
10190             };
10191
10192             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10193                 this.clearOpacity();
10194                 (function(){
10195                     this.fxanim({
10196                         height:{to:1},
10197                         points:{by:[0, this.getHeight() * .5]}
10198                     }, o, 'motion', 0.3, 'easeIn', after);
10199                 }).defer(100, this);
10200             });
10201         });
10202         return this;
10203     },
10204
10205     /**
10206      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10207      * changed using the "attr" config option) and then fading back to the original color. If no original
10208      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10209      * Usage:
10210 <pre><code>
10211 // default: highlight background to yellow
10212 el.highlight();
10213
10214 // custom: highlight foreground text to blue for 2 seconds
10215 el.highlight("0000ff", { attr: 'color', duration: 2 });
10216
10217 // common config options shown with default values
10218 el.highlight("ffff9c", {
10219     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10220     endColor: (current color) or "ffffff",
10221     easing: 'easeIn',
10222     duration: 1
10223 });
10224 </code></pre>
10225      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10226      * @param {Object} options (optional) Object literal with any of the Fx config options
10227      * @return {Roo.Element} The Element
10228      */ 
10229     highlight : function(color, o){
10230         var el = this.getFxEl();
10231         o = o || {};
10232
10233         el.queueFx(o, function(){
10234             color = color || "ffff9c";
10235             attr = o.attr || "backgroundColor";
10236
10237             this.clearOpacity();
10238             this.show();
10239
10240             var origColor = this.getColor(attr);
10241             var restoreColor = this.dom.style[attr];
10242             endColor = (o.endColor || origColor) || "ffffff";
10243
10244             var after = function(){
10245                 el.dom.style[attr] = restoreColor;
10246                 el.afterFx(o);
10247             };
10248
10249             var a = {};
10250             a[attr] = {from: color, to: endColor};
10251             arguments.callee.anim = this.fxanim(a,
10252                 o,
10253                 'color',
10254                 1,
10255                 'easeIn', after);
10256         });
10257         return this;
10258     },
10259
10260    /**
10261     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10262     * Usage:
10263 <pre><code>
10264 // default: a single light blue ripple
10265 el.frame();
10266
10267 // custom: 3 red ripples lasting 3 seconds total
10268 el.frame("ff0000", 3, { duration: 3 });
10269
10270 // common config options shown with default values
10271 el.frame("C3DAF9", 1, {
10272     duration: 1 //duration of entire animation (not each individual ripple)
10273     // Note: Easing is not configurable and will be ignored if included
10274 });
10275 </code></pre>
10276     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10277     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10278     * @param {Object} options (optional) Object literal with any of the Fx config options
10279     * @return {Roo.Element} The Element
10280     */
10281     frame : function(color, count, o){
10282         var el = this.getFxEl();
10283         o = o || {};
10284
10285         el.queueFx(o, function(){
10286             color = color || "#C3DAF9";
10287             if(color.length == 6){
10288                 color = "#" + color;
10289             }
10290             count = count || 1;
10291             duration = o.duration || 1;
10292             this.show();
10293
10294             var b = this.getBox();
10295             var animFn = function(){
10296                 var proxy = this.createProxy({
10297
10298                      style:{
10299                         visbility:"hidden",
10300                         position:"absolute",
10301                         "z-index":"35000", // yee haw
10302                         border:"0px solid " + color
10303                      }
10304                   });
10305                 var scale = Roo.isBorderBox ? 2 : 1;
10306                 proxy.animate({
10307                     top:{from:b.y, to:b.y - 20},
10308                     left:{from:b.x, to:b.x - 20},
10309                     borderWidth:{from:0, to:10},
10310                     opacity:{from:1, to:0},
10311                     height:{from:b.height, to:(b.height + (20*scale))},
10312                     width:{from:b.width, to:(b.width + (20*scale))}
10313                 }, duration, function(){
10314                     proxy.remove();
10315                 });
10316                 if(--count > 0){
10317                      animFn.defer((duration/2)*1000, this);
10318                 }else{
10319                     el.afterFx(o);
10320                 }
10321             };
10322             animFn.call(this);
10323         });
10324         return this;
10325     },
10326
10327    /**
10328     * Creates a pause before any subsequent queued effects begin.  If there are
10329     * no effects queued after the pause it will have no effect.
10330     * Usage:
10331 <pre><code>
10332 el.pause(1);
10333 </code></pre>
10334     * @param {Number} seconds The length of time to pause (in seconds)
10335     * @return {Roo.Element} The Element
10336     */
10337     pause : function(seconds){
10338         var el = this.getFxEl();
10339         var o = {};
10340
10341         el.queueFx(o, function(){
10342             setTimeout(function(){
10343                 el.afterFx(o);
10344             }, seconds * 1000);
10345         });
10346         return this;
10347     },
10348
10349    /**
10350     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10351     * using the "endOpacity" config option.
10352     * Usage:
10353 <pre><code>
10354 // default: fade in from opacity 0 to 100%
10355 el.fadeIn();
10356
10357 // custom: fade in from opacity 0 to 75% over 2 seconds
10358 el.fadeIn({ endOpacity: .75, duration: 2});
10359
10360 // common config options shown with default values
10361 el.fadeIn({
10362     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10363     easing: 'easeOut',
10364     duration: .5
10365 });
10366 </code></pre>
10367     * @param {Object} options (optional) Object literal with any of the Fx config options
10368     * @return {Roo.Element} The Element
10369     */
10370     fadeIn : function(o){
10371         var el = this.getFxEl();
10372         o = o || {};
10373         el.queueFx(o, function(){
10374             this.setOpacity(0);
10375             this.fixDisplay();
10376             this.dom.style.visibility = 'visible';
10377             var to = o.endOpacity || 1;
10378             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10379                 o, null, .5, "easeOut", function(){
10380                 if(to == 1){
10381                     this.clearOpacity();
10382                 }
10383                 el.afterFx(o);
10384             });
10385         });
10386         return this;
10387     },
10388
10389    /**
10390     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10391     * using the "endOpacity" config option.
10392     * Usage:
10393 <pre><code>
10394 // default: fade out from the element's current opacity to 0
10395 el.fadeOut();
10396
10397 // custom: fade out from the element's current opacity to 25% over 2 seconds
10398 el.fadeOut({ endOpacity: .25, duration: 2});
10399
10400 // common config options shown with default values
10401 el.fadeOut({
10402     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10403     easing: 'easeOut',
10404     duration: .5
10405     remove: false,
10406     useDisplay: false
10407 });
10408 </code></pre>
10409     * @param {Object} options (optional) Object literal with any of the Fx config options
10410     * @return {Roo.Element} The Element
10411     */
10412     fadeOut : function(o){
10413         var el = this.getFxEl();
10414         o = o || {};
10415         el.queueFx(o, function(){
10416             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10417                 o, null, .5, "easeOut", function(){
10418                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10419                      this.dom.style.display = "none";
10420                 }else{
10421                      this.dom.style.visibility = "hidden";
10422                 }
10423                 this.clearOpacity();
10424                 el.afterFx(o);
10425             });
10426         });
10427         return this;
10428     },
10429
10430    /**
10431     * Animates the transition of an element's dimensions from a starting height/width
10432     * to an ending height/width.
10433     * Usage:
10434 <pre><code>
10435 // change height and width to 100x100 pixels
10436 el.scale(100, 100);
10437
10438 // common config options shown with default values.  The height and width will default to
10439 // the element's existing values if passed as null.
10440 el.scale(
10441     [element's width],
10442     [element's height], {
10443     easing: 'easeOut',
10444     duration: .35
10445 });
10446 </code></pre>
10447     * @param {Number} width  The new width (pass undefined to keep the original width)
10448     * @param {Number} height  The new height (pass undefined to keep the original height)
10449     * @param {Object} options (optional) Object literal with any of the Fx config options
10450     * @return {Roo.Element} The Element
10451     */
10452     scale : function(w, h, o){
10453         this.shift(Roo.apply({}, o, {
10454             width: w,
10455             height: h
10456         }));
10457         return this;
10458     },
10459
10460    /**
10461     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10462     * Any of these properties not specified in the config object will not be changed.  This effect 
10463     * requires that at least one new dimension, position or opacity setting must be passed in on
10464     * the config object in order for the function to have any effect.
10465     * Usage:
10466 <pre><code>
10467 // slide the element horizontally to x position 200 while changing the height and opacity
10468 el.shift({ x: 200, height: 50, opacity: .8 });
10469
10470 // common config options shown with default values.
10471 el.shift({
10472     width: [element's width],
10473     height: [element's height],
10474     x: [element's x position],
10475     y: [element's y position],
10476     opacity: [element's opacity],
10477     easing: 'easeOut',
10478     duration: .35
10479 });
10480 </code></pre>
10481     * @param {Object} options  Object literal with any of the Fx config options
10482     * @return {Roo.Element} The Element
10483     */
10484     shift : function(o){
10485         var el = this.getFxEl();
10486         o = o || {};
10487         el.queueFx(o, function(){
10488             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10489             if(w !== undefined){
10490                 a.width = {to: this.adjustWidth(w)};
10491             }
10492             if(h !== undefined){
10493                 a.height = {to: this.adjustHeight(h)};
10494             }
10495             if(x !== undefined || y !== undefined){
10496                 a.points = {to: [
10497                     x !== undefined ? x : this.getX(),
10498                     y !== undefined ? y : this.getY()
10499                 ]};
10500             }
10501             if(op !== undefined){
10502                 a.opacity = {to: op};
10503             }
10504             if(o.xy !== undefined){
10505                 a.points = {to: o.xy};
10506             }
10507             arguments.callee.anim = this.fxanim(a,
10508                 o, 'motion', .35, "easeOut", function(){
10509                 el.afterFx(o);
10510             });
10511         });
10512         return this;
10513     },
10514
10515         /**
10516          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10517          * ending point of the effect.
10518          * Usage:
10519          *<pre><code>
10520 // default: slide the element downward while fading out
10521 el.ghost();
10522
10523 // custom: slide the element out to the right with a 2-second duration
10524 el.ghost('r', { duration: 2 });
10525
10526 // common config options shown with default values
10527 el.ghost('b', {
10528     easing: 'easeOut',
10529     duration: .5
10530     remove: false,
10531     useDisplay: false
10532 });
10533 </code></pre>
10534          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10535          * @param {Object} options (optional) Object literal with any of the Fx config options
10536          * @return {Roo.Element} The Element
10537          */
10538     ghost : function(anchor, o){
10539         var el = this.getFxEl();
10540         o = o || {};
10541
10542         el.queueFx(o, function(){
10543             anchor = anchor || "b";
10544
10545             // restore values after effect
10546             var r = this.getFxRestore();
10547             var w = this.getWidth(),
10548                 h = this.getHeight();
10549
10550             var st = this.dom.style;
10551
10552             var after = function(){
10553                 if(o.useDisplay){
10554                     el.setDisplayed(false);
10555                 }else{
10556                     el.hide();
10557                 }
10558
10559                 el.clearOpacity();
10560                 el.setPositioning(r.pos);
10561                 st.width = r.width;
10562                 st.height = r.height;
10563
10564                 el.afterFx(o);
10565             };
10566
10567             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10568             switch(anchor.toLowerCase()){
10569                 case "t":
10570                     pt.by = [0, -h];
10571                 break;
10572                 case "l":
10573                     pt.by = [-w, 0];
10574                 break;
10575                 case "r":
10576                     pt.by = [w, 0];
10577                 break;
10578                 case "b":
10579                     pt.by = [0, h];
10580                 break;
10581                 case "tl":
10582                     pt.by = [-w, -h];
10583                 break;
10584                 case "bl":
10585                     pt.by = [-w, h];
10586                 break;
10587                 case "br":
10588                     pt.by = [w, h];
10589                 break;
10590                 case "tr":
10591                     pt.by = [w, -h];
10592                 break;
10593             }
10594
10595             arguments.callee.anim = this.fxanim(a,
10596                 o,
10597                 'motion',
10598                 .5,
10599                 "easeOut", after);
10600         });
10601         return this;
10602     },
10603
10604         /**
10605          * Ensures that all effects queued after syncFx is called on the element are
10606          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10607          * @return {Roo.Element} The Element
10608          */
10609     syncFx : function(){
10610         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10611             block : false,
10612             concurrent : true,
10613             stopFx : false
10614         });
10615         return this;
10616     },
10617
10618         /**
10619          * Ensures that all effects queued after sequenceFx is called on the element are
10620          * run in sequence.  This is the opposite of {@link #syncFx}.
10621          * @return {Roo.Element} The Element
10622          */
10623     sequenceFx : function(){
10624         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10625             block : false,
10626             concurrent : false,
10627             stopFx : false
10628         });
10629         return this;
10630     },
10631
10632         /* @private */
10633     nextFx : function(){
10634         var ef = this.fxQueue[0];
10635         if(ef){
10636             ef.call(this);
10637         }
10638     },
10639
10640         /**
10641          * Returns true if the element has any effects actively running or queued, else returns false.
10642          * @return {Boolean} True if element has active effects, else false
10643          */
10644     hasActiveFx : function(){
10645         return this.fxQueue && this.fxQueue[0];
10646     },
10647
10648         /**
10649          * Stops any running effects and clears the element's internal effects queue if it contains
10650          * any additional effects that haven't started yet.
10651          * @return {Roo.Element} The Element
10652          */
10653     stopFx : function(){
10654         if(this.hasActiveFx()){
10655             var cur = this.fxQueue[0];
10656             if(cur && cur.anim && cur.anim.isAnimated()){
10657                 this.fxQueue = [cur]; // clear out others
10658                 cur.anim.stop(true);
10659             }
10660         }
10661         return this;
10662     },
10663
10664         /* @private */
10665     beforeFx : function(o){
10666         if(this.hasActiveFx() && !o.concurrent){
10667            if(o.stopFx){
10668                this.stopFx();
10669                return true;
10670            }
10671            return false;
10672         }
10673         return true;
10674     },
10675
10676         /**
10677          * Returns true if the element is currently blocking so that no other effect can be queued
10678          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10679          * used to ensure that an effect initiated by a user action runs to completion prior to the
10680          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10681          * @return {Boolean} True if blocking, else false
10682          */
10683     hasFxBlock : function(){
10684         var q = this.fxQueue;
10685         return q && q[0] && q[0].block;
10686     },
10687
10688         /* @private */
10689     queueFx : function(o, fn){
10690         if(!this.fxQueue){
10691             this.fxQueue = [];
10692         }
10693         if(!this.hasFxBlock()){
10694             Roo.applyIf(o, this.fxDefaults);
10695             if(!o.concurrent){
10696                 var run = this.beforeFx(o);
10697                 fn.block = o.block;
10698                 this.fxQueue.push(fn);
10699                 if(run){
10700                     this.nextFx();
10701                 }
10702             }else{
10703                 fn.call(this);
10704             }
10705         }
10706         return this;
10707     },
10708
10709         /* @private */
10710     fxWrap : function(pos, o, vis){
10711         var wrap;
10712         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10713             var wrapXY;
10714             if(o.fixPosition){
10715                 wrapXY = this.getXY();
10716             }
10717             var div = document.createElement("div");
10718             div.style.visibility = vis;
10719             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10720             wrap.setPositioning(pos);
10721             if(wrap.getStyle("position") == "static"){
10722                 wrap.position("relative");
10723             }
10724             this.clearPositioning('auto');
10725             wrap.clip();
10726             wrap.dom.appendChild(this.dom);
10727             if(wrapXY){
10728                 wrap.setXY(wrapXY);
10729             }
10730         }
10731         return wrap;
10732     },
10733
10734         /* @private */
10735     fxUnwrap : function(wrap, pos, o){
10736         this.clearPositioning();
10737         this.setPositioning(pos);
10738         if(!o.wrap){
10739             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10740             wrap.remove();
10741         }
10742     },
10743
10744         /* @private */
10745     getFxRestore : function(){
10746         var st = this.dom.style;
10747         return {pos: this.getPositioning(), width: st.width, height : st.height};
10748     },
10749
10750         /* @private */
10751     afterFx : function(o){
10752         if(o.afterStyle){
10753             this.applyStyles(o.afterStyle);
10754         }
10755         if(o.afterCls){
10756             this.addClass(o.afterCls);
10757         }
10758         if(o.remove === true){
10759             this.remove();
10760         }
10761         Roo.callback(o.callback, o.scope, [this]);
10762         if(!o.concurrent){
10763             this.fxQueue.shift();
10764             this.nextFx();
10765         }
10766     },
10767
10768         /* @private */
10769     getFxEl : function(){ // support for composite element fx
10770         return Roo.get(this.dom);
10771     },
10772
10773         /* @private */
10774     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10775         animType = animType || 'run';
10776         opt = opt || {};
10777         var anim = Roo.lib.Anim[animType](
10778             this.dom, args,
10779             (opt.duration || defaultDur) || .35,
10780             (opt.easing || defaultEase) || 'easeOut',
10781             function(){
10782                 Roo.callback(cb, this);
10783             },
10784             this
10785         );
10786         opt.anim = anim;
10787         return anim;
10788     }
10789 };
10790
10791 // backwords compat
10792 Roo.Fx.resize = Roo.Fx.scale;
10793
10794 //When included, Roo.Fx is automatically applied to Element so that all basic
10795 //effects are available directly via the Element API
10796 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10797  * Based on:
10798  * Ext JS Library 1.1.1
10799  * Copyright(c) 2006-2007, Ext JS, LLC.
10800  *
10801  * Originally Released Under LGPL - original licence link has changed is not relivant.
10802  *
10803  * Fork - LGPL
10804  * <script type="text/javascript">
10805  */
10806
10807
10808 /**
10809  * @class Roo.CompositeElement
10810  * Standard composite class. Creates a Roo.Element for every element in the collection.
10811  * <br><br>
10812  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10813  * actions will be performed on all the elements in this collection.</b>
10814  * <br><br>
10815  * All methods return <i>this</i> and can be chained.
10816  <pre><code>
10817  var els = Roo.select("#some-el div.some-class", true);
10818  // or select directly from an existing element
10819  var el = Roo.get('some-el');
10820  el.select('div.some-class', true);
10821
10822  els.setWidth(100); // all elements become 100 width
10823  els.hide(true); // all elements fade out and hide
10824  // or
10825  els.setWidth(100).hide(true);
10826  </code></pre>
10827  */
10828 Roo.CompositeElement = function(els){
10829     this.elements = [];
10830     this.addElements(els);
10831 };
10832 Roo.CompositeElement.prototype = {
10833     isComposite: true,
10834     addElements : function(els){
10835         if(!els) return this;
10836         if(typeof els == "string"){
10837             els = Roo.Element.selectorFunction(els);
10838         }
10839         var yels = this.elements;
10840         var index = yels.length-1;
10841         for(var i = 0, len = els.length; i < len; i++) {
10842                 yels[++index] = Roo.get(els[i]);
10843         }
10844         return this;
10845     },
10846
10847     /**
10848     * Clears this composite and adds the elements returned by the passed selector.
10849     * @param {String/Array} els A string CSS selector, an array of elements or an element
10850     * @return {CompositeElement} this
10851     */
10852     fill : function(els){
10853         this.elements = [];
10854         this.add(els);
10855         return this;
10856     },
10857
10858     /**
10859     * Filters this composite to only elements that match the passed selector.
10860     * @param {String} selector A string CSS selector
10861     * @return {CompositeElement} this
10862     */
10863     filter : function(selector){
10864         var els = [];
10865         this.each(function(el){
10866             if(el.is(selector)){
10867                 els[els.length] = el.dom;
10868             }
10869         });
10870         this.fill(els);
10871         return this;
10872     },
10873
10874     invoke : function(fn, args){
10875         var els = this.elements;
10876         for(var i = 0, len = els.length; i < len; i++) {
10877                 Roo.Element.prototype[fn].apply(els[i], args);
10878         }
10879         return this;
10880     },
10881     /**
10882     * Adds elements to this composite.
10883     * @param {String/Array} els A string CSS selector, an array of elements or an element
10884     * @return {CompositeElement} this
10885     */
10886     add : function(els){
10887         if(typeof els == "string"){
10888             this.addElements(Roo.Element.selectorFunction(els));
10889         }else if(els.length !== undefined){
10890             this.addElements(els);
10891         }else{
10892             this.addElements([els]);
10893         }
10894         return this;
10895     },
10896     /**
10897     * Calls the passed function passing (el, this, index) for each element in this composite.
10898     * @param {Function} fn The function to call
10899     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10900     * @return {CompositeElement} this
10901     */
10902     each : function(fn, scope){
10903         var els = this.elements;
10904         for(var i = 0, len = els.length; i < len; i++){
10905             if(fn.call(scope || els[i], els[i], this, i) === false) {
10906                 break;
10907             }
10908         }
10909         return this;
10910     },
10911
10912     /**
10913      * Returns the Element object at the specified index
10914      * @param {Number} index
10915      * @return {Roo.Element}
10916      */
10917     item : function(index){
10918         return this.elements[index] || null;
10919     },
10920
10921     /**
10922      * Returns the first Element
10923      * @return {Roo.Element}
10924      */
10925     first : function(){
10926         return this.item(0);
10927     },
10928
10929     /**
10930      * Returns the last Element
10931      * @return {Roo.Element}
10932      */
10933     last : function(){
10934         return this.item(this.elements.length-1);
10935     },
10936
10937     /**
10938      * Returns the number of elements in this composite
10939      * @return Number
10940      */
10941     getCount : function(){
10942         return this.elements.length;
10943     },
10944
10945     /**
10946      * Returns true if this composite contains the passed element
10947      * @return Boolean
10948      */
10949     contains : function(el){
10950         return this.indexOf(el) !== -1;
10951     },
10952
10953     /**
10954      * Returns true if this composite contains the passed element
10955      * @return Boolean
10956      */
10957     indexOf : function(el){
10958         return this.elements.indexOf(Roo.get(el));
10959     },
10960
10961
10962     /**
10963     * Removes the specified element(s).
10964     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10965     * or an array of any of those.
10966     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10967     * @return {CompositeElement} this
10968     */
10969     removeElement : function(el, removeDom){
10970         if(el instanceof Array){
10971             for(var i = 0, len = el.length; i < len; i++){
10972                 this.removeElement(el[i]);
10973             }
10974             return this;
10975         }
10976         var index = typeof el == 'number' ? el : this.indexOf(el);
10977         if(index !== -1){
10978             if(removeDom){
10979                 var d = this.elements[index];
10980                 if(d.dom){
10981                     d.remove();
10982                 }else{
10983                     d.parentNode.removeChild(d);
10984                 }
10985             }
10986             this.elements.splice(index, 1);
10987         }
10988         return this;
10989     },
10990
10991     /**
10992     * Replaces the specified element with the passed element.
10993     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10994     * to replace.
10995     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10996     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10997     * @return {CompositeElement} this
10998     */
10999     replaceElement : function(el, replacement, domReplace){
11000         var index = typeof el == 'number' ? el : this.indexOf(el);
11001         if(index !== -1){
11002             if(domReplace){
11003                 this.elements[index].replaceWith(replacement);
11004             }else{
11005                 this.elements.splice(index, 1, Roo.get(replacement))
11006             }
11007         }
11008         return this;
11009     },
11010
11011     /**
11012      * Removes all elements.
11013      */
11014     clear : function(){
11015         this.elements = [];
11016     }
11017 };
11018 (function(){
11019     Roo.CompositeElement.createCall = function(proto, fnName){
11020         if(!proto[fnName]){
11021             proto[fnName] = function(){
11022                 return this.invoke(fnName, arguments);
11023             };
11024         }
11025     };
11026     for(var fnName in Roo.Element.prototype){
11027         if(typeof Roo.Element.prototype[fnName] == "function"){
11028             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11029         }
11030     };
11031 })();
11032 /*
11033  * Based on:
11034  * Ext JS Library 1.1.1
11035  * Copyright(c) 2006-2007, Ext JS, LLC.
11036  *
11037  * Originally Released Under LGPL - original licence link has changed is not relivant.
11038  *
11039  * Fork - LGPL
11040  * <script type="text/javascript">
11041  */
11042
11043 /**
11044  * @class Roo.CompositeElementLite
11045  * @extends Roo.CompositeElement
11046  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11047  <pre><code>
11048  var els = Roo.select("#some-el div.some-class");
11049  // or select directly from an existing element
11050  var el = Roo.get('some-el');
11051  el.select('div.some-class');
11052
11053  els.setWidth(100); // all elements become 100 width
11054  els.hide(true); // all elements fade out and hide
11055  // or
11056  els.setWidth(100).hide(true);
11057  </code></pre><br><br>
11058  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11059  * actions will be performed on all the elements in this collection.</b>
11060  */
11061 Roo.CompositeElementLite = function(els){
11062     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11063     this.el = new Roo.Element.Flyweight();
11064 };
11065 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11066     addElements : function(els){
11067         if(els){
11068             if(els instanceof Array){
11069                 this.elements = this.elements.concat(els);
11070             }else{
11071                 var yels = this.elements;
11072                 var index = yels.length-1;
11073                 for(var i = 0, len = els.length; i < len; i++) {
11074                     yels[++index] = els[i];
11075                 }
11076             }
11077         }
11078         return this;
11079     },
11080     invoke : function(fn, args){
11081         var els = this.elements;
11082         var el = this.el;
11083         for(var i = 0, len = els.length; i < len; i++) {
11084             el.dom = els[i];
11085                 Roo.Element.prototype[fn].apply(el, args);
11086         }
11087         return this;
11088     },
11089     /**
11090      * Returns a flyweight Element of the dom element object at the specified index
11091      * @param {Number} index
11092      * @return {Roo.Element}
11093      */
11094     item : function(index){
11095         if(!this.elements[index]){
11096             return null;
11097         }
11098         this.el.dom = this.elements[index];
11099         return this.el;
11100     },
11101
11102     // fixes scope with flyweight
11103     addListener : function(eventName, handler, scope, opt){
11104         var els = this.elements;
11105         for(var i = 0, len = els.length; i < len; i++) {
11106             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11107         }
11108         return this;
11109     },
11110
11111     /**
11112     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11113     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11114     * a reference to the dom node, use el.dom.</b>
11115     * @param {Function} fn The function to call
11116     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11117     * @return {CompositeElement} this
11118     */
11119     each : function(fn, scope){
11120         var els = this.elements;
11121         var el = this.el;
11122         for(var i = 0, len = els.length; i < len; i++){
11123             el.dom = els[i];
11124                 if(fn.call(scope || el, el, this, i) === false){
11125                 break;
11126             }
11127         }
11128         return this;
11129     },
11130
11131     indexOf : function(el){
11132         return this.elements.indexOf(Roo.getDom(el));
11133     },
11134
11135     replaceElement : function(el, replacement, domReplace){
11136         var index = typeof el == 'number' ? el : this.indexOf(el);
11137         if(index !== -1){
11138             replacement = Roo.getDom(replacement);
11139             if(domReplace){
11140                 var d = this.elements[index];
11141                 d.parentNode.insertBefore(replacement, d);
11142                 d.parentNode.removeChild(d);
11143             }
11144             this.elements.splice(index, 1, replacement);
11145         }
11146         return this;
11147     }
11148 });
11149 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11150
11151 /*
11152  * Based on:
11153  * Ext JS Library 1.1.1
11154  * Copyright(c) 2006-2007, Ext JS, LLC.
11155  *
11156  * Originally Released Under LGPL - original licence link has changed is not relivant.
11157  *
11158  * Fork - LGPL
11159  * <script type="text/javascript">
11160  */
11161
11162  
11163
11164 /**
11165  * @class Roo.data.Connection
11166  * @extends Roo.util.Observable
11167  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11168  * either to a configured URL, or to a URL specified at request time.<br><br>
11169  * <p>
11170  * Requests made by this class are asynchronous, and will return immediately. No data from
11171  * the server will be available to the statement immediately following the {@link #request} call.
11172  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11173  * <p>
11174  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11175  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11176  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11177  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11178  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11179  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11180  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11181  * standard DOM methods.
11182  * @constructor
11183  * @param {Object} config a configuration object.
11184  */
11185 Roo.data.Connection = function(config){
11186     Roo.apply(this, config);
11187     this.addEvents({
11188         /**
11189          * @event beforerequest
11190          * Fires before a network request is made to retrieve a data object.
11191          * @param {Connection} conn This Connection object.
11192          * @param {Object} options The options config object passed to the {@link #request} method.
11193          */
11194         "beforerequest" : true,
11195         /**
11196          * @event requestcomplete
11197          * Fires if the request was successfully completed.
11198          * @param {Connection} conn This Connection object.
11199          * @param {Object} response The XHR object containing the response data.
11200          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11201          * @param {Object} options The options config object passed to the {@link #request} method.
11202          */
11203         "requestcomplete" : true,
11204         /**
11205          * @event requestexception
11206          * Fires if an error HTTP status was returned from the server.
11207          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11208          * @param {Connection} conn This Connection object.
11209          * @param {Object} response The XHR object containing the response data.
11210          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11211          * @param {Object} options The options config object passed to the {@link #request} method.
11212          */
11213         "requestexception" : true
11214     });
11215     Roo.data.Connection.superclass.constructor.call(this);
11216 };
11217
11218 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11219     /**
11220      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11221      */
11222     /**
11223      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11224      * extra parameters to each request made by this object. (defaults to undefined)
11225      */
11226     /**
11227      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11228      *  to each request made by this object. (defaults to undefined)
11229      */
11230     /**
11231      * @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)
11232      */
11233     /**
11234      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11235      */
11236     timeout : 30000,
11237     /**
11238      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11239      * @type Boolean
11240      */
11241     autoAbort:false,
11242
11243     /**
11244      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11245      * @type Boolean
11246      */
11247     disableCaching: true,
11248
11249     /**
11250      * Sends an HTTP request to a remote server.
11251      * @param {Object} options An object which may contain the following properties:<ul>
11252      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11253      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11254      * request, a url encoded string or a function to call to get either.</li>
11255      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11256      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11257      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11258      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11259      * <li>options {Object} The parameter to the request call.</li>
11260      * <li>success {Boolean} True if the request succeeded.</li>
11261      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11262      * </ul></li>
11263      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11264      * The callback is passed the following parameters:<ul>
11265      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11266      * <li>options {Object} The parameter to the request call.</li>
11267      * </ul></li>
11268      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11269      * The callback is passed the following parameters:<ul>
11270      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11271      * <li>options {Object} The parameter to the request call.</li>
11272      * </ul></li>
11273      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11274      * for the callback function. Defaults to the browser window.</li>
11275      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11276      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11277      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11278      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11279      * params for the post data. Any params will be appended to the URL.</li>
11280      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11281      * </ul>
11282      * @return {Number} transactionId
11283      */
11284     request : function(o){
11285         if(this.fireEvent("beforerequest", this, o) !== false){
11286             var p = o.params;
11287
11288             if(typeof p == "function"){
11289                 p = p.call(o.scope||window, o);
11290             }
11291             if(typeof p == "object"){
11292                 p = Roo.urlEncode(o.params);
11293             }
11294             if(this.extraParams){
11295                 var extras = Roo.urlEncode(this.extraParams);
11296                 p = p ? (p + '&' + extras) : extras;
11297             }
11298
11299             var url = o.url || this.url;
11300             if(typeof url == 'function'){
11301                 url = url.call(o.scope||window, o);
11302             }
11303
11304             if(o.form){
11305                 var form = Roo.getDom(o.form);
11306                 url = url || form.action;
11307
11308                 var enctype = form.getAttribute("enctype");
11309                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11310                     return this.doFormUpload(o, p, url);
11311                 }
11312                 var f = Roo.lib.Ajax.serializeForm(form);
11313                 p = p ? (p + '&' + f) : f;
11314             }
11315
11316             var hs = o.headers;
11317             if(this.defaultHeaders){
11318                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11319                 if(!o.headers){
11320                     o.headers = hs;
11321                 }
11322             }
11323
11324             var cb = {
11325                 success: this.handleResponse,
11326                 failure: this.handleFailure,
11327                 scope: this,
11328                 argument: {options: o},
11329                 timeout : this.timeout
11330             };
11331
11332             var method = o.method||this.method||(p ? "POST" : "GET");
11333
11334             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11335                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11336             }
11337
11338             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11339                 if(o.autoAbort){
11340                     this.abort();
11341                 }
11342             }else if(this.autoAbort !== false){
11343                 this.abort();
11344             }
11345
11346             if((method == 'GET' && p) || o.xmlData){
11347                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11348                 p = '';
11349             }
11350             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11351             return this.transId;
11352         }else{
11353             Roo.callback(o.callback, o.scope, [o, null, null]);
11354             return null;
11355         }
11356     },
11357
11358     /**
11359      * Determine whether this object has a request outstanding.
11360      * @param {Number} transactionId (Optional) defaults to the last transaction
11361      * @return {Boolean} True if there is an outstanding request.
11362      */
11363     isLoading : function(transId){
11364         if(transId){
11365             return Roo.lib.Ajax.isCallInProgress(transId);
11366         }else{
11367             return this.transId ? true : false;
11368         }
11369     },
11370
11371     /**
11372      * Aborts any outstanding request.
11373      * @param {Number} transactionId (Optional) defaults to the last transaction
11374      */
11375     abort : function(transId){
11376         if(transId || this.isLoading()){
11377             Roo.lib.Ajax.abort(transId || this.transId);
11378         }
11379     },
11380
11381     // private
11382     handleResponse : function(response){
11383         this.transId = false;
11384         var options = response.argument.options;
11385         response.argument = options ? options.argument : null;
11386         this.fireEvent("requestcomplete", this, response, options);
11387         Roo.callback(options.success, options.scope, [response, options]);
11388         Roo.callback(options.callback, options.scope, [options, true, response]);
11389     },
11390
11391     // private
11392     handleFailure : function(response, e){
11393         this.transId = false;
11394         var options = response.argument.options;
11395         response.argument = options ? options.argument : null;
11396         this.fireEvent("requestexception", this, response, options, e);
11397         Roo.callback(options.failure, options.scope, [response, options]);
11398         Roo.callback(options.callback, options.scope, [options, false, response]);
11399     },
11400
11401     // private
11402     doFormUpload : function(o, ps, url){
11403         var id = Roo.id();
11404         var frame = document.createElement('iframe');
11405         frame.id = id;
11406         frame.name = id;
11407         frame.className = 'x-hidden';
11408         if(Roo.isIE){
11409             frame.src = Roo.SSL_SECURE_URL;
11410         }
11411         document.body.appendChild(frame);
11412
11413         if(Roo.isIE){
11414            document.frames[id].name = id;
11415         }
11416
11417         var form = Roo.getDom(o.form);
11418         form.target = id;
11419         form.method = 'POST';
11420         form.enctype = form.encoding = 'multipart/form-data';
11421         if(url){
11422             form.action = url;
11423         }
11424
11425         var hiddens, hd;
11426         if(ps){ // add dynamic params
11427             hiddens = [];
11428             ps = Roo.urlDecode(ps, false);
11429             for(var k in ps){
11430                 if(ps.hasOwnProperty(k)){
11431                     hd = document.createElement('input');
11432                     hd.type = 'hidden';
11433                     hd.name = k;
11434                     hd.value = ps[k];
11435                     form.appendChild(hd);
11436                     hiddens.push(hd);
11437                 }
11438             }
11439         }
11440
11441         function cb(){
11442             var r = {  // bogus response object
11443                 responseText : '',
11444                 responseXML : null
11445             };
11446
11447             r.argument = o ? o.argument : null;
11448
11449             try { //
11450                 var doc;
11451                 if(Roo.isIE){
11452                     doc = frame.contentWindow.document;
11453                 }else {
11454                     doc = (frame.contentDocument || window.frames[id].document);
11455                 }
11456                 if(doc && doc.body){
11457                     r.responseText = doc.body.innerHTML;
11458                 }
11459                 if(doc && doc.XMLDocument){
11460                     r.responseXML = doc.XMLDocument;
11461                 }else {
11462                     r.responseXML = doc;
11463                 }
11464             }
11465             catch(e) {
11466                 // ignore
11467             }
11468
11469             Roo.EventManager.removeListener(frame, 'load', cb, this);
11470
11471             this.fireEvent("requestcomplete", this, r, o);
11472             Roo.callback(o.success, o.scope, [r, o]);
11473             Roo.callback(o.callback, o.scope, [o, true, r]);
11474
11475             setTimeout(function(){document.body.removeChild(frame);}, 100);
11476         }
11477
11478         Roo.EventManager.on(frame, 'load', cb, this);
11479         form.submit();
11480
11481         if(hiddens){ // remove dynamic params
11482             for(var i = 0, len = hiddens.length; i < len; i++){
11483                 form.removeChild(hiddens[i]);
11484             }
11485         }
11486     }
11487 });
11488
11489 /**
11490  * @class Roo.Ajax
11491  * @extends Roo.data.Connection
11492  * Global Ajax request class.
11493  *
11494  * @singleton
11495  */
11496 Roo.Ajax = new Roo.data.Connection({
11497     // fix up the docs
11498    /**
11499      * @cfg {String} url @hide
11500      */
11501     /**
11502      * @cfg {Object} extraParams @hide
11503      */
11504     /**
11505      * @cfg {Object} defaultHeaders @hide
11506      */
11507     /**
11508      * @cfg {String} method (Optional) @hide
11509      */
11510     /**
11511      * @cfg {Number} timeout (Optional) @hide
11512      */
11513     /**
11514      * @cfg {Boolean} autoAbort (Optional) @hide
11515      */
11516
11517     /**
11518      * @cfg {Boolean} disableCaching (Optional) @hide
11519      */
11520
11521     /**
11522      * @property  disableCaching
11523      * True to add a unique cache-buster param to GET requests. (defaults to true)
11524      * @type Boolean
11525      */
11526     /**
11527      * @property  url
11528      * The default URL to be used for requests to the server. (defaults to undefined)
11529      * @type String
11530      */
11531     /**
11532      * @property  extraParams
11533      * An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      * @type Object
11536      */
11537     /**
11538      * @property  defaultHeaders
11539      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11540      * @type Object
11541      */
11542     /**
11543      * @property  method
11544      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11545      * @type String
11546      */
11547     /**
11548      * @property  timeout
11549      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11550      * @type Number
11551      */
11552
11553     /**
11554      * @property  autoAbort
11555      * Whether a new request should abort any pending requests. (defaults to false)
11556      * @type Boolean
11557      */
11558     autoAbort : false,
11559
11560     /**
11561      * Serialize the passed form into a url encoded string
11562      * @param {String/HTMLElement} form
11563      * @return {String}
11564      */
11565     serializeForm : function(form){
11566         return Roo.lib.Ajax.serializeForm(form);
11567     }
11568 });/*
11569  * Based on:
11570  * Ext JS Library 1.1.1
11571  * Copyright(c) 2006-2007, Ext JS, LLC.
11572  *
11573  * Originally Released Under LGPL - original licence link has changed is not relivant.
11574  *
11575  * Fork - LGPL
11576  * <script type="text/javascript">
11577  */
11578  
11579 /**
11580  * Global Ajax request class.
11581  * 
11582  * @class Roo.Ajax
11583  * @extends Roo.data.Connection
11584  * @static
11585  * 
11586  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11587  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11588  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11589  * @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)
11590  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11591  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11592  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11593  */
11594 Roo.Ajax = new Roo.data.Connection({
11595     // fix up the docs
11596     /**
11597      * @scope Roo.Ajax
11598      * @type {Boolear} 
11599      */
11600     autoAbort : false,
11601
11602     /**
11603      * Serialize the passed form into a url encoded string
11604      * @scope Roo.Ajax
11605      * @param {String/HTMLElement} form
11606      * @return {String}
11607      */
11608     serializeForm : function(form){
11609         return Roo.lib.Ajax.serializeForm(form);
11610     }
11611 });/*
11612  * Based on:
11613  * Ext JS Library 1.1.1
11614  * Copyright(c) 2006-2007, Ext JS, LLC.
11615  *
11616  * Originally Released Under LGPL - original licence link has changed is not relivant.
11617  *
11618  * Fork - LGPL
11619  * <script type="text/javascript">
11620  */
11621
11622  
11623 /**
11624  * @class Roo.UpdateManager
11625  * @extends Roo.util.Observable
11626  * Provides AJAX-style update for Element object.<br><br>
11627  * Usage:<br>
11628  * <pre><code>
11629  * // Get it from a Roo.Element object
11630  * var el = Roo.get("foo");
11631  * var mgr = el.getUpdateManager();
11632  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11633  * ...
11634  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11635  * <br>
11636  * // or directly (returns the same UpdateManager instance)
11637  * var mgr = new Roo.UpdateManager("myElementId");
11638  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11639  * mgr.on("update", myFcnNeedsToKnow);
11640  * <br>
11641    // short handed call directly from the element object
11642    Roo.get("foo").load({
11643         url: "bar.php",
11644         scripts:true,
11645         params: "for=bar",
11646         text: "Loading Foo..."
11647    });
11648  * </code></pre>
11649  * @constructor
11650  * Create new UpdateManager directly.
11651  * @param {String/HTMLElement/Roo.Element} el The element to update
11652  * @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).
11653  */
11654 Roo.UpdateManager = function(el, forceNew){
11655     el = Roo.get(el);
11656     if(!forceNew && el.updateManager){
11657         return el.updateManager;
11658     }
11659     /**
11660      * The Element object
11661      * @type Roo.Element
11662      */
11663     this.el = el;
11664     /**
11665      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11666      * @type String
11667      */
11668     this.defaultUrl = null;
11669
11670     this.addEvents({
11671         /**
11672          * @event beforeupdate
11673          * Fired before an update is made, return false from your handler and the update is cancelled.
11674          * @param {Roo.Element} el
11675          * @param {String/Object/Function} url
11676          * @param {String/Object} params
11677          */
11678         "beforeupdate": true,
11679         /**
11680          * @event update
11681          * Fired after successful update is made.
11682          * @param {Roo.Element} el
11683          * @param {Object} oResponseObject The response Object
11684          */
11685         "update": true,
11686         /**
11687          * @event failure
11688          * Fired on update failure.
11689          * @param {Roo.Element} el
11690          * @param {Object} oResponseObject The response Object
11691          */
11692         "failure": true
11693     });
11694     var d = Roo.UpdateManager.defaults;
11695     /**
11696      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11697      * @type String
11698      */
11699     this.sslBlankUrl = d.sslBlankUrl;
11700     /**
11701      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11702      * @type Boolean
11703      */
11704     this.disableCaching = d.disableCaching;
11705     /**
11706      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11707      * @type String
11708      */
11709     this.indicatorText = d.indicatorText;
11710     /**
11711      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11712      * @type String
11713      */
11714     this.showLoadIndicator = d.showLoadIndicator;
11715     /**
11716      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11717      * @type Number
11718      */
11719     this.timeout = d.timeout;
11720
11721     /**
11722      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11723      * @type Boolean
11724      */
11725     this.loadScripts = d.loadScripts;
11726
11727     /**
11728      * Transaction object of current executing transaction
11729      */
11730     this.transaction = null;
11731
11732     /**
11733      * @private
11734      */
11735     this.autoRefreshProcId = null;
11736     /**
11737      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11738      * @type Function
11739      */
11740     this.refreshDelegate = this.refresh.createDelegate(this);
11741     /**
11742      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11743      * @type Function
11744      */
11745     this.updateDelegate = this.update.createDelegate(this);
11746     /**
11747      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11748      * @type Function
11749      */
11750     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11751     /**
11752      * @private
11753      */
11754     this.successDelegate = this.processSuccess.createDelegate(this);
11755     /**
11756      * @private
11757      */
11758     this.failureDelegate = this.processFailure.createDelegate(this);
11759
11760     if(!this.renderer){
11761      /**
11762       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11763       */
11764     this.renderer = new Roo.UpdateManager.BasicRenderer();
11765     }
11766     
11767     Roo.UpdateManager.superclass.constructor.call(this);
11768 };
11769
11770 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11771     /**
11772      * Get the Element this UpdateManager is bound to
11773      * @return {Roo.Element} The element
11774      */
11775     getEl : function(){
11776         return this.el;
11777     },
11778     /**
11779      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11780      * @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:
11781 <pre><code>
11782 um.update({<br/>
11783     url: "your-url.php",<br/>
11784     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11785     callback: yourFunction,<br/>
11786     scope: yourObject, //(optional scope)  <br/>
11787     discardUrl: false, <br/>
11788     nocache: false,<br/>
11789     text: "Loading...",<br/>
11790     timeout: 30,<br/>
11791     scripts: false<br/>
11792 });
11793 </code></pre>
11794      * The only required property is url. The optional properties nocache, text and scripts
11795      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11796      * @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}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11798      * @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.
11799      */
11800     update : function(url, params, callback, discardUrl){
11801         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11802             var method = this.method, cfg;
11803             if(typeof url == "object"){ // must be config object
11804                 cfg = url;
11805                 url = cfg.url;
11806                 params = params || cfg.params;
11807                 callback = callback || cfg.callback;
11808                 discardUrl = discardUrl || cfg.discardUrl;
11809                 if(callback && cfg.scope){
11810                     callback = callback.createDelegate(cfg.scope);
11811                 }
11812                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11813                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11814                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11815                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11816                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11817             }
11818             this.showLoading();
11819             if(!discardUrl){
11820                 this.defaultUrl = url;
11821             }
11822             if(typeof url == "function"){
11823                 url = url.call(this);
11824             }
11825
11826             method = method || (params ? "POST" : "GET");
11827             if(method == "GET"){
11828                 url = this.prepareUrl(url);
11829             }
11830
11831             var o = Roo.apply(cfg ||{}, {
11832                 url : url,
11833                 params: params,
11834                 success: this.successDelegate,
11835                 failure: this.failureDelegate,
11836                 callback: undefined,
11837                 timeout: (this.timeout*1000),
11838                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11839             });
11840
11841             this.transaction = Roo.Ajax.request(o);
11842         }
11843     },
11844
11845     /**
11846      * 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.
11847      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11848      * @param {String/HTMLElement} form The form Id or form element
11849      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11850      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11851      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11852      */
11853     formUpdate : function(form, url, reset, callback){
11854         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11855             if(typeof url == "function"){
11856                 url = url.call(this);
11857             }
11858             form = Roo.getDom(form);
11859             this.transaction = Roo.Ajax.request({
11860                 form: form,
11861                 url:url,
11862                 success: this.successDelegate,
11863                 failure: this.failureDelegate,
11864                 timeout: (this.timeout*1000),
11865                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11866             });
11867             this.showLoading.defer(1, this);
11868         }
11869     },
11870
11871     /**
11872      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11873      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11874      */
11875     refresh : function(callback){
11876         if(this.defaultUrl == null){
11877             return;
11878         }
11879         this.update(this.defaultUrl, null, callback, true);
11880     },
11881
11882     /**
11883      * Set this element to auto refresh.
11884      * @param {Number} interval How often to update (in seconds).
11885      * @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)
11886      * @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}
11887      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11888      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11889      */
11890     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11891         if(refreshNow){
11892             this.update(url || this.defaultUrl, params, callback, true);
11893         }
11894         if(this.autoRefreshProcId){
11895             clearInterval(this.autoRefreshProcId);
11896         }
11897         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11898     },
11899
11900     /**
11901      * Stop auto refresh on this element.
11902      */
11903      stopAutoRefresh : function(){
11904         if(this.autoRefreshProcId){
11905             clearInterval(this.autoRefreshProcId);
11906             delete this.autoRefreshProcId;
11907         }
11908     },
11909
11910     isAutoRefreshing : function(){
11911        return this.autoRefreshProcId ? true : false;
11912     },
11913     /**
11914      * Called to update the element to "Loading" state. Override to perform custom action.
11915      */
11916     showLoading : function(){
11917         if(this.showLoadIndicator){
11918             this.el.update(this.indicatorText);
11919         }
11920     },
11921
11922     /**
11923      * Adds unique parameter to query string if disableCaching = true
11924      * @private
11925      */
11926     prepareUrl : function(url){
11927         if(this.disableCaching){
11928             var append = "_dc=" + (new Date().getTime());
11929             if(url.indexOf("?") !== -1){
11930                 url += "&" + append;
11931             }else{
11932                 url += "?" + append;
11933             }
11934         }
11935         return url;
11936     },
11937
11938     /**
11939      * @private
11940      */
11941     processSuccess : function(response){
11942         this.transaction = null;
11943         if(response.argument.form && response.argument.reset){
11944             try{ // put in try/catch since some older FF releases had problems with this
11945                 response.argument.form.reset();
11946             }catch(e){}
11947         }
11948         if(this.loadScripts){
11949             this.renderer.render(this.el, response, this,
11950                 this.updateComplete.createDelegate(this, [response]));
11951         }else{
11952             this.renderer.render(this.el, response, this);
11953             this.updateComplete(response);
11954         }
11955     },
11956
11957     updateComplete : function(response){
11958         this.fireEvent("update", this.el, response);
11959         if(typeof response.argument.callback == "function"){
11960             response.argument.callback(this.el, true, response);
11961         }
11962     },
11963
11964     /**
11965      * @private
11966      */
11967     processFailure : function(response){
11968         this.transaction = null;
11969         this.fireEvent("failure", this.el, response);
11970         if(typeof response.argument.callback == "function"){
11971             response.argument.callback(this.el, false, response);
11972         }
11973     },
11974
11975     /**
11976      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11977      * @param {Object} renderer The object implementing the render() method
11978      */
11979     setRenderer : function(renderer){
11980         this.renderer = renderer;
11981     },
11982
11983     getRenderer : function(){
11984        return this.renderer;
11985     },
11986
11987     /**
11988      * Set the defaultUrl used for updates
11989      * @param {String/Function} defaultUrl The url or a function to call to get the url
11990      */
11991     setDefaultUrl : function(defaultUrl){
11992         this.defaultUrl = defaultUrl;
11993     },
11994
11995     /**
11996      * Aborts the executing transaction
11997      */
11998     abort : function(){
11999         if(this.transaction){
12000             Roo.Ajax.abort(this.transaction);
12001         }
12002     },
12003
12004     /**
12005      * Returns true if an update is in progress
12006      * @return {Boolean}
12007      */
12008     isUpdating : function(){
12009         if(this.transaction){
12010             return Roo.Ajax.isLoading(this.transaction);
12011         }
12012         return false;
12013     }
12014 });
12015
12016 /**
12017  * @class Roo.UpdateManager.defaults
12018  * @static (not really - but it helps the doc tool)
12019  * The defaults collection enables customizing the default properties of UpdateManager
12020  */
12021    Roo.UpdateManager.defaults = {
12022        /**
12023          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12024          * @type Number
12025          */
12026          timeout : 30,
12027
12028          /**
12029          * True to process scripts by default (Defaults to false).
12030          * @type Boolean
12031          */
12032         loadScripts : false,
12033
12034         /**
12035         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12036         * @type String
12037         */
12038         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12039         /**
12040          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12041          * @type Boolean
12042          */
12043         disableCaching : false,
12044         /**
12045          * Whether to show indicatorText when loading (Defaults to true).
12046          * @type Boolean
12047          */
12048         showLoadIndicator : true,
12049         /**
12050          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12051          * @type String
12052          */
12053         indicatorText : '<div class="loading-indicator">Loading...</div>'
12054    };
12055
12056 /**
12057  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12058  *Usage:
12059  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12060  * @param {String/HTMLElement/Roo.Element} el The element to update
12061  * @param {String} url The url
12062  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12063  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12064  * @static
12065  * @deprecated
12066  * @member Roo.UpdateManager
12067  */
12068 Roo.UpdateManager.updateElement = function(el, url, params, options){
12069     var um = Roo.get(el, true).getUpdateManager();
12070     Roo.apply(um, options);
12071     um.update(url, params, options ? options.callback : null);
12072 };
12073 // alias for backwards compat
12074 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12075 /**
12076  * @class Roo.UpdateManager.BasicRenderer
12077  * Default Content renderer. Updates the elements innerHTML with the responseText.
12078  */
12079 Roo.UpdateManager.BasicRenderer = function(){};
12080
12081 Roo.UpdateManager.BasicRenderer.prototype = {
12082     /**
12083      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12084      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12085      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12086      * @param {Roo.Element} el The element being rendered
12087      * @param {Object} response The YUI Connect response object
12088      * @param {UpdateManager} updateManager The calling update manager
12089      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12090      */
12091      render : function(el, response, updateManager, callback){
12092         el.update(response.responseText, updateManager.loadScripts, callback);
12093     }
12094 };
12095 /*
12096  * Based on:
12097  * Ext JS Library 1.1.1
12098  * Copyright(c) 2006-2007, Ext JS, LLC.
12099  *
12100  * Originally Released Under LGPL - original licence link has changed is not relivant.
12101  *
12102  * Fork - LGPL
12103  * <script type="text/javascript">
12104  */
12105
12106 /**
12107  * @class Roo.util.DelayedTask
12108  * Provides a convenient method of performing setTimeout where a new
12109  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12110  * You can use this class to buffer
12111  * the keypress events for a certain number of milliseconds, and perform only if they stop
12112  * for that amount of time.
12113  * @constructor The parameters to this constructor serve as defaults and are not required.
12114  * @param {Function} fn (optional) The default function to timeout
12115  * @param {Object} scope (optional) The default scope of that timeout
12116  * @param {Array} args (optional) The default Array of arguments
12117  */
12118 Roo.util.DelayedTask = function(fn, scope, args){
12119     var id = null, d, t;
12120
12121     var call = function(){
12122         var now = new Date().getTime();
12123         if(now - t >= d){
12124             clearInterval(id);
12125             id = null;
12126             fn.apply(scope, args || []);
12127         }
12128     };
12129     /**
12130      * Cancels any pending timeout and queues a new one
12131      * @param {Number} delay The milliseconds to delay
12132      * @param {Function} newFn (optional) Overrides function passed to constructor
12133      * @param {Object} newScope (optional) Overrides scope passed to constructor
12134      * @param {Array} newArgs (optional) Overrides args passed to constructor
12135      */
12136     this.delay = function(delay, newFn, newScope, newArgs){
12137         if(id && delay != d){
12138             this.cancel();
12139         }
12140         d = delay;
12141         t = new Date().getTime();
12142         fn = newFn || fn;
12143         scope = newScope || scope;
12144         args = newArgs || args;
12145         if(!id){
12146             id = setInterval(call, d);
12147         }
12148     };
12149
12150     /**
12151      * Cancel the last queued timeout
12152      */
12153     this.cancel = function(){
12154         if(id){
12155             clearInterval(id);
12156             id = null;
12157         }
12158     };
12159 };/*
12160  * Based on:
12161  * Ext JS Library 1.1.1
12162  * Copyright(c) 2006-2007, Ext JS, LLC.
12163  *
12164  * Originally Released Under LGPL - original licence link has changed is not relivant.
12165  *
12166  * Fork - LGPL
12167  * <script type="text/javascript">
12168  */
12169  
12170  
12171 Roo.util.TaskRunner = function(interval){
12172     interval = interval || 10;
12173     var tasks = [], removeQueue = [];
12174     var id = 0;
12175     var running = false;
12176
12177     var stopThread = function(){
12178         running = false;
12179         clearInterval(id);
12180         id = 0;
12181     };
12182
12183     var startThread = function(){
12184         if(!running){
12185             running = true;
12186             id = setInterval(runTasks, interval);
12187         }
12188     };
12189
12190     var removeTask = function(task){
12191         removeQueue.push(task);
12192         if(task.onStop){
12193             task.onStop();
12194         }
12195     };
12196
12197     var runTasks = function(){
12198         if(removeQueue.length > 0){
12199             for(var i = 0, len = removeQueue.length; i < len; i++){
12200                 tasks.remove(removeQueue[i]);
12201             }
12202             removeQueue = [];
12203             if(tasks.length < 1){
12204                 stopThread();
12205                 return;
12206             }
12207         }
12208         var now = new Date().getTime();
12209         for(var i = 0, len = tasks.length; i < len; ++i){
12210             var t = tasks[i];
12211             var itime = now - t.taskRunTime;
12212             if(t.interval <= itime){
12213                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12214                 t.taskRunTime = now;
12215                 if(rt === false || t.taskRunCount === t.repeat){
12216                     removeTask(t);
12217                     return;
12218                 }
12219             }
12220             if(t.duration && t.duration <= (now - t.taskStartTime)){
12221                 removeTask(t);
12222             }
12223         }
12224     };
12225
12226     /**
12227      * Queues a new task.
12228      * @param {Object} task
12229      */
12230     this.start = function(task){
12231         tasks.push(task);
12232         task.taskStartTime = new Date().getTime();
12233         task.taskRunTime = 0;
12234         task.taskRunCount = 0;
12235         startThread();
12236         return task;
12237     };
12238
12239     this.stop = function(task){
12240         removeTask(task);
12241         return task;
12242     };
12243
12244     this.stopAll = function(){
12245         stopThread();
12246         for(var i = 0, len = tasks.length; i < len; i++){
12247             if(tasks[i].onStop){
12248                 tasks[i].onStop();
12249             }
12250         }
12251         tasks = [];
12252         removeQueue = [];
12253     };
12254 };
12255
12256 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12257  * Based on:
12258  * Ext JS Library 1.1.1
12259  * Copyright(c) 2006-2007, Ext JS, LLC.
12260  *
12261  * Originally Released Under LGPL - original licence link has changed is not relivant.
12262  *
12263  * Fork - LGPL
12264  * <script type="text/javascript">
12265  */
12266
12267  
12268 /**
12269  * @class Roo.util.MixedCollection
12270  * @extends Roo.util.Observable
12271  * A Collection class that maintains both numeric indexes and keys and exposes events.
12272  * @constructor
12273  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12274  * collection (defaults to false)
12275  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12276  * and return the key value for that item.  This is used when available to look up the key on items that
12277  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12278  * equivalent to providing an implementation for the {@link #getKey} method.
12279  */
12280 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12281     this.items = [];
12282     this.map = {};
12283     this.keys = [];
12284     this.length = 0;
12285     this.addEvents({
12286         /**
12287          * @event clear
12288          * Fires when the collection is cleared.
12289          */
12290         "clear" : true,
12291         /**
12292          * @event add
12293          * Fires when an item is added to the collection.
12294          * @param {Number} index The index at which the item was added.
12295          * @param {Object} o The item added.
12296          * @param {String} key The key associated with the added item.
12297          */
12298         "add" : true,
12299         /**
12300          * @event replace
12301          * Fires when an item is replaced in the collection.
12302          * @param {String} key he key associated with the new added.
12303          * @param {Object} old The item being replaced.
12304          * @param {Object} new The new item.
12305          */
12306         "replace" : true,
12307         /**
12308          * @event remove
12309          * Fires when an item is removed from the collection.
12310          * @param {Object} o The item being removed.
12311          * @param {String} key (optional) The key associated with the removed item.
12312          */
12313         "remove" : true,
12314         "sort" : true
12315     });
12316     this.allowFunctions = allowFunctions === true;
12317     if(keyFn){
12318         this.getKey = keyFn;
12319     }
12320     Roo.util.MixedCollection.superclass.constructor.call(this);
12321 };
12322
12323 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12324     allowFunctions : false,
12325     
12326 /**
12327  * Adds an item to the collection.
12328  * @param {String} key The key to associate with the item
12329  * @param {Object} o The item to add.
12330  * @return {Object} The item added.
12331  */
12332     add : function(key, o){
12333         if(arguments.length == 1){
12334             o = arguments[0];
12335             key = this.getKey(o);
12336         }
12337         if(typeof key == "undefined" || key === null){
12338             this.length++;
12339             this.items.push(o);
12340             this.keys.push(null);
12341         }else{
12342             var old = this.map[key];
12343             if(old){
12344                 return this.replace(key, o);
12345             }
12346             this.length++;
12347             this.items.push(o);
12348             this.map[key] = o;
12349             this.keys.push(key);
12350         }
12351         this.fireEvent("add", this.length-1, o, key);
12352         return o;
12353     },
12354        
12355 /**
12356   * MixedCollection has a generic way to fetch keys if you implement getKey.
12357 <pre><code>
12358 // normal way
12359 var mc = new Roo.util.MixedCollection();
12360 mc.add(someEl.dom.id, someEl);
12361 mc.add(otherEl.dom.id, otherEl);
12362 //and so on
12363
12364 // using getKey
12365 var mc = new Roo.util.MixedCollection();
12366 mc.getKey = function(el){
12367    return el.dom.id;
12368 };
12369 mc.add(someEl);
12370 mc.add(otherEl);
12371
12372 // or via the constructor
12373 var mc = new Roo.util.MixedCollection(false, function(el){
12374    return el.dom.id;
12375 });
12376 mc.add(someEl);
12377 mc.add(otherEl);
12378 </code></pre>
12379  * @param o {Object} The item for which to find the key.
12380  * @return {Object} The key for the passed item.
12381  */
12382     getKey : function(o){
12383          return o.id; 
12384     },
12385    
12386 /**
12387  * Replaces an item in the collection.
12388  * @param {String} key The key associated with the item to replace, or the item to replace.
12389  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12390  * @return {Object}  The new item.
12391  */
12392     replace : function(key, o){
12393         if(arguments.length == 1){
12394             o = arguments[0];
12395             key = this.getKey(o);
12396         }
12397         var old = this.item(key);
12398         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12399              return this.add(key, o);
12400         }
12401         var index = this.indexOfKey(key);
12402         this.items[index] = o;
12403         this.map[key] = o;
12404         this.fireEvent("replace", key, old, o);
12405         return o;
12406     },
12407    
12408 /**
12409  * Adds all elements of an Array or an Object to the collection.
12410  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12411  * an Array of values, each of which are added to the collection.
12412  */
12413     addAll : function(objs){
12414         if(arguments.length > 1 || objs instanceof Array){
12415             var args = arguments.length > 1 ? arguments : objs;
12416             for(var i = 0, len = args.length; i < len; i++){
12417                 this.add(args[i]);
12418             }
12419         }else{
12420             for(var key in objs){
12421                 if(this.allowFunctions || typeof objs[key] != "function"){
12422                     this.add(key, objs[key]);
12423                 }
12424             }
12425         }
12426     },
12427    
12428 /**
12429  * Executes the specified function once for every item in the collection, passing each
12430  * item as the first and only parameter. returning false from the function will stop the iteration.
12431  * @param {Function} fn The function to execute for each item.
12432  * @param {Object} scope (optional) The scope in which to execute the function.
12433  */
12434     each : function(fn, scope){
12435         var items = [].concat(this.items); // each safe for removal
12436         for(var i = 0, len = items.length; i < len; i++){
12437             if(fn.call(scope || items[i], items[i], i, len) === false){
12438                 break;
12439             }
12440         }
12441     },
12442    
12443 /**
12444  * Executes the specified function once for every key in the collection, passing each
12445  * key, and its associated item as the first two parameters.
12446  * @param {Function} fn The function to execute for each item.
12447  * @param {Object} scope (optional) The scope in which to execute the function.
12448  */
12449     eachKey : function(fn, scope){
12450         for(var i = 0, len = this.keys.length; i < len; i++){
12451             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12452         }
12453     },
12454    
12455 /**
12456  * Returns the first item in the collection which elicits a true return value from the
12457  * passed selection function.
12458  * @param {Function} fn The selection function to execute for each item.
12459  * @param {Object} scope (optional) The scope in which to execute the function.
12460  * @return {Object} The first item in the collection which returned true from the selection function.
12461  */
12462     find : function(fn, scope){
12463         for(var i = 0, len = this.items.length; i < len; i++){
12464             if(fn.call(scope || window, this.items[i], this.keys[i])){
12465                 return this.items[i];
12466             }
12467         }
12468         return null;
12469     },
12470    
12471 /**
12472  * Inserts an item at the specified index in the collection.
12473  * @param {Number} index The index to insert the item at.
12474  * @param {String} key The key to associate with the new item, or the item itself.
12475  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12476  * @return {Object} The item inserted.
12477  */
12478     insert : function(index, key, o){
12479         if(arguments.length == 2){
12480             o = arguments[1];
12481             key = this.getKey(o);
12482         }
12483         if(index >= this.length){
12484             return this.add(key, o);
12485         }
12486         this.length++;
12487         this.items.splice(index, 0, o);
12488         if(typeof key != "undefined" && key != null){
12489             this.map[key] = o;
12490         }
12491         this.keys.splice(index, 0, key);
12492         this.fireEvent("add", index, o, key);
12493         return o;
12494     },
12495    
12496 /**
12497  * Removed an item from the collection.
12498  * @param {Object} o The item to remove.
12499  * @return {Object} The item removed.
12500  */
12501     remove : function(o){
12502         return this.removeAt(this.indexOf(o));
12503     },
12504    
12505 /**
12506  * Remove an item from a specified index in the collection.
12507  * @param {Number} index The index within the collection of the item to remove.
12508  */
12509     removeAt : function(index){
12510         if(index < this.length && index >= 0){
12511             this.length--;
12512             var o = this.items[index];
12513             this.items.splice(index, 1);
12514             var key = this.keys[index];
12515             if(typeof key != "undefined"){
12516                 delete this.map[key];
12517             }
12518             this.keys.splice(index, 1);
12519             this.fireEvent("remove", o, key);
12520         }
12521     },
12522    
12523 /**
12524  * Removed an item associated with the passed key fom the collection.
12525  * @param {String} key The key of the item to remove.
12526  */
12527     removeKey : function(key){
12528         return this.removeAt(this.indexOfKey(key));
12529     },
12530    
12531 /**
12532  * Returns the number of items in the collection.
12533  * @return {Number} the number of items in the collection.
12534  */
12535     getCount : function(){
12536         return this.length; 
12537     },
12538    
12539 /**
12540  * Returns index within the collection of the passed Object.
12541  * @param {Object} o The item to find the index of.
12542  * @return {Number} index of the item.
12543  */
12544     indexOf : function(o){
12545         if(!this.items.indexOf){
12546             for(var i = 0, len = this.items.length; i < len; i++){
12547                 if(this.items[i] == o) return i;
12548             }
12549             return -1;
12550         }else{
12551             return this.items.indexOf(o);
12552         }
12553     },
12554    
12555 /**
12556  * Returns index within the collection of the passed key.
12557  * @param {String} key The key to find the index of.
12558  * @return {Number} index of the key.
12559  */
12560     indexOfKey : function(key){
12561         if(!this.keys.indexOf){
12562             for(var i = 0, len = this.keys.length; i < len; i++){
12563                 if(this.keys[i] == key) return i;
12564             }
12565             return -1;
12566         }else{
12567             return this.keys.indexOf(key);
12568         }
12569     },
12570    
12571 /**
12572  * Returns the item associated with the passed key OR index. Key has priority over index.
12573  * @param {String/Number} key The key or index of the item.
12574  * @return {Object} The item associated with the passed key.
12575  */
12576     item : function(key){
12577         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12578         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12579     },
12580     
12581 /**
12582  * Returns the item at the specified index.
12583  * @param {Number} index The index of the item.
12584  * @return {Object}
12585  */
12586     itemAt : function(index){
12587         return this.items[index];
12588     },
12589     
12590 /**
12591  * Returns the item associated with the passed key.
12592  * @param {String/Number} key The key of the item.
12593  * @return {Object} The item associated with the passed key.
12594  */
12595     key : function(key){
12596         return this.map[key];
12597     },
12598    
12599 /**
12600  * Returns true if the collection contains the passed Object as an item.
12601  * @param {Object} o  The Object to look for in the collection.
12602  * @return {Boolean} True if the collection contains the Object as an item.
12603  */
12604     contains : function(o){
12605         return this.indexOf(o) != -1;
12606     },
12607    
12608 /**
12609  * Returns true if the collection contains the passed Object as a key.
12610  * @param {String} key The key to look for in the collection.
12611  * @return {Boolean} True if the collection contains the Object as a key.
12612  */
12613     containsKey : function(key){
12614         return typeof this.map[key] != "undefined";
12615     },
12616    
12617 /**
12618  * Removes all items from the collection.
12619  */
12620     clear : function(){
12621         this.length = 0;
12622         this.items = [];
12623         this.keys = [];
12624         this.map = {};
12625         this.fireEvent("clear");
12626     },
12627    
12628 /**
12629  * Returns the first item in the collection.
12630  * @return {Object} the first item in the collection..
12631  */
12632     first : function(){
12633         return this.items[0]; 
12634     },
12635    
12636 /**
12637  * Returns the last item in the collection.
12638  * @return {Object} the last item in the collection..
12639  */
12640     last : function(){
12641         return this.items[this.length-1];   
12642     },
12643     
12644     _sort : function(property, dir, fn){
12645         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12646         fn = fn || function(a, b){
12647             return a-b;
12648         };
12649         var c = [], k = this.keys, items = this.items;
12650         for(var i = 0, len = items.length; i < len; i++){
12651             c[c.length] = {key: k[i], value: items[i], index: i};
12652         }
12653         c.sort(function(a, b){
12654             var v = fn(a[property], b[property]) * dsc;
12655             if(v == 0){
12656                 v = (a.index < b.index ? -1 : 1);
12657             }
12658             return v;
12659         });
12660         for(var i = 0, len = c.length; i < len; i++){
12661             items[i] = c[i].value;
12662             k[i] = c[i].key;
12663         }
12664         this.fireEvent("sort", this);
12665     },
12666     
12667     /**
12668      * Sorts this collection with the passed comparison function
12669      * @param {String} direction (optional) "ASC" or "DESC"
12670      * @param {Function} fn (optional) comparison function
12671      */
12672     sort : function(dir, fn){
12673         this._sort("value", dir, fn);
12674     },
12675     
12676     /**
12677      * Sorts this collection by keys
12678      * @param {String} direction (optional) "ASC" or "DESC"
12679      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12680      */
12681     keySort : function(dir, fn){
12682         this._sort("key", dir, fn || function(a, b){
12683             return String(a).toUpperCase()-String(b).toUpperCase();
12684         });
12685     },
12686     
12687     /**
12688      * Returns a range of items in this collection
12689      * @param {Number} startIndex (optional) defaults to 0
12690      * @param {Number} endIndex (optional) default to the last item
12691      * @return {Array} An array of items
12692      */
12693     getRange : function(start, end){
12694         var items = this.items;
12695         if(items.length < 1){
12696             return [];
12697         }
12698         start = start || 0;
12699         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12700         var r = [];
12701         if(start <= end){
12702             for(var i = start; i <= end; i++) {
12703                     r[r.length] = items[i];
12704             }
12705         }else{
12706             for(var i = start; i >= end; i--) {
12707                     r[r.length] = items[i];
12708             }
12709         }
12710         return r;
12711     },
12712         
12713     /**
12714      * Filter the <i>objects</i> in this collection by a specific property. 
12715      * Returns a new collection that has been filtered.
12716      * @param {String} property A property on your objects
12717      * @param {String/RegExp} value Either string that the property values 
12718      * should start with or a RegExp to test against the property
12719      * @return {MixedCollection} The new filtered collection
12720      */
12721     filter : function(property, value){
12722         if(!value.exec){ // not a regex
12723             value = String(value);
12724             if(value.length == 0){
12725                 return this.clone();
12726             }
12727             value = new RegExp("^" + Roo.escapeRe(value), "i");
12728         }
12729         return this.filterBy(function(o){
12730             return o && value.test(o[property]);
12731         });
12732         },
12733     
12734     /**
12735      * Filter by a function. * Returns a new collection that has been filtered.
12736      * The passed function will be called with each 
12737      * object in the collection. If the function returns true, the value is included 
12738      * otherwise it is filtered.
12739      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12740      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12741      * @return {MixedCollection} The new filtered collection
12742      */
12743     filterBy : function(fn, scope){
12744         var r = new Roo.util.MixedCollection();
12745         r.getKey = this.getKey;
12746         var k = this.keys, it = this.items;
12747         for(var i = 0, len = it.length; i < len; i++){
12748             if(fn.call(scope||this, it[i], k[i])){
12749                                 r.add(k[i], it[i]);
12750                         }
12751         }
12752         return r;
12753     },
12754     
12755     /**
12756      * Creates a duplicate of this collection
12757      * @return {MixedCollection}
12758      */
12759     clone : function(){
12760         var r = new Roo.util.MixedCollection();
12761         var k = this.keys, it = this.items;
12762         for(var i = 0, len = it.length; i < len; i++){
12763             r.add(k[i], it[i]);
12764         }
12765         r.getKey = this.getKey;
12766         return r;
12767     }
12768 });
12769 /**
12770  * Returns the item associated with the passed key or index.
12771  * @method
12772  * @param {String/Number} key The key or index of the item.
12773  * @return {Object} The item associated with the passed key.
12774  */
12775 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12776  * Based on:
12777  * Ext JS Library 1.1.1
12778  * Copyright(c) 2006-2007, Ext JS, LLC.
12779  *
12780  * Originally Released Under LGPL - original licence link has changed is not relivant.
12781  *
12782  * Fork - LGPL
12783  * <script type="text/javascript">
12784  */
12785 /**
12786  * @class Roo.util.JSON
12787  * Modified version of Douglas Crockford"s json.js that doesn"t
12788  * mess with the Object prototype 
12789  * http://www.json.org/js.html
12790  * @singleton
12791  */
12792 Roo.util.JSON = new (function(){
12793     var useHasOwn = {}.hasOwnProperty ? true : false;
12794     
12795     // crashes Safari in some instances
12796     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12797     
12798     var pad = function(n) {
12799         return n < 10 ? "0" + n : n;
12800     };
12801     
12802     var m = {
12803         "\b": '\\b',
12804         "\t": '\\t',
12805         "\n": '\\n',
12806         "\f": '\\f',
12807         "\r": '\\r',
12808         '"' : '\\"',
12809         "\\": '\\\\'
12810     };
12811
12812     var encodeString = function(s){
12813         if (/["\\\x00-\x1f]/.test(s)) {
12814             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12815                 var c = m[b];
12816                 if(c){
12817                     return c;
12818                 }
12819                 c = b.charCodeAt();
12820                 return "\\u00" +
12821                     Math.floor(c / 16).toString(16) +
12822                     (c % 16).toString(16);
12823             }) + '"';
12824         }
12825         return '"' + s + '"';
12826     };
12827     
12828     var encodeArray = function(o){
12829         var a = ["["], b, i, l = o.length, v;
12830             for (i = 0; i < l; i += 1) {
12831                 v = o[i];
12832                 switch (typeof v) {
12833                     case "undefined":
12834                     case "function":
12835                     case "unknown":
12836                         break;
12837                     default:
12838                         if (b) {
12839                             a.push(',');
12840                         }
12841                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12842                         b = true;
12843                 }
12844             }
12845             a.push("]");
12846             return a.join("");
12847     };
12848     
12849     var encodeDate = function(o){
12850         return '"' + o.getFullYear() + "-" +
12851                 pad(o.getMonth() + 1) + "-" +
12852                 pad(o.getDate()) + "T" +
12853                 pad(o.getHours()) + ":" +
12854                 pad(o.getMinutes()) + ":" +
12855                 pad(o.getSeconds()) + '"';
12856     };
12857     
12858     /**
12859      * Encodes an Object, Array or other value
12860      * @param {Mixed} o The variable to encode
12861      * @return {String} The JSON string
12862      */
12863     this.encode = function(o)
12864     {
12865         // should this be extended to fully wrap stringify..
12866         
12867         if(typeof o == "undefined" || o === null){
12868             return "null";
12869         }else if(o instanceof Array){
12870             return encodeArray(o);
12871         }else if(o instanceof Date){
12872             return encodeDate(o);
12873         }else if(typeof o == "string"){
12874             return encodeString(o);
12875         }else if(typeof o == "number"){
12876             return isFinite(o) ? String(o) : "null";
12877         }else if(typeof o == "boolean"){
12878             return String(o);
12879         }else {
12880             var a = ["{"], b, i, v;
12881             for (i in o) {
12882                 if(!useHasOwn || o.hasOwnProperty(i)) {
12883                     v = o[i];
12884                     switch (typeof v) {
12885                     case "undefined":
12886                     case "function":
12887                     case "unknown":
12888                         break;
12889                     default:
12890                         if(b){
12891                             a.push(',');
12892                         }
12893                         a.push(this.encode(i), ":",
12894                                 v === null ? "null" : this.encode(v));
12895                         b = true;
12896                     }
12897                 }
12898             }
12899             a.push("}");
12900             return a.join("");
12901         }
12902     };
12903     
12904     /**
12905      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12906      * @param {String} json The JSON string
12907      * @return {Object} The resulting object
12908      */
12909     this.decode = function(json){
12910         
12911         return  /** eval:var:json */ eval("(" + json + ')');
12912     };
12913 })();
12914 /** 
12915  * Shorthand for {@link Roo.util.JSON#encode}
12916  * @member Roo encode 
12917  * @method */
12918 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12919 /** 
12920  * Shorthand for {@link Roo.util.JSON#decode}
12921  * @member Roo decode 
12922  * @method */
12923 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12924 /*
12925  * Based on:
12926  * Ext JS Library 1.1.1
12927  * Copyright(c) 2006-2007, Ext JS, LLC.
12928  *
12929  * Originally Released Under LGPL - original licence link has changed is not relivant.
12930  *
12931  * Fork - LGPL
12932  * <script type="text/javascript">
12933  */
12934  
12935 /**
12936  * @class Roo.util.Format
12937  * Reusable data formatting functions
12938  * @singleton
12939  */
12940 Roo.util.Format = function(){
12941     var trimRe = /^\s+|\s+$/g;
12942     return {
12943         /**
12944          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12945          * @param {String} value The string to truncate
12946          * @param {Number} length The maximum length to allow before truncating
12947          * @return {String} The converted text
12948          */
12949         ellipsis : function(value, len){
12950             if(value && value.length > len){
12951                 return value.substr(0, len-3)+"...";
12952             }
12953             return value;
12954         },
12955
12956         /**
12957          * Checks a reference and converts it to empty string if it is undefined
12958          * @param {Mixed} value Reference to check
12959          * @return {Mixed} Empty string if converted, otherwise the original value
12960          */
12961         undef : function(value){
12962             return typeof value != "undefined" ? value : "";
12963         },
12964
12965         /**
12966          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12967          * @param {String} value The string to encode
12968          * @return {String} The encoded text
12969          */
12970         htmlEncode : function(value){
12971             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12972         },
12973
12974         /**
12975          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12976          * @param {String} value The string to decode
12977          * @return {String} The decoded text
12978          */
12979         htmlDecode : function(value){
12980             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12981         },
12982
12983         /**
12984          * Trims any whitespace from either side of a string
12985          * @param {String} value The text to trim
12986          * @return {String} The trimmed text
12987          */
12988         trim : function(value){
12989             return String(value).replace(trimRe, "");
12990         },
12991
12992         /**
12993          * Returns a substring from within an original string
12994          * @param {String} value The original text
12995          * @param {Number} start The start index of the substring
12996          * @param {Number} length The length of the substring
12997          * @return {String} The substring
12998          */
12999         substr : function(value, start, length){
13000             return String(value).substr(start, length);
13001         },
13002
13003         /**
13004          * Converts a string to all lower case letters
13005          * @param {String} value The text to convert
13006          * @return {String} The converted text
13007          */
13008         lowercase : function(value){
13009             return String(value).toLowerCase();
13010         },
13011
13012         /**
13013          * Converts a string to all upper case letters
13014          * @param {String} value The text to convert
13015          * @return {String} The converted text
13016          */
13017         uppercase : function(value){
13018             return String(value).toUpperCase();
13019         },
13020
13021         /**
13022          * Converts the first character only of a string to upper case
13023          * @param {String} value The text to convert
13024          * @return {String} The converted text
13025          */
13026         capitalize : function(value){
13027             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13028         },
13029
13030         // private
13031         call : function(value, fn){
13032             if(arguments.length > 2){
13033                 var args = Array.prototype.slice.call(arguments, 2);
13034                 args.unshift(value);
13035                  
13036                 return /** eval:var:value */  eval(fn).apply(window, args);
13037             }else{
13038                 /** eval:var:value */
13039                 return /** eval:var:value */ eval(fn).call(window, value);
13040             }
13041         },
13042
13043        
13044         /**
13045          * safer version of Math.toFixed..??/
13046          * @param {Number/String} value The numeric value to format
13047          * @param {Number/String} value Decimal places 
13048          * @return {String} The formatted currency string
13049          */
13050         toFixed : function(v, n)
13051         {
13052             // why not use to fixed - precision is buggered???
13053             if (!n) {
13054                 return Math.round(v-0);
13055             }
13056             var fact = Math.pow(10,n+1);
13057             v = (Math.round((v-0)*fact))/fact;
13058             var z = (''+fact).substring(2);
13059             if (v == Math.floor(v)) {
13060                 return Math.floor(v) + '.' + z;
13061             }
13062             
13063             // now just padd decimals..
13064             var ps = String(v).split('.');
13065             var fd = (ps[1] + z);
13066             var r = fd.substring(0,n); 
13067             var rm = fd.substring(n); 
13068             if (rm < 5) {
13069                 return ps[0] + '.' + r;
13070             }
13071             r*=1; // turn it into a number;
13072             r++;
13073             if (String(r).length != n) {
13074                 ps[0]*=1;
13075                 ps[0]++;
13076                 r = String(r).substring(1); // chop the end off.
13077             }
13078             
13079             return ps[0] + '.' + r;
13080              
13081         },
13082         
13083         /**
13084          * Format a number as US currency
13085          * @param {Number/String} value The numeric value to format
13086          * @return {String} The formatted currency string
13087          */
13088         usMoney : function(v){
13089             v = (Math.round((v-0)*100))/100;
13090             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13091             v = String(v);
13092             var ps = v.split('.');
13093             var whole = ps[0];
13094             var sub = ps[1] ? '.'+ ps[1] : '.00';
13095             var r = /(\d+)(\d{3})/;
13096             while (r.test(whole)) {
13097                 whole = whole.replace(r, '$1' + ',' + '$2');
13098             }
13099             return "$" + whole + sub ;
13100         },
13101         
13102         /**
13103          * Parse a value into a formatted date using the specified format pattern.
13104          * @param {Mixed} value The value to format
13105          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13106          * @return {String} The formatted date string
13107          */
13108         date : function(v, format){
13109             if(!v){
13110                 return "";
13111             }
13112             if(!(v instanceof Date)){
13113                 v = new Date(Date.parse(v));
13114             }
13115             return v.dateFormat(format || "m/d/Y");
13116         },
13117
13118         /**
13119          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13120          * @param {String} format Any valid date format string
13121          * @return {Function} The date formatting function
13122          */
13123         dateRenderer : function(format){
13124             return function(v){
13125                 return Roo.util.Format.date(v, format);  
13126             };
13127         },
13128
13129         // private
13130         stripTagsRE : /<\/?[^>]+>/gi,
13131         
13132         /**
13133          * Strips all HTML tags
13134          * @param {Mixed} value The text from which to strip tags
13135          * @return {String} The stripped text
13136          */
13137         stripTags : function(v){
13138             return !v ? v : String(v).replace(this.stripTagsRE, "");
13139         }
13140     };
13141 }();/*
13142  * Based on:
13143  * Ext JS Library 1.1.1
13144  * Copyright(c) 2006-2007, Ext JS, LLC.
13145  *
13146  * Originally Released Under LGPL - original licence link has changed is not relivant.
13147  *
13148  * Fork - LGPL
13149  * <script type="text/javascript">
13150  */
13151
13152
13153  
13154
13155 /**
13156  * @class Roo.MasterTemplate
13157  * @extends Roo.Template
13158  * Provides a template that can have child templates. The syntax is:
13159 <pre><code>
13160 var t = new Roo.MasterTemplate(
13161         '&lt;select name="{name}"&gt;',
13162                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13163         '&lt;/select&gt;'
13164 );
13165 t.add('options', {value: 'foo', text: 'bar'});
13166 // or you can add multiple child elements in one shot
13167 t.addAll('options', [
13168     {value: 'foo', text: 'bar'},
13169     {value: 'foo2', text: 'bar2'},
13170     {value: 'foo3', text: 'bar3'}
13171 ]);
13172 // then append, applying the master template values
13173 t.append('my-form', {name: 'my-select'});
13174 </code></pre>
13175 * A name attribute for the child template is not required if you have only one child
13176 * template or you want to refer to them by index.
13177  */
13178 Roo.MasterTemplate = function(){
13179     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13180     this.originalHtml = this.html;
13181     var st = {};
13182     var m, re = this.subTemplateRe;
13183     re.lastIndex = 0;
13184     var subIndex = 0;
13185     while(m = re.exec(this.html)){
13186         var name = m[1], content = m[2];
13187         st[subIndex] = {
13188             name: name,
13189             index: subIndex,
13190             buffer: [],
13191             tpl : new Roo.Template(content)
13192         };
13193         if(name){
13194             st[name] = st[subIndex];
13195         }
13196         st[subIndex].tpl.compile();
13197         st[subIndex].tpl.call = this.call.createDelegate(this);
13198         subIndex++;
13199     }
13200     this.subCount = subIndex;
13201     this.subs = st;
13202 };
13203 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13204     /**
13205     * The regular expression used to match sub templates
13206     * @type RegExp
13207     * @property
13208     */
13209     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13210
13211     /**
13212      * Applies the passed values to a child template.
13213      * @param {String/Number} name (optional) The name or index of the child template
13214      * @param {Array/Object} values The values to be applied to the template
13215      * @return {MasterTemplate} this
13216      */
13217      add : function(name, values){
13218         if(arguments.length == 1){
13219             values = arguments[0];
13220             name = 0;
13221         }
13222         var s = this.subs[name];
13223         s.buffer[s.buffer.length] = s.tpl.apply(values);
13224         return this;
13225     },
13226
13227     /**
13228      * Applies all the passed values to a child template.
13229      * @param {String/Number} name (optional) The name or index of the child template
13230      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13231      * @param {Boolean} reset (optional) True to reset the template first
13232      * @return {MasterTemplate} this
13233      */
13234     fill : function(name, values, reset){
13235         var a = arguments;
13236         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13237             values = a[0];
13238             name = 0;
13239             reset = a[1];
13240         }
13241         if(reset){
13242             this.reset();
13243         }
13244         for(var i = 0, len = values.length; i < len; i++){
13245             this.add(name, values[i]);
13246         }
13247         return this;
13248     },
13249
13250     /**
13251      * Resets the template for reuse
13252      * @return {MasterTemplate} this
13253      */
13254      reset : function(){
13255         var s = this.subs;
13256         for(var i = 0; i < this.subCount; i++){
13257             s[i].buffer = [];
13258         }
13259         return this;
13260     },
13261
13262     applyTemplate : function(values){
13263         var s = this.subs;
13264         var replaceIndex = -1;
13265         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13266             return s[++replaceIndex].buffer.join("");
13267         });
13268         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13269     },
13270
13271     apply : function(){
13272         return this.applyTemplate.apply(this, arguments);
13273     },
13274
13275     compile : function(){return this;}
13276 });
13277
13278 /**
13279  * Alias for fill().
13280  * @method
13281  */
13282 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13283  /**
13284  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13285  * var tpl = Roo.MasterTemplate.from('element-id');
13286  * @param {String/HTMLElement} el
13287  * @param {Object} config
13288  * @static
13289  */
13290 Roo.MasterTemplate.from = function(el, config){
13291     el = Roo.getDom(el);
13292     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13293 };/*
13294  * Based on:
13295  * Ext JS Library 1.1.1
13296  * Copyright(c) 2006-2007, Ext JS, LLC.
13297  *
13298  * Originally Released Under LGPL - original licence link has changed is not relivant.
13299  *
13300  * Fork - LGPL
13301  * <script type="text/javascript">
13302  */
13303
13304  
13305 /**
13306  * @class Roo.util.CSS
13307  * Utility class for manipulating CSS rules
13308  * @singleton
13309  */
13310 Roo.util.CSS = function(){
13311         var rules = null;
13312         var doc = document;
13313
13314     var camelRe = /(-[a-z])/gi;
13315     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13316
13317    return {
13318    /**
13319     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13320     * tag and appended to the HEAD of the document.
13321     * @param {String|Object} cssText The text containing the css rules
13322     * @param {String} id An id to add to the stylesheet for later removal
13323     * @return {StyleSheet}
13324     */
13325     createStyleSheet : function(cssText, id){
13326         var ss;
13327         var head = doc.getElementsByTagName("head")[0];
13328         var nrules = doc.createElement("style");
13329         nrules.setAttribute("type", "text/css");
13330         if(id){
13331             nrules.setAttribute("id", id);
13332         }
13333         if (typeof(cssText) != 'string') {
13334             // support object maps..
13335             // not sure if this a good idea.. 
13336             // perhaps it should be merged with the general css handling
13337             // and handle js style props.
13338             var cssTextNew = [];
13339             for(var n in cssText) {
13340                 var citems = [];
13341                 for(var k in cssText[n]) {
13342                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13343                 }
13344                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13345                 
13346             }
13347             cssText = cssTextNew.join("\n");
13348             
13349         }
13350        
13351        
13352        if(Roo.isIE){
13353            head.appendChild(nrules);
13354            ss = nrules.styleSheet;
13355            ss.cssText = cssText;
13356        }else{
13357            try{
13358                 nrules.appendChild(doc.createTextNode(cssText));
13359            }catch(e){
13360                nrules.cssText = cssText; 
13361            }
13362            head.appendChild(nrules);
13363            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13364        }
13365        this.cacheStyleSheet(ss);
13366        return ss;
13367    },
13368
13369    /**
13370     * Removes a style or link tag by id
13371     * @param {String} id The id of the tag
13372     */
13373    removeStyleSheet : function(id){
13374        var existing = doc.getElementById(id);
13375        if(existing){
13376            existing.parentNode.removeChild(existing);
13377        }
13378    },
13379
13380    /**
13381     * Dynamically swaps an existing stylesheet reference for a new one
13382     * @param {String} id The id of an existing link tag to remove
13383     * @param {String} url The href of the new stylesheet to include
13384     */
13385    swapStyleSheet : function(id, url){
13386        this.removeStyleSheet(id);
13387        var ss = doc.createElement("link");
13388        ss.setAttribute("rel", "stylesheet");
13389        ss.setAttribute("type", "text/css");
13390        ss.setAttribute("id", id);
13391        ss.setAttribute("href", url);
13392        doc.getElementsByTagName("head")[0].appendChild(ss);
13393    },
13394    
13395    /**
13396     * Refresh the rule cache if you have dynamically added stylesheets
13397     * @return {Object} An object (hash) of rules indexed by selector
13398     */
13399    refreshCache : function(){
13400        return this.getRules(true);
13401    },
13402
13403    // private
13404    cacheStyleSheet : function(stylesheet){
13405        if(!rules){
13406            rules = {};
13407        }
13408        try{// try catch for cross domain access issue
13409            var ssRules = stylesheet.cssRules || stylesheet.rules;
13410            for(var j = ssRules.length-1; j >= 0; --j){
13411                rules[ssRules[j].selectorText] = ssRules[j];
13412            }
13413        }catch(e){}
13414    },
13415    
13416    /**
13417     * Gets all css rules for the document
13418     * @param {Boolean} refreshCache true to refresh the internal cache
13419     * @return {Object} An object (hash) of rules indexed by selector
13420     */
13421    getRules : function(refreshCache){
13422                 if(rules == null || refreshCache){
13423                         rules = {};
13424                         var ds = doc.styleSheets;
13425                         for(var i =0, len = ds.length; i < len; i++){
13426                             try{
13427                         this.cacheStyleSheet(ds[i]);
13428                     }catch(e){} 
13429                 }
13430                 }
13431                 return rules;
13432         },
13433         
13434         /**
13435     * Gets an an individual CSS rule by selector(s)
13436     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13437     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13438     * @return {CSSRule} The CSS rule or null if one is not found
13439     */
13440    getRule : function(selector, refreshCache){
13441                 var rs = this.getRules(refreshCache);
13442                 if(!(selector instanceof Array)){
13443                     return rs[selector];
13444                 }
13445                 for(var i = 0; i < selector.length; i++){
13446                         if(rs[selector[i]]){
13447                                 return rs[selector[i]];
13448                         }
13449                 }
13450                 return null;
13451         },
13452         
13453         
13454         /**
13455     * Updates a rule property
13456     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13457     * @param {String} property The css property
13458     * @param {String} value The new value for the property
13459     * @return {Boolean} true If a rule was found and updated
13460     */
13461    updateRule : function(selector, property, value){
13462                 if(!(selector instanceof Array)){
13463                         var rule = this.getRule(selector);
13464                         if(rule){
13465                                 rule.style[property.replace(camelRe, camelFn)] = value;
13466                                 return true;
13467                         }
13468                 }else{
13469                         for(var i = 0; i < selector.length; i++){
13470                                 if(this.updateRule(selector[i], property, value)){
13471                                         return true;
13472                                 }
13473                         }
13474                 }
13475                 return false;
13476         }
13477    };   
13478 }();/*
13479  * Based on:
13480  * Ext JS Library 1.1.1
13481  * Copyright(c) 2006-2007, Ext JS, LLC.
13482  *
13483  * Originally Released Under LGPL - original licence link has changed is not relivant.
13484  *
13485  * Fork - LGPL
13486  * <script type="text/javascript">
13487  */
13488
13489  
13490
13491 /**
13492  * @class Roo.util.ClickRepeater
13493  * @extends Roo.util.Observable
13494  * 
13495  * A wrapper class which can be applied to any element. Fires a "click" event while the
13496  * mouse is pressed. The interval between firings may be specified in the config but
13497  * defaults to 10 milliseconds.
13498  * 
13499  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13500  * 
13501  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13502  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13503  * Similar to an autorepeat key delay.
13504  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13505  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13506  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13507  *           "interval" and "delay" are ignored. "immediate" is honored.
13508  * @cfg {Boolean} preventDefault True to prevent the default click event
13509  * @cfg {Boolean} stopDefault True to stop the default click event
13510  * 
13511  * @history
13512  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13513  *     2007-02-02 jvs Renamed to ClickRepeater
13514  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13515  *
13516  *  @constructor
13517  * @param {String/HTMLElement/Element} el The element to listen on
13518  * @param {Object} config
13519  **/
13520 Roo.util.ClickRepeater = function(el, config)
13521 {
13522     this.el = Roo.get(el);
13523     this.el.unselectable();
13524
13525     Roo.apply(this, config);
13526
13527     this.addEvents({
13528     /**
13529      * @event mousedown
13530      * Fires when the mouse button is depressed.
13531      * @param {Roo.util.ClickRepeater} this
13532      */
13533         "mousedown" : true,
13534     /**
13535      * @event click
13536      * Fires on a specified interval during the time the element is pressed.
13537      * @param {Roo.util.ClickRepeater} this
13538      */
13539         "click" : true,
13540     /**
13541      * @event mouseup
13542      * Fires when the mouse key is released.
13543      * @param {Roo.util.ClickRepeater} this
13544      */
13545         "mouseup" : true
13546     });
13547
13548     this.el.on("mousedown", this.handleMouseDown, this);
13549     if(this.preventDefault || this.stopDefault){
13550         this.el.on("click", function(e){
13551             if(this.preventDefault){
13552                 e.preventDefault();
13553             }
13554             if(this.stopDefault){
13555                 e.stopEvent();
13556             }
13557         }, this);
13558     }
13559
13560     // allow inline handler
13561     if(this.handler){
13562         this.on("click", this.handler,  this.scope || this);
13563     }
13564
13565     Roo.util.ClickRepeater.superclass.constructor.call(this);
13566 };
13567
13568 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13569     interval : 20,
13570     delay: 250,
13571     preventDefault : true,
13572     stopDefault : false,
13573     timer : 0,
13574
13575     // private
13576     handleMouseDown : function(){
13577         clearTimeout(this.timer);
13578         this.el.blur();
13579         if(this.pressClass){
13580             this.el.addClass(this.pressClass);
13581         }
13582         this.mousedownTime = new Date();
13583
13584         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13585         this.el.on("mouseout", this.handleMouseOut, this);
13586
13587         this.fireEvent("mousedown", this);
13588         this.fireEvent("click", this);
13589         
13590         this.timer = this.click.defer(this.delay || this.interval, this);
13591     },
13592
13593     // private
13594     click : function(){
13595         this.fireEvent("click", this);
13596         this.timer = this.click.defer(this.getInterval(), this);
13597     },
13598
13599     // private
13600     getInterval: function(){
13601         if(!this.accelerate){
13602             return this.interval;
13603         }
13604         var pressTime = this.mousedownTime.getElapsed();
13605         if(pressTime < 500){
13606             return 400;
13607         }else if(pressTime < 1700){
13608             return 320;
13609         }else if(pressTime < 2600){
13610             return 250;
13611         }else if(pressTime < 3500){
13612             return 180;
13613         }else if(pressTime < 4400){
13614             return 140;
13615         }else if(pressTime < 5300){
13616             return 80;
13617         }else if(pressTime < 6200){
13618             return 50;
13619         }else{
13620             return 10;
13621         }
13622     },
13623
13624     // private
13625     handleMouseOut : function(){
13626         clearTimeout(this.timer);
13627         if(this.pressClass){
13628             this.el.removeClass(this.pressClass);
13629         }
13630         this.el.on("mouseover", this.handleMouseReturn, this);
13631     },
13632
13633     // private
13634     handleMouseReturn : function(){
13635         this.el.un("mouseover", this.handleMouseReturn);
13636         if(this.pressClass){
13637             this.el.addClass(this.pressClass);
13638         }
13639         this.click();
13640     },
13641
13642     // private
13643     handleMouseUp : function(){
13644         clearTimeout(this.timer);
13645         this.el.un("mouseover", this.handleMouseReturn);
13646         this.el.un("mouseout", this.handleMouseOut);
13647         Roo.get(document).un("mouseup", this.handleMouseUp);
13648         this.el.removeClass(this.pressClass);
13649         this.fireEvent("mouseup", this);
13650     }
13651 });/*
13652  * Based on:
13653  * Ext JS Library 1.1.1
13654  * Copyright(c) 2006-2007, Ext JS, LLC.
13655  *
13656  * Originally Released Under LGPL - original licence link has changed is not relivant.
13657  *
13658  * Fork - LGPL
13659  * <script type="text/javascript">
13660  */
13661
13662  
13663 /**
13664  * @class Roo.KeyNav
13665  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13666  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13667  * way to implement custom navigation schemes for any UI component.</p>
13668  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13669  * pageUp, pageDown, del, home, end.  Usage:</p>
13670  <pre><code>
13671 var nav = new Roo.KeyNav("my-element", {
13672     "left" : function(e){
13673         this.moveLeft(e.ctrlKey);
13674     },
13675     "right" : function(e){
13676         this.moveRight(e.ctrlKey);
13677     },
13678     "enter" : function(e){
13679         this.save();
13680     },
13681     scope : this
13682 });
13683 </code></pre>
13684  * @constructor
13685  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13686  * @param {Object} config The config
13687  */
13688 Roo.KeyNav = function(el, config){
13689     this.el = Roo.get(el);
13690     Roo.apply(this, config);
13691     if(!this.disabled){
13692         this.disabled = true;
13693         this.enable();
13694     }
13695 };
13696
13697 Roo.KeyNav.prototype = {
13698     /**
13699      * @cfg {Boolean} disabled
13700      * True to disable this KeyNav instance (defaults to false)
13701      */
13702     disabled : false,
13703     /**
13704      * @cfg {String} defaultEventAction
13705      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13706      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13707      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13708      */
13709     defaultEventAction: "stopEvent",
13710     /**
13711      * @cfg {Boolean} forceKeyDown
13712      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13713      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13714      * handle keydown instead of keypress.
13715      */
13716     forceKeyDown : false,
13717
13718     // private
13719     prepareEvent : function(e){
13720         var k = e.getKey();
13721         var h = this.keyToHandler[k];
13722         //if(h && this[h]){
13723         //    e.stopPropagation();
13724         //}
13725         if(Roo.isSafari && h && k >= 37 && k <= 40){
13726             e.stopEvent();
13727         }
13728     },
13729
13730     // private
13731     relay : function(e){
13732         var k = e.getKey();
13733         var h = this.keyToHandler[k];
13734         if(h && this[h]){
13735             if(this.doRelay(e, this[h], h) !== true){
13736                 e[this.defaultEventAction]();
13737             }
13738         }
13739     },
13740
13741     // private
13742     doRelay : function(e, h, hname){
13743         return h.call(this.scope || this, e);
13744     },
13745
13746     // possible handlers
13747     enter : false,
13748     left : false,
13749     right : false,
13750     up : false,
13751     down : false,
13752     tab : false,
13753     esc : false,
13754     pageUp : false,
13755     pageDown : false,
13756     del : false,
13757     home : false,
13758     end : false,
13759
13760     // quick lookup hash
13761     keyToHandler : {
13762         37 : "left",
13763         39 : "right",
13764         38 : "up",
13765         40 : "down",
13766         33 : "pageUp",
13767         34 : "pageDown",
13768         46 : "del",
13769         36 : "home",
13770         35 : "end",
13771         13 : "enter",
13772         27 : "esc",
13773         9  : "tab"
13774     },
13775
13776         /**
13777          * Enable this KeyNav
13778          */
13779         enable: function(){
13780                 if(this.disabled){
13781             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13782             // the EventObject will normalize Safari automatically
13783             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13784                 this.el.on("keydown", this.relay,  this);
13785             }else{
13786                 this.el.on("keydown", this.prepareEvent,  this);
13787                 this.el.on("keypress", this.relay,  this);
13788             }
13789                     this.disabled = false;
13790                 }
13791         },
13792
13793         /**
13794          * Disable this KeyNav
13795          */
13796         disable: function(){
13797                 if(!this.disabled){
13798                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13799                 this.el.un("keydown", this.relay);
13800             }else{
13801                 this.el.un("keydown", this.prepareEvent);
13802                 this.el.un("keypress", this.relay);
13803             }
13804                     this.disabled = true;
13805                 }
13806         }
13807 };/*
13808  * Based on:
13809  * Ext JS Library 1.1.1
13810  * Copyright(c) 2006-2007, Ext JS, LLC.
13811  *
13812  * Originally Released Under LGPL - original licence link has changed is not relivant.
13813  *
13814  * Fork - LGPL
13815  * <script type="text/javascript">
13816  */
13817
13818  
13819 /**
13820  * @class Roo.KeyMap
13821  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13822  * The constructor accepts the same config object as defined by {@link #addBinding}.
13823  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13824  * combination it will call the function with this signature (if the match is a multi-key
13825  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13826  * A KeyMap can also handle a string representation of keys.<br />
13827  * Usage:
13828  <pre><code>
13829 // map one key by key code
13830 var map = new Roo.KeyMap("my-element", {
13831     key: 13, // or Roo.EventObject.ENTER
13832     fn: myHandler,
13833     scope: myObject
13834 });
13835
13836 // map multiple keys to one action by string
13837 var map = new Roo.KeyMap("my-element", {
13838     key: "a\r\n\t",
13839     fn: myHandler,
13840     scope: myObject
13841 });
13842
13843 // map multiple keys to multiple actions by strings and array of codes
13844 var map = new Roo.KeyMap("my-element", [
13845     {
13846         key: [10,13],
13847         fn: function(){ alert("Return was pressed"); }
13848     }, {
13849         key: "abc",
13850         fn: function(){ alert('a, b or c was pressed'); }
13851     }, {
13852         key: "\t",
13853         ctrl:true,
13854         shift:true,
13855         fn: function(){ alert('Control + shift + tab was pressed.'); }
13856     }
13857 ]);
13858 </code></pre>
13859  * <b>Note: A KeyMap starts enabled</b>
13860  * @constructor
13861  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13862  * @param {Object} config The config (see {@link #addBinding})
13863  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13864  */
13865 Roo.KeyMap = function(el, config, eventName){
13866     this.el  = Roo.get(el);
13867     this.eventName = eventName || "keydown";
13868     this.bindings = [];
13869     if(config){
13870         this.addBinding(config);
13871     }
13872     this.enable();
13873 };
13874
13875 Roo.KeyMap.prototype = {
13876     /**
13877      * True to stop the event from bubbling and prevent the default browser action if the
13878      * key was handled by the KeyMap (defaults to false)
13879      * @type Boolean
13880      */
13881     stopEvent : false,
13882
13883     /**
13884      * Add a new binding to this KeyMap. The following config object properties are supported:
13885      * <pre>
13886 Property    Type             Description
13887 ----------  ---------------  ----------------------------------------------------------------------
13888 key         String/Array     A single keycode or an array of keycodes to handle
13889 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13890 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13891 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13892 fn          Function         The function to call when KeyMap finds the expected key combination
13893 scope       Object           The scope of the callback function
13894 </pre>
13895      *
13896      * Usage:
13897      * <pre><code>
13898 // Create a KeyMap
13899 var map = new Roo.KeyMap(document, {
13900     key: Roo.EventObject.ENTER,
13901     fn: handleKey,
13902     scope: this
13903 });
13904
13905 //Add a new binding to the existing KeyMap later
13906 map.addBinding({
13907     key: 'abc',
13908     shift: true,
13909     fn: handleKey,
13910     scope: this
13911 });
13912 </code></pre>
13913      * @param {Object/Array} config A single KeyMap config or an array of configs
13914      */
13915         addBinding : function(config){
13916         if(config instanceof Array){
13917             for(var i = 0, len = config.length; i < len; i++){
13918                 this.addBinding(config[i]);
13919             }
13920             return;
13921         }
13922         var keyCode = config.key,
13923             shift = config.shift, 
13924             ctrl = config.ctrl, 
13925             alt = config.alt,
13926             fn = config.fn,
13927             scope = config.scope;
13928         if(typeof keyCode == "string"){
13929             var ks = [];
13930             var keyString = keyCode.toUpperCase();
13931             for(var j = 0, len = keyString.length; j < len; j++){
13932                 ks.push(keyString.charCodeAt(j));
13933             }
13934             keyCode = ks;
13935         }
13936         var keyArray = keyCode instanceof Array;
13937         var handler = function(e){
13938             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13939                 var k = e.getKey();
13940                 if(keyArray){
13941                     for(var i = 0, len = keyCode.length; i < len; i++){
13942                         if(keyCode[i] == k){
13943                           if(this.stopEvent){
13944                               e.stopEvent();
13945                           }
13946                           fn.call(scope || window, k, e);
13947                           return;
13948                         }
13949                     }
13950                 }else{
13951                     if(k == keyCode){
13952                         if(this.stopEvent){
13953                            e.stopEvent();
13954                         }
13955                         fn.call(scope || window, k, e);
13956                     }
13957                 }
13958             }
13959         };
13960         this.bindings.push(handler);  
13961         },
13962
13963     /**
13964      * Shorthand for adding a single key listener
13965      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13966      * following options:
13967      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13968      * @param {Function} fn The function to call
13969      * @param {Object} scope (optional) The scope of the function
13970      */
13971     on : function(key, fn, scope){
13972         var keyCode, shift, ctrl, alt;
13973         if(typeof key == "object" && !(key instanceof Array)){
13974             keyCode = key.key;
13975             shift = key.shift;
13976             ctrl = key.ctrl;
13977             alt = key.alt;
13978         }else{
13979             keyCode = key;
13980         }
13981         this.addBinding({
13982             key: keyCode,
13983             shift: shift,
13984             ctrl: ctrl,
13985             alt: alt,
13986             fn: fn,
13987             scope: scope
13988         })
13989     },
13990
13991     // private
13992     handleKeyDown : function(e){
13993             if(this.enabled){ //just in case
13994             var b = this.bindings;
13995             for(var i = 0, len = b.length; i < len; i++){
13996                 b[i].call(this, e);
13997             }
13998             }
13999         },
14000         
14001         /**
14002          * Returns true if this KeyMap is enabled
14003          * @return {Boolean} 
14004          */
14005         isEnabled : function(){
14006             return this.enabled;  
14007         },
14008         
14009         /**
14010          * Enables this KeyMap
14011          */
14012         enable: function(){
14013                 if(!this.enabled){
14014                     this.el.on(this.eventName, this.handleKeyDown, this);
14015                     this.enabled = true;
14016                 }
14017         },
14018
14019         /**
14020          * Disable this KeyMap
14021          */
14022         disable: function(){
14023                 if(this.enabled){
14024                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14025                     this.enabled = false;
14026                 }
14027         }
14028 };/*
14029  * Based on:
14030  * Ext JS Library 1.1.1
14031  * Copyright(c) 2006-2007, Ext JS, LLC.
14032  *
14033  * Originally Released Under LGPL - original licence link has changed is not relivant.
14034  *
14035  * Fork - LGPL
14036  * <script type="text/javascript">
14037  */
14038
14039  
14040 /**
14041  * @class Roo.util.TextMetrics
14042  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14043  * wide, in pixels, a given block of text will be.
14044  * @singleton
14045  */
14046 Roo.util.TextMetrics = function(){
14047     var shared;
14048     return {
14049         /**
14050          * Measures the size of the specified text
14051          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14052          * that can affect the size of the rendered text
14053          * @param {String} text The text to measure
14054          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14055          * in order to accurately measure the text height
14056          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14057          */
14058         measure : function(el, text, fixedWidth){
14059             if(!shared){
14060                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14061             }
14062             shared.bind(el);
14063             shared.setFixedWidth(fixedWidth || 'auto');
14064             return shared.getSize(text);
14065         },
14066
14067         /**
14068          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14069          * the overhead of multiple calls to initialize the style properties on each measurement.
14070          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14071          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14072          * in order to accurately measure the text height
14073          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14074          */
14075         createInstance : function(el, fixedWidth){
14076             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14077         }
14078     };
14079 }();
14080
14081  
14082
14083 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14084     var ml = new Roo.Element(document.createElement('div'));
14085     document.body.appendChild(ml.dom);
14086     ml.position('absolute');
14087     ml.setLeftTop(-1000, -1000);
14088     ml.hide();
14089
14090     if(fixedWidth){
14091         ml.setWidth(fixedWidth);
14092     }
14093      
14094     var instance = {
14095         /**
14096          * Returns the size of the specified text based on the internal element's style and width properties
14097          * @memberOf Roo.util.TextMetrics.Instance#
14098          * @param {String} text The text to measure
14099          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14100          */
14101         getSize : function(text){
14102             ml.update(text);
14103             var s = ml.getSize();
14104             ml.update('');
14105             return s;
14106         },
14107
14108         /**
14109          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14110          * that can affect the size of the rendered text
14111          * @memberOf Roo.util.TextMetrics.Instance#
14112          * @param {String/HTMLElement} el The element, dom node or id
14113          */
14114         bind : function(el){
14115             ml.setStyle(
14116                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14117             );
14118         },
14119
14120         /**
14121          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14122          * to set a fixed width in order to accurately measure the text height.
14123          * @memberOf Roo.util.TextMetrics.Instance#
14124          * @param {Number} width The width to set on the element
14125          */
14126         setFixedWidth : function(width){
14127             ml.setWidth(width);
14128         },
14129
14130         /**
14131          * Returns the measured width of the specified text
14132          * @memberOf Roo.util.TextMetrics.Instance#
14133          * @param {String} text The text to measure
14134          * @return {Number} width The width in pixels
14135          */
14136         getWidth : function(text){
14137             ml.dom.style.width = 'auto';
14138             return this.getSize(text).width;
14139         },
14140
14141         /**
14142          * Returns the measured height of the specified text.  For multiline text, be sure to call
14143          * {@link #setFixedWidth} if necessary.
14144          * @memberOf Roo.util.TextMetrics.Instance#
14145          * @param {String} text The text to measure
14146          * @return {Number} height The height in pixels
14147          */
14148         getHeight : function(text){
14149             return this.getSize(text).height;
14150         }
14151     };
14152
14153     instance.bind(bindTo);
14154
14155     return instance;
14156 };
14157
14158 // backwards compat
14159 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169
14170 /**
14171  * @class Roo.state.Provider
14172  * Abstract base class for state provider implementations. This class provides methods
14173  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14174  * Provider interface.
14175  */
14176 Roo.state.Provider = function(){
14177     /**
14178      * @event statechange
14179      * Fires when a state change occurs.
14180      * @param {Provider} this This state provider
14181      * @param {String} key The state key which was changed
14182      * @param {String} value The encoded value for the state
14183      */
14184     this.addEvents({
14185         "statechange": true
14186     });
14187     this.state = {};
14188     Roo.state.Provider.superclass.constructor.call(this);
14189 };
14190 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14191     /**
14192      * Returns the current value for a key
14193      * @param {String} name The key name
14194      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14195      * @return {Mixed} The state data
14196      */
14197     get : function(name, defaultValue){
14198         return typeof this.state[name] == "undefined" ?
14199             defaultValue : this.state[name];
14200     },
14201     
14202     /**
14203      * Clears a value from the state
14204      * @param {String} name The key name
14205      */
14206     clear : function(name){
14207         delete this.state[name];
14208         this.fireEvent("statechange", this, name, null);
14209     },
14210     
14211     /**
14212      * Sets the value for a key
14213      * @param {String} name The key name
14214      * @param {Mixed} value The value to set
14215      */
14216     set : function(name, value){
14217         this.state[name] = value;
14218         this.fireEvent("statechange", this, name, value);
14219     },
14220     
14221     /**
14222      * Decodes a string previously encoded with {@link #encodeValue}.
14223      * @param {String} value The value to decode
14224      * @return {Mixed} The decoded value
14225      */
14226     decodeValue : function(cookie){
14227         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14228         var matches = re.exec(unescape(cookie));
14229         if(!matches || !matches[1]) return; // non state cookie
14230         var type = matches[1];
14231         var v = matches[2];
14232         switch(type){
14233             case "n":
14234                 return parseFloat(v);
14235             case "d":
14236                 return new Date(Date.parse(v));
14237             case "b":
14238                 return (v == "1");
14239             case "a":
14240                 var all = [];
14241                 var values = v.split("^");
14242                 for(var i = 0, len = values.length; i < len; i++){
14243                     all.push(this.decodeValue(values[i]));
14244                 }
14245                 return all;
14246            case "o":
14247                 var all = {};
14248                 var values = v.split("^");
14249                 for(var i = 0, len = values.length; i < len; i++){
14250                     var kv = values[i].split("=");
14251                     all[kv[0]] = this.decodeValue(kv[1]);
14252                 }
14253                 return all;
14254            default:
14255                 return v;
14256         }
14257     },
14258     
14259     /**
14260      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14261      * @param {Mixed} value The value to encode
14262      * @return {String} The encoded value
14263      */
14264     encodeValue : function(v){
14265         var enc;
14266         if(typeof v == "number"){
14267             enc = "n:" + v;
14268         }else if(typeof v == "boolean"){
14269             enc = "b:" + (v ? "1" : "0");
14270         }else if(v instanceof Date){
14271             enc = "d:" + v.toGMTString();
14272         }else if(v instanceof Array){
14273             var flat = "";
14274             for(var i = 0, len = v.length; i < len; i++){
14275                 flat += this.encodeValue(v[i]);
14276                 if(i != len-1) flat += "^";
14277             }
14278             enc = "a:" + flat;
14279         }else if(typeof v == "object"){
14280             var flat = "";
14281             for(var key in v){
14282                 if(typeof v[key] != "function"){
14283                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14284                 }
14285             }
14286             enc = "o:" + flat.substring(0, flat.length-1);
14287         }else{
14288             enc = "s:" + v;
14289         }
14290         return escape(enc);        
14291     }
14292 });
14293
14294 /*
14295  * Based on:
14296  * Ext JS Library 1.1.1
14297  * Copyright(c) 2006-2007, Ext JS, LLC.
14298  *
14299  * Originally Released Under LGPL - original licence link has changed is not relivant.
14300  *
14301  * Fork - LGPL
14302  * <script type="text/javascript">
14303  */
14304 /**
14305  * @class Roo.state.Manager
14306  * This is the global state manager. By default all components that are "state aware" check this class
14307  * for state information if you don't pass them a custom state provider. In order for this class
14308  * to be useful, it must be initialized with a provider when your application initializes.
14309  <pre><code>
14310 // in your initialization function
14311 init : function(){
14312    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14313    ...
14314    // supposed you have a {@link Roo.BorderLayout}
14315    var layout = new Roo.BorderLayout(...);
14316    layout.restoreState();
14317    // or a {Roo.BasicDialog}
14318    var dialog = new Roo.BasicDialog(...);
14319    dialog.restoreState();
14320  </code></pre>
14321  * @singleton
14322  */
14323 Roo.state.Manager = function(){
14324     var provider = new Roo.state.Provider();
14325     
14326     return {
14327         /**
14328          * Configures the default state provider for your application
14329          * @param {Provider} stateProvider The state provider to set
14330          */
14331         setProvider : function(stateProvider){
14332             provider = stateProvider;
14333         },
14334         
14335         /**
14336          * Returns the current value for a key
14337          * @param {String} name The key name
14338          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14339          * @return {Mixed} The state data
14340          */
14341         get : function(key, defaultValue){
14342             return provider.get(key, defaultValue);
14343         },
14344         
14345         /**
14346          * Sets the value for a key
14347          * @param {String} name The key name
14348          * @param {Mixed} value The state data
14349          */
14350          set : function(key, value){
14351             provider.set(key, value);
14352         },
14353         
14354         /**
14355          * Clears a value from the state
14356          * @param {String} name The key name
14357          */
14358         clear : function(key){
14359             provider.clear(key);
14360         },
14361         
14362         /**
14363          * Gets the currently configured state provider
14364          * @return {Provider} The state provider
14365          */
14366         getProvider : function(){
14367             return provider;
14368         }
14369     };
14370 }();
14371 /*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381 /**
14382  * @class Roo.state.CookieProvider
14383  * @extends Roo.state.Provider
14384  * The default Provider implementation which saves state via cookies.
14385  * <br />Usage:
14386  <pre><code>
14387    var cp = new Roo.state.CookieProvider({
14388        path: "/cgi-bin/",
14389        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14390        domain: "roojs.com"
14391    })
14392    Roo.state.Manager.setProvider(cp);
14393  </code></pre>
14394  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14395  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14396  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14397  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14398  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14399  * domain the page is running on including the 'www' like 'www.roojs.com')
14400  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14401  * @constructor
14402  * Create a new CookieProvider
14403  * @param {Object} config The configuration object
14404  */
14405 Roo.state.CookieProvider = function(config){
14406     Roo.state.CookieProvider.superclass.constructor.call(this);
14407     this.path = "/";
14408     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14409     this.domain = null;
14410     this.secure = false;
14411     Roo.apply(this, config);
14412     this.state = this.readCookies();
14413 };
14414
14415 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14416     // private
14417     set : function(name, value){
14418         if(typeof value == "undefined" || value === null){
14419             this.clear(name);
14420             return;
14421         }
14422         this.setCookie(name, value);
14423         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14424     },
14425
14426     // private
14427     clear : function(name){
14428         this.clearCookie(name);
14429         Roo.state.CookieProvider.superclass.clear.call(this, name);
14430     },
14431
14432     // private
14433     readCookies : function(){
14434         var cookies = {};
14435         var c = document.cookie + ";";
14436         var re = /\s?(.*?)=(.*?);/g;
14437         var matches;
14438         while((matches = re.exec(c)) != null){
14439             var name = matches[1];
14440             var value = matches[2];
14441             if(name && name.substring(0,3) == "ys-"){
14442                 cookies[name.substr(3)] = this.decodeValue(value);
14443             }
14444         }
14445         return cookies;
14446     },
14447
14448     // private
14449     setCookie : function(name, value){
14450         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14451            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14452            ((this.path == null) ? "" : ("; path=" + this.path)) +
14453            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14454            ((this.secure == true) ? "; secure" : "");
14455     },
14456
14457     // private
14458     clearCookie : function(name){
14459         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14460            ((this.path == null) ? "" : ("; path=" + this.path)) +
14461            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14462            ((this.secure == true) ? "; secure" : "");
14463     }
14464 });/*
14465  * Based on:
14466  * Ext JS Library 1.1.1
14467  * Copyright(c) 2006-2007, Ext JS, LLC.
14468  *
14469  * Originally Released Under LGPL - original licence link has changed is not relivant.
14470  *
14471  * Fork - LGPL
14472  * <script type="text/javascript">
14473  */
14474
14475
14476
14477 /*
14478  * These classes are derivatives of the similarly named classes in the YUI Library.
14479  * The original license:
14480  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14481  * Code licensed under the BSD License:
14482  * http://developer.yahoo.net/yui/license.txt
14483  */
14484
14485 (function() {
14486
14487 var Event=Roo.EventManager;
14488 var Dom=Roo.lib.Dom;
14489
14490 /**
14491  * @class Roo.dd.DragDrop
14492  * @extends Roo.util.Observable
14493  * Defines the interface and base operation of items that that can be
14494  * dragged or can be drop targets.  It was designed to be extended, overriding
14495  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14496  * Up to three html elements can be associated with a DragDrop instance:
14497  * <ul>
14498  * <li>linked element: the element that is passed into the constructor.
14499  * This is the element which defines the boundaries for interaction with
14500  * other DragDrop objects.</li>
14501  * <li>handle element(s): The drag operation only occurs if the element that
14502  * was clicked matches a handle element.  By default this is the linked
14503  * element, but there are times that you will want only a portion of the
14504  * linked element to initiate the drag operation, and the setHandleElId()
14505  * method provides a way to define this.</li>
14506  * <li>drag element: this represents the element that would be moved along
14507  * with the cursor during a drag operation.  By default, this is the linked
14508  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14509  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14510  * </li>
14511  * </ul>
14512  * This class should not be instantiated until the onload event to ensure that
14513  * the associated elements are available.
14514  * The following would define a DragDrop obj that would interact with any
14515  * other DragDrop obj in the "group1" group:
14516  * <pre>
14517  *  dd = new Roo.dd.DragDrop("div1", "group1");
14518  * </pre>
14519  * Since none of the event handlers have been implemented, nothing would
14520  * actually happen if you were to run the code above.  Normally you would
14521  * override this class or one of the default implementations, but you can
14522  * also override the methods you want on an instance of the class...
14523  * <pre>
14524  *  dd.onDragDrop = function(e, id) {
14525  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14526  *  }
14527  * </pre>
14528  * @constructor
14529  * @param {String} id of the element that is linked to this instance
14530  * @param {String} sGroup the group of related DragDrop objects
14531  * @param {object} config an object containing configurable attributes
14532  *                Valid properties for DragDrop:
14533  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14534  */
14535 Roo.dd.DragDrop = function(id, sGroup, config) {
14536     if (id) {
14537         this.init(id, sGroup, config);
14538     }
14539     
14540 };
14541
14542 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14543
14544     /**
14545      * The id of the element associated with this object.  This is what we
14546      * refer to as the "linked element" because the size and position of
14547      * this element is used to determine when the drag and drop objects have
14548      * interacted.
14549      * @property id
14550      * @type String
14551      */
14552     id: null,
14553
14554     /**
14555      * Configuration attributes passed into the constructor
14556      * @property config
14557      * @type object
14558      */
14559     config: null,
14560
14561     /**
14562      * The id of the element that will be dragged.  By default this is same
14563      * as the linked element , but could be changed to another element. Ex:
14564      * Roo.dd.DDProxy
14565      * @property dragElId
14566      * @type String
14567      * @private
14568      */
14569     dragElId: null,
14570
14571     /**
14572      * the id of the element that initiates the drag operation.  By default
14573      * this is the linked element, but could be changed to be a child of this
14574      * element.  This lets us do things like only starting the drag when the
14575      * header element within the linked html element is clicked.
14576      * @property handleElId
14577      * @type String
14578      * @private
14579      */
14580     handleElId: null,
14581
14582     /**
14583      * An associative array of HTML tags that will be ignored if clicked.
14584      * @property invalidHandleTypes
14585      * @type {string: string}
14586      */
14587     invalidHandleTypes: null,
14588
14589     /**
14590      * An associative array of ids for elements that will be ignored if clicked
14591      * @property invalidHandleIds
14592      * @type {string: string}
14593      */
14594     invalidHandleIds: null,
14595
14596     /**
14597      * An indexted array of css class names for elements that will be ignored
14598      * if clicked.
14599      * @property invalidHandleClasses
14600      * @type string[]
14601      */
14602     invalidHandleClasses: null,
14603
14604     /**
14605      * The linked element's absolute X position at the time the drag was
14606      * started
14607      * @property startPageX
14608      * @type int
14609      * @private
14610      */
14611     startPageX: 0,
14612
14613     /**
14614      * The linked element's absolute X position at the time the drag was
14615      * started
14616      * @property startPageY
14617      * @type int
14618      * @private
14619      */
14620     startPageY: 0,
14621
14622     /**
14623      * The group defines a logical collection of DragDrop objects that are
14624      * related.  Instances only get events when interacting with other
14625      * DragDrop object in the same group.  This lets us define multiple
14626      * groups using a single DragDrop subclass if we want.
14627      * @property groups
14628      * @type {string: string}
14629      */
14630     groups: null,
14631
14632     /**
14633      * Individual drag/drop instances can be locked.  This will prevent
14634      * onmousedown start drag.
14635      * @property locked
14636      * @type boolean
14637      * @private
14638      */
14639     locked: false,
14640
14641     /**
14642      * Lock this instance
14643      * @method lock
14644      */
14645     lock: function() { this.locked = true; },
14646
14647     /**
14648      * Unlock this instace
14649      * @method unlock
14650      */
14651     unlock: function() { this.locked = false; },
14652
14653     /**
14654      * By default, all insances can be a drop target.  This can be disabled by
14655      * setting isTarget to false.
14656      * @method isTarget
14657      * @type boolean
14658      */
14659     isTarget: true,
14660
14661     /**
14662      * The padding configured for this drag and drop object for calculating
14663      * the drop zone intersection with this object.
14664      * @method padding
14665      * @type int[]
14666      */
14667     padding: null,
14668
14669     /**
14670      * Cached reference to the linked element
14671      * @property _domRef
14672      * @private
14673      */
14674     _domRef: null,
14675
14676     /**
14677      * Internal typeof flag
14678      * @property __ygDragDrop
14679      * @private
14680      */
14681     __ygDragDrop: true,
14682
14683     /**
14684      * Set to true when horizontal contraints are applied
14685      * @property constrainX
14686      * @type boolean
14687      * @private
14688      */
14689     constrainX: false,
14690
14691     /**
14692      * Set to true when vertical contraints are applied
14693      * @property constrainY
14694      * @type boolean
14695      * @private
14696      */
14697     constrainY: false,
14698
14699     /**
14700      * The left constraint
14701      * @property minX
14702      * @type int
14703      * @private
14704      */
14705     minX: 0,
14706
14707     /**
14708      * The right constraint
14709      * @property maxX
14710      * @type int
14711      * @private
14712      */
14713     maxX: 0,
14714
14715     /**
14716      * The up constraint
14717      * @property minY
14718      * @type int
14719      * @type int
14720      * @private
14721      */
14722     minY: 0,
14723
14724     /**
14725      * The down constraint
14726      * @property maxY
14727      * @type int
14728      * @private
14729      */
14730     maxY: 0,
14731
14732     /**
14733      * Maintain offsets when we resetconstraints.  Set to true when you want
14734      * the position of the element relative to its parent to stay the same
14735      * when the page changes
14736      *
14737      * @property maintainOffset
14738      * @type boolean
14739      */
14740     maintainOffset: false,
14741
14742     /**
14743      * Array of pixel locations the element will snap to if we specified a
14744      * horizontal graduation/interval.  This array is generated automatically
14745      * when you define a tick interval.
14746      * @property xTicks
14747      * @type int[]
14748      */
14749     xTicks: null,
14750
14751     /**
14752      * Array of pixel locations the element will snap to if we specified a
14753      * vertical graduation/interval.  This array is generated automatically
14754      * when you define a tick interval.
14755      * @property yTicks
14756      * @type int[]
14757      */
14758     yTicks: null,
14759
14760     /**
14761      * By default the drag and drop instance will only respond to the primary
14762      * button click (left button for a right-handed mouse).  Set to true to
14763      * allow drag and drop to start with any mouse click that is propogated
14764      * by the browser
14765      * @property primaryButtonOnly
14766      * @type boolean
14767      */
14768     primaryButtonOnly: true,
14769
14770     /**
14771      * The availabe property is false until the linked dom element is accessible.
14772      * @property available
14773      * @type boolean
14774      */
14775     available: false,
14776
14777     /**
14778      * By default, drags can only be initiated if the mousedown occurs in the
14779      * region the linked element is.  This is done in part to work around a
14780      * bug in some browsers that mis-report the mousedown if the previous
14781      * mouseup happened outside of the window.  This property is set to true
14782      * if outer handles are defined.
14783      *
14784      * @property hasOuterHandles
14785      * @type boolean
14786      * @default false
14787      */
14788     hasOuterHandles: false,
14789
14790     /**
14791      * Code that executes immediately before the startDrag event
14792      * @method b4StartDrag
14793      * @private
14794      */
14795     b4StartDrag: function(x, y) { },
14796
14797     /**
14798      * Abstract method called after a drag/drop object is clicked
14799      * and the drag or mousedown time thresholds have beeen met.
14800      * @method startDrag
14801      * @param {int} X click location
14802      * @param {int} Y click location
14803      */
14804     startDrag: function(x, y) { /* override this */ },
14805
14806     /**
14807      * Code that executes immediately before the onDrag event
14808      * @method b4Drag
14809      * @private
14810      */
14811     b4Drag: function(e) { },
14812
14813     /**
14814      * Abstract method called during the onMouseMove event while dragging an
14815      * object.
14816      * @method onDrag
14817      * @param {Event} e the mousemove event
14818      */
14819     onDrag: function(e) { /* override this */ },
14820
14821     /**
14822      * Abstract method called when this element fist begins hovering over
14823      * another DragDrop obj
14824      * @method onDragEnter
14825      * @param {Event} e the mousemove event
14826      * @param {String|DragDrop[]} id In POINT mode, the element
14827      * id this is hovering over.  In INTERSECT mode, an array of one or more
14828      * dragdrop items being hovered over.
14829      */
14830     onDragEnter: function(e, id) { /* override this */ },
14831
14832     /**
14833      * Code that executes immediately before the onDragOver event
14834      * @method b4DragOver
14835      * @private
14836      */
14837     b4DragOver: function(e) { },
14838
14839     /**
14840      * Abstract method called when this element is hovering over another
14841      * DragDrop obj
14842      * @method onDragOver
14843      * @param {Event} e the mousemove event
14844      * @param {String|DragDrop[]} id In POINT mode, the element
14845      * id this is hovering over.  In INTERSECT mode, an array of dd items
14846      * being hovered over.
14847      */
14848     onDragOver: function(e, id) { /* override this */ },
14849
14850     /**
14851      * Code that executes immediately before the onDragOut event
14852      * @method b4DragOut
14853      * @private
14854      */
14855     b4DragOut: function(e) { },
14856
14857     /**
14858      * Abstract method called when we are no longer hovering over an element
14859      * @method onDragOut
14860      * @param {Event} e the mousemove event
14861      * @param {String|DragDrop[]} id In POINT mode, the element
14862      * id this was hovering over.  In INTERSECT mode, an array of dd items
14863      * that the mouse is no longer over.
14864      */
14865     onDragOut: function(e, id) { /* override this */ },
14866
14867     /**
14868      * Code that executes immediately before the onDragDrop event
14869      * @method b4DragDrop
14870      * @private
14871      */
14872     b4DragDrop: function(e) { },
14873
14874     /**
14875      * Abstract method called when this item is dropped on another DragDrop
14876      * obj
14877      * @method onDragDrop
14878      * @param {Event} e the mouseup event
14879      * @param {String|DragDrop[]} id In POINT mode, the element
14880      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14881      * was dropped on.
14882      */
14883     onDragDrop: function(e, id) { /* override this */ },
14884
14885     /**
14886      * Abstract method called when this item is dropped on an area with no
14887      * drop target
14888      * @method onInvalidDrop
14889      * @param {Event} e the mouseup event
14890      */
14891     onInvalidDrop: function(e) { /* override this */ },
14892
14893     /**
14894      * Code that executes immediately before the endDrag event
14895      * @method b4EndDrag
14896      * @private
14897      */
14898     b4EndDrag: function(e) { },
14899
14900     /**
14901      * Fired when we are done dragging the object
14902      * @method endDrag
14903      * @param {Event} e the mouseup event
14904      */
14905     endDrag: function(e) { /* override this */ },
14906
14907     /**
14908      * Code executed immediately before the onMouseDown event
14909      * @method b4MouseDown
14910      * @param {Event} e the mousedown event
14911      * @private
14912      */
14913     b4MouseDown: function(e) {  },
14914
14915     /**
14916      * Event handler that fires when a drag/drop obj gets a mousedown
14917      * @method onMouseDown
14918      * @param {Event} e the mousedown event
14919      */
14920     onMouseDown: function(e) { /* override this */ },
14921
14922     /**
14923      * Event handler that fires when a drag/drop obj gets a mouseup
14924      * @method onMouseUp
14925      * @param {Event} e the mouseup event
14926      */
14927     onMouseUp: function(e) { /* override this */ },
14928
14929     /**
14930      * Override the onAvailable method to do what is needed after the initial
14931      * position was determined.
14932      * @method onAvailable
14933      */
14934     onAvailable: function () {
14935     },
14936
14937     /*
14938      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14939      * @type Object
14940      */
14941     defaultPadding : {left:0, right:0, top:0, bottom:0},
14942
14943     /*
14944      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14945  *
14946  * Usage:
14947  <pre><code>
14948  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14949                 { dragElId: "existingProxyDiv" });
14950  dd.startDrag = function(){
14951      this.constrainTo("parent-id");
14952  };
14953  </code></pre>
14954  * Or you can initalize it using the {@link Roo.Element} object:
14955  <pre><code>
14956  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14957      startDrag : function(){
14958          this.constrainTo("parent-id");
14959      }
14960  });
14961  </code></pre>
14962      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14963      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14964      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14965      * an object containing the sides to pad. For example: {right:10, bottom:10}
14966      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14967      */
14968     constrainTo : function(constrainTo, pad, inContent){
14969         if(typeof pad == "number"){
14970             pad = {left: pad, right:pad, top:pad, bottom:pad};
14971         }
14972         pad = pad || this.defaultPadding;
14973         var b = Roo.get(this.getEl()).getBox();
14974         var ce = Roo.get(constrainTo);
14975         var s = ce.getScroll();
14976         var c, cd = ce.dom;
14977         if(cd == document.body){
14978             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14979         }else{
14980             xy = ce.getXY();
14981             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14982         }
14983
14984
14985         var topSpace = b.y - c.y;
14986         var leftSpace = b.x - c.x;
14987
14988         this.resetConstraints();
14989         this.setXConstraint(leftSpace - (pad.left||0), // left
14990                 c.width - leftSpace - b.width - (pad.right||0) //right
14991         );
14992         this.setYConstraint(topSpace - (pad.top||0), //top
14993                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14994         );
14995     },
14996
14997     /**
14998      * Returns a reference to the linked element
14999      * @method getEl
15000      * @return {HTMLElement} the html element
15001      */
15002     getEl: function() {
15003         if (!this._domRef) {
15004             this._domRef = Roo.getDom(this.id);
15005         }
15006
15007         return this._domRef;
15008     },
15009
15010     /**
15011      * Returns a reference to the actual element to drag.  By default this is
15012      * the same as the html element, but it can be assigned to another
15013      * element. An example of this can be found in Roo.dd.DDProxy
15014      * @method getDragEl
15015      * @return {HTMLElement} the html element
15016      */
15017     getDragEl: function() {
15018         return Roo.getDom(this.dragElId);
15019     },
15020
15021     /**
15022      * Sets up the DragDrop object.  Must be called in the constructor of any
15023      * Roo.dd.DragDrop subclass
15024      * @method init
15025      * @param id the id of the linked element
15026      * @param {String} sGroup the group of related items
15027      * @param {object} config configuration attributes
15028      */
15029     init: function(id, sGroup, config) {
15030         this.initTarget(id, sGroup, config);
15031         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15032         // Event.on(this.id, "selectstart", Event.preventDefault);
15033     },
15034
15035     /**
15036      * Initializes Targeting functionality only... the object does not
15037      * get a mousedown handler.
15038      * @method initTarget
15039      * @param id the id of the linked element
15040      * @param {String} sGroup the group of related items
15041      * @param {object} config configuration attributes
15042      */
15043     initTarget: function(id, sGroup, config) {
15044
15045         // configuration attributes
15046         this.config = config || {};
15047
15048         // create a local reference to the drag and drop manager
15049         this.DDM = Roo.dd.DDM;
15050         // initialize the groups array
15051         this.groups = {};
15052
15053         // assume that we have an element reference instead of an id if the
15054         // parameter is not a string
15055         if (typeof id !== "string") {
15056             id = Roo.id(id);
15057         }
15058
15059         // set the id
15060         this.id = id;
15061
15062         // add to an interaction group
15063         this.addToGroup((sGroup) ? sGroup : "default");
15064
15065         // We don't want to register this as the handle with the manager
15066         // so we just set the id rather than calling the setter.
15067         this.handleElId = id;
15068
15069         // the linked element is the element that gets dragged by default
15070         this.setDragElId(id);
15071
15072         // by default, clicked anchors will not start drag operations.
15073         this.invalidHandleTypes = { A: "A" };
15074         this.invalidHandleIds = {};
15075         this.invalidHandleClasses = [];
15076
15077         this.applyConfig();
15078
15079         this.handleOnAvailable();
15080     },
15081
15082     /**
15083      * Applies the configuration parameters that were passed into the constructor.
15084      * This is supposed to happen at each level through the inheritance chain.  So
15085      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15086      * DragDrop in order to get all of the parameters that are available in
15087      * each object.
15088      * @method applyConfig
15089      */
15090     applyConfig: function() {
15091
15092         // configurable properties:
15093         //    padding, isTarget, maintainOffset, primaryButtonOnly
15094         this.padding           = this.config.padding || [0, 0, 0, 0];
15095         this.isTarget          = (this.config.isTarget !== false);
15096         this.maintainOffset    = (this.config.maintainOffset);
15097         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15098
15099     },
15100
15101     /**
15102      * Executed when the linked element is available
15103      * @method handleOnAvailable
15104      * @private
15105      */
15106     handleOnAvailable: function() {
15107         this.available = true;
15108         this.resetConstraints();
15109         this.onAvailable();
15110     },
15111
15112      /**
15113      * Configures the padding for the target zone in px.  Effectively expands
15114      * (or reduces) the virtual object size for targeting calculations.
15115      * Supports css-style shorthand; if only one parameter is passed, all sides
15116      * will have that padding, and if only two are passed, the top and bottom
15117      * will have the first param, the left and right the second.
15118      * @method setPadding
15119      * @param {int} iTop    Top pad
15120      * @param {int} iRight  Right pad
15121      * @param {int} iBot    Bot pad
15122      * @param {int} iLeft   Left pad
15123      */
15124     setPadding: function(iTop, iRight, iBot, iLeft) {
15125         // this.padding = [iLeft, iRight, iTop, iBot];
15126         if (!iRight && 0 !== iRight) {
15127             this.padding = [iTop, iTop, iTop, iTop];
15128         } else if (!iBot && 0 !== iBot) {
15129             this.padding = [iTop, iRight, iTop, iRight];
15130         } else {
15131             this.padding = [iTop, iRight, iBot, iLeft];
15132         }
15133     },
15134
15135     /**
15136      * Stores the initial placement of the linked element.
15137      * @method setInitialPosition
15138      * @param {int} diffX   the X offset, default 0
15139      * @param {int} diffY   the Y offset, default 0
15140      */
15141     setInitPosition: function(diffX, diffY) {
15142         var el = this.getEl();
15143
15144         if (!this.DDM.verifyEl(el)) {
15145             return;
15146         }
15147
15148         var dx = diffX || 0;
15149         var dy = diffY || 0;
15150
15151         var p = Dom.getXY( el );
15152
15153         this.initPageX = p[0] - dx;
15154         this.initPageY = p[1] - dy;
15155
15156         this.lastPageX = p[0];
15157         this.lastPageY = p[1];
15158
15159
15160         this.setStartPosition(p);
15161     },
15162
15163     /**
15164      * Sets the start position of the element.  This is set when the obj
15165      * is initialized, the reset when a drag is started.
15166      * @method setStartPosition
15167      * @param pos current position (from previous lookup)
15168      * @private
15169      */
15170     setStartPosition: function(pos) {
15171         var p = pos || Dom.getXY( this.getEl() );
15172         this.deltaSetXY = null;
15173
15174         this.startPageX = p[0];
15175         this.startPageY = p[1];
15176     },
15177
15178     /**
15179      * Add this instance to a group of related drag/drop objects.  All
15180      * instances belong to at least one group, and can belong to as many
15181      * groups as needed.
15182      * @method addToGroup
15183      * @param sGroup {string} the name of the group
15184      */
15185     addToGroup: function(sGroup) {
15186         this.groups[sGroup] = true;
15187         this.DDM.regDragDrop(this, sGroup);
15188     },
15189
15190     /**
15191      * Remove's this instance from the supplied interaction group
15192      * @method removeFromGroup
15193      * @param {string}  sGroup  The group to drop
15194      */
15195     removeFromGroup: function(sGroup) {
15196         if (this.groups[sGroup]) {
15197             delete this.groups[sGroup];
15198         }
15199
15200         this.DDM.removeDDFromGroup(this, sGroup);
15201     },
15202
15203     /**
15204      * Allows you to specify that an element other than the linked element
15205      * will be moved with the cursor during a drag
15206      * @method setDragElId
15207      * @param id {string} the id of the element that will be used to initiate the drag
15208      */
15209     setDragElId: function(id) {
15210         this.dragElId = id;
15211     },
15212
15213     /**
15214      * Allows you to specify a child of the linked element that should be
15215      * used to initiate the drag operation.  An example of this would be if
15216      * you have a content div with text and links.  Clicking anywhere in the
15217      * content area would normally start the drag operation.  Use this method
15218      * to specify that an element inside of the content div is the element
15219      * that starts the drag operation.
15220      * @method setHandleElId
15221      * @param id {string} the id of the element that will be used to
15222      * initiate the drag.
15223      */
15224     setHandleElId: function(id) {
15225         if (typeof id !== "string") {
15226             id = Roo.id(id);
15227         }
15228         this.handleElId = id;
15229         this.DDM.regHandle(this.id, id);
15230     },
15231
15232     /**
15233      * Allows you to set an element outside of the linked element as a drag
15234      * handle
15235      * @method setOuterHandleElId
15236      * @param id the id of the element that will be used to initiate the drag
15237      */
15238     setOuterHandleElId: function(id) {
15239         if (typeof id !== "string") {
15240             id = Roo.id(id);
15241         }
15242         Event.on(id, "mousedown",
15243                 this.handleMouseDown, this);
15244         this.setHandleElId(id);
15245
15246         this.hasOuterHandles = true;
15247     },
15248
15249     /**
15250      * Remove all drag and drop hooks for this element
15251      * @method unreg
15252      */
15253     unreg: function() {
15254         Event.un(this.id, "mousedown",
15255                 this.handleMouseDown);
15256         this._domRef = null;
15257         this.DDM._remove(this);
15258     },
15259
15260     destroy : function(){
15261         this.unreg();
15262     },
15263
15264     /**
15265      * Returns true if this instance is locked, or the drag drop mgr is locked
15266      * (meaning that all drag/drop is disabled on the page.)
15267      * @method isLocked
15268      * @return {boolean} true if this obj or all drag/drop is locked, else
15269      * false
15270      */
15271     isLocked: function() {
15272         return (this.DDM.isLocked() || this.locked);
15273     },
15274
15275     /**
15276      * Fired when this object is clicked
15277      * @method handleMouseDown
15278      * @param {Event} e
15279      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15280      * @private
15281      */
15282     handleMouseDown: function(e, oDD){
15283         if (this.primaryButtonOnly && e.button != 0) {
15284             return;
15285         }
15286
15287         if (this.isLocked()) {
15288             return;
15289         }
15290
15291         this.DDM.refreshCache(this.groups);
15292
15293         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15294         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15295         } else {
15296             if (this.clickValidator(e)) {
15297
15298                 // set the initial element position
15299                 this.setStartPosition();
15300
15301
15302                 this.b4MouseDown(e);
15303                 this.onMouseDown(e);
15304
15305                 this.DDM.handleMouseDown(e, this);
15306
15307                 this.DDM.stopEvent(e);
15308             } else {
15309
15310
15311             }
15312         }
15313     },
15314
15315     clickValidator: function(e) {
15316         var target = e.getTarget();
15317         return ( this.isValidHandleChild(target) &&
15318                     (this.id == this.handleElId ||
15319                         this.DDM.handleWasClicked(target, this.id)) );
15320     },
15321
15322     /**
15323      * Allows you to specify a tag name that should not start a drag operation
15324      * when clicked.  This is designed to facilitate embedding links within a
15325      * drag handle that do something other than start the drag.
15326      * @method addInvalidHandleType
15327      * @param {string} tagName the type of element to exclude
15328      */
15329     addInvalidHandleType: function(tagName) {
15330         var type = tagName.toUpperCase();
15331         this.invalidHandleTypes[type] = type;
15332     },
15333
15334     /**
15335      * Lets you to specify an element id for a child of a drag handle
15336      * that should not initiate a drag
15337      * @method addInvalidHandleId
15338      * @param {string} id the element id of the element you wish to ignore
15339      */
15340     addInvalidHandleId: function(id) {
15341         if (typeof id !== "string") {
15342             id = Roo.id(id);
15343         }
15344         this.invalidHandleIds[id] = id;
15345     },
15346
15347     /**
15348      * Lets you specify a css class of elements that will not initiate a drag
15349      * @method addInvalidHandleClass
15350      * @param {string} cssClass the class of the elements you wish to ignore
15351      */
15352     addInvalidHandleClass: function(cssClass) {
15353         this.invalidHandleClasses.push(cssClass);
15354     },
15355
15356     /**
15357      * Unsets an excluded tag name set by addInvalidHandleType
15358      * @method removeInvalidHandleType
15359      * @param {string} tagName the type of element to unexclude
15360      */
15361     removeInvalidHandleType: function(tagName) {
15362         var type = tagName.toUpperCase();
15363         // this.invalidHandleTypes[type] = null;
15364         delete this.invalidHandleTypes[type];
15365     },
15366
15367     /**
15368      * Unsets an invalid handle id
15369      * @method removeInvalidHandleId
15370      * @param {string} id the id of the element to re-enable
15371      */
15372     removeInvalidHandleId: function(id) {
15373         if (typeof id !== "string") {
15374             id = Roo.id(id);
15375         }
15376         delete this.invalidHandleIds[id];
15377     },
15378
15379     /**
15380      * Unsets an invalid css class
15381      * @method removeInvalidHandleClass
15382      * @param {string} cssClass the class of the element(s) you wish to
15383      * re-enable
15384      */
15385     removeInvalidHandleClass: function(cssClass) {
15386         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15387             if (this.invalidHandleClasses[i] == cssClass) {
15388                 delete this.invalidHandleClasses[i];
15389             }
15390         }
15391     },
15392
15393     /**
15394      * Checks the tag exclusion list to see if this click should be ignored
15395      * @method isValidHandleChild
15396      * @param {HTMLElement} node the HTMLElement to evaluate
15397      * @return {boolean} true if this is a valid tag type, false if not
15398      */
15399     isValidHandleChild: function(node) {
15400
15401         var valid = true;
15402         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15403         var nodeName;
15404         try {
15405             nodeName = node.nodeName.toUpperCase();
15406         } catch(e) {
15407             nodeName = node.nodeName;
15408         }
15409         valid = valid && !this.invalidHandleTypes[nodeName];
15410         valid = valid && !this.invalidHandleIds[node.id];
15411
15412         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15413             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15414         }
15415
15416
15417         return valid;
15418
15419     },
15420
15421     /**
15422      * Create the array of horizontal tick marks if an interval was specified
15423      * in setXConstraint().
15424      * @method setXTicks
15425      * @private
15426      */
15427     setXTicks: function(iStartX, iTickSize) {
15428         this.xTicks = [];
15429         this.xTickSize = iTickSize;
15430
15431         var tickMap = {};
15432
15433         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15434             if (!tickMap[i]) {
15435                 this.xTicks[this.xTicks.length] = i;
15436                 tickMap[i] = true;
15437             }
15438         }
15439
15440         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15441             if (!tickMap[i]) {
15442                 this.xTicks[this.xTicks.length] = i;
15443                 tickMap[i] = true;
15444             }
15445         }
15446
15447         this.xTicks.sort(this.DDM.numericSort) ;
15448     },
15449
15450     /**
15451      * Create the array of vertical tick marks if an interval was specified in
15452      * setYConstraint().
15453      * @method setYTicks
15454      * @private
15455      */
15456     setYTicks: function(iStartY, iTickSize) {
15457         this.yTicks = [];
15458         this.yTickSize = iTickSize;
15459
15460         var tickMap = {};
15461
15462         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15463             if (!tickMap[i]) {
15464                 this.yTicks[this.yTicks.length] = i;
15465                 tickMap[i] = true;
15466             }
15467         }
15468
15469         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15470             if (!tickMap[i]) {
15471                 this.yTicks[this.yTicks.length] = i;
15472                 tickMap[i] = true;
15473             }
15474         }
15475
15476         this.yTicks.sort(this.DDM.numericSort) ;
15477     },
15478
15479     /**
15480      * By default, the element can be dragged any place on the screen.  Use
15481      * this method to limit the horizontal travel of the element.  Pass in
15482      * 0,0 for the parameters if you want to lock the drag to the y axis.
15483      * @method setXConstraint
15484      * @param {int} iLeft the number of pixels the element can move to the left
15485      * @param {int} iRight the number of pixels the element can move to the
15486      * right
15487      * @param {int} iTickSize optional parameter for specifying that the
15488      * element
15489      * should move iTickSize pixels at a time.
15490      */
15491     setXConstraint: function(iLeft, iRight, iTickSize) {
15492         this.leftConstraint = iLeft;
15493         this.rightConstraint = iRight;
15494
15495         this.minX = this.initPageX - iLeft;
15496         this.maxX = this.initPageX + iRight;
15497         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15498
15499         this.constrainX = true;
15500     },
15501
15502     /**
15503      * Clears any constraints applied to this instance.  Also clears ticks
15504      * since they can't exist independent of a constraint at this time.
15505      * @method clearConstraints
15506      */
15507     clearConstraints: function() {
15508         this.constrainX = false;
15509         this.constrainY = false;
15510         this.clearTicks();
15511     },
15512
15513     /**
15514      * Clears any tick interval defined for this instance
15515      * @method clearTicks
15516      */
15517     clearTicks: function() {
15518         this.xTicks = null;
15519         this.yTicks = null;
15520         this.xTickSize = 0;
15521         this.yTickSize = 0;
15522     },
15523
15524     /**
15525      * By default, the element can be dragged any place on the screen.  Set
15526      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15527      * parameters if you want to lock the drag to the x axis.
15528      * @method setYConstraint
15529      * @param {int} iUp the number of pixels the element can move up
15530      * @param {int} iDown the number of pixels the element can move down
15531      * @param {int} iTickSize optional parameter for specifying that the
15532      * element should move iTickSize pixels at a time.
15533      */
15534     setYConstraint: function(iUp, iDown, iTickSize) {
15535         this.topConstraint = iUp;
15536         this.bottomConstraint = iDown;
15537
15538         this.minY = this.initPageY - iUp;
15539         this.maxY = this.initPageY + iDown;
15540         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15541
15542         this.constrainY = true;
15543
15544     },
15545
15546     /**
15547      * resetConstraints must be called if you manually reposition a dd element.
15548      * @method resetConstraints
15549      * @param {boolean} maintainOffset
15550      */
15551     resetConstraints: function() {
15552
15553
15554         // Maintain offsets if necessary
15555         if (this.initPageX || this.initPageX === 0) {
15556             // figure out how much this thing has moved
15557             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15558             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15559
15560             this.setInitPosition(dx, dy);
15561
15562         // This is the first time we have detected the element's position
15563         } else {
15564             this.setInitPosition();
15565         }
15566
15567         if (this.constrainX) {
15568             this.setXConstraint( this.leftConstraint,
15569                                  this.rightConstraint,
15570                                  this.xTickSize        );
15571         }
15572
15573         if (this.constrainY) {
15574             this.setYConstraint( this.topConstraint,
15575                                  this.bottomConstraint,
15576                                  this.yTickSize         );
15577         }
15578     },
15579
15580     /**
15581      * Normally the drag element is moved pixel by pixel, but we can specify
15582      * that it move a number of pixels at a time.  This method resolves the
15583      * location when we have it set up like this.
15584      * @method getTick
15585      * @param {int} val where we want to place the object
15586      * @param {int[]} tickArray sorted array of valid points
15587      * @return {int} the closest tick
15588      * @private
15589      */
15590     getTick: function(val, tickArray) {
15591
15592         if (!tickArray) {
15593             // If tick interval is not defined, it is effectively 1 pixel,
15594             // so we return the value passed to us.
15595             return val;
15596         } else if (tickArray[0] >= val) {
15597             // The value is lower than the first tick, so we return the first
15598             // tick.
15599             return tickArray[0];
15600         } else {
15601             for (var i=0, len=tickArray.length; i<len; ++i) {
15602                 var next = i + 1;
15603                 if (tickArray[next] && tickArray[next] >= val) {
15604                     var diff1 = val - tickArray[i];
15605                     var diff2 = tickArray[next] - val;
15606                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15607                 }
15608             }
15609
15610             // The value is larger than the last tick, so we return the last
15611             // tick.
15612             return tickArray[tickArray.length - 1];
15613         }
15614     },
15615
15616     /**
15617      * toString method
15618      * @method toString
15619      * @return {string} string representation of the dd obj
15620      */
15621     toString: function() {
15622         return ("DragDrop " + this.id);
15623     }
15624
15625 });
15626
15627 })();
15628 /*
15629  * Based on:
15630  * Ext JS Library 1.1.1
15631  * Copyright(c) 2006-2007, Ext JS, LLC.
15632  *
15633  * Originally Released Under LGPL - original licence link has changed is not relivant.
15634  *
15635  * Fork - LGPL
15636  * <script type="text/javascript">
15637  */
15638
15639
15640 /**
15641  * The drag and drop utility provides a framework for building drag and drop
15642  * applications.  In addition to enabling drag and drop for specific elements,
15643  * the drag and drop elements are tracked by the manager class, and the
15644  * interactions between the various elements are tracked during the drag and
15645  * the implementing code is notified about these important moments.
15646  */
15647
15648 // Only load the library once.  Rewriting the manager class would orphan
15649 // existing drag and drop instances.
15650 if (!Roo.dd.DragDropMgr) {
15651
15652 /**
15653  * @class Roo.dd.DragDropMgr
15654  * DragDropMgr is a singleton that tracks the element interaction for
15655  * all DragDrop items in the window.  Generally, you will not call
15656  * this class directly, but it does have helper methods that could
15657  * be useful in your DragDrop implementations.
15658  * @singleton
15659  */
15660 Roo.dd.DragDropMgr = function() {
15661
15662     var Event = Roo.EventManager;
15663
15664     return {
15665
15666         /**
15667          * Two dimensional Array of registered DragDrop objects.  The first
15668          * dimension is the DragDrop item group, the second the DragDrop
15669          * object.
15670          * @property ids
15671          * @type {string: string}
15672          * @private
15673          * @static
15674          */
15675         ids: {},
15676
15677         /**
15678          * Array of element ids defined as drag handles.  Used to determine
15679          * if the element that generated the mousedown event is actually the
15680          * handle and not the html element itself.
15681          * @property handleIds
15682          * @type {string: string}
15683          * @private
15684          * @static
15685          */
15686         handleIds: {},
15687
15688         /**
15689          * the DragDrop object that is currently being dragged
15690          * @property dragCurrent
15691          * @type DragDrop
15692          * @private
15693          * @static
15694          **/
15695         dragCurrent: null,
15696
15697         /**
15698          * the DragDrop object(s) that are being hovered over
15699          * @property dragOvers
15700          * @type Array
15701          * @private
15702          * @static
15703          */
15704         dragOvers: {},
15705
15706         /**
15707          * the X distance between the cursor and the object being dragged
15708          * @property deltaX
15709          * @type int
15710          * @private
15711          * @static
15712          */
15713         deltaX: 0,
15714
15715         /**
15716          * the Y distance between the cursor and the object being dragged
15717          * @property deltaY
15718          * @type int
15719          * @private
15720          * @static
15721          */
15722         deltaY: 0,
15723
15724         /**
15725          * Flag to determine if we should prevent the default behavior of the
15726          * events we define. By default this is true, but this can be set to
15727          * false if you need the default behavior (not recommended)
15728          * @property preventDefault
15729          * @type boolean
15730          * @static
15731          */
15732         preventDefault: true,
15733
15734         /**
15735          * Flag to determine if we should stop the propagation of the events
15736          * we generate. This is true by default but you may want to set it to
15737          * false if the html element contains other features that require the
15738          * mouse click.
15739          * @property stopPropagation
15740          * @type boolean
15741          * @static
15742          */
15743         stopPropagation: true,
15744
15745         /**
15746          * Internal flag that is set to true when drag and drop has been
15747          * intialized
15748          * @property initialized
15749          * @private
15750          * @static
15751          */
15752         initalized: false,
15753
15754         /**
15755          * All drag and drop can be disabled.
15756          * @property locked
15757          * @private
15758          * @static
15759          */
15760         locked: false,
15761
15762         /**
15763          * Called the first time an element is registered.
15764          * @method init
15765          * @private
15766          * @static
15767          */
15768         init: function() {
15769             this.initialized = true;
15770         },
15771
15772         /**
15773          * In point mode, drag and drop interaction is defined by the
15774          * location of the cursor during the drag/drop
15775          * @property POINT
15776          * @type int
15777          * @static
15778          */
15779         POINT: 0,
15780
15781         /**
15782          * In intersect mode, drag and drop interactio nis defined by the
15783          * overlap of two or more drag and drop objects.
15784          * @property INTERSECT
15785          * @type int
15786          * @static
15787          */
15788         INTERSECT: 1,
15789
15790         /**
15791          * The current drag and drop mode.  Default: POINT
15792          * @property mode
15793          * @type int
15794          * @static
15795          */
15796         mode: 0,
15797
15798         /**
15799          * Runs method on all drag and drop objects
15800          * @method _execOnAll
15801          * @private
15802          * @static
15803          */
15804         _execOnAll: function(sMethod, args) {
15805             for (var i in this.ids) {
15806                 for (var j in this.ids[i]) {
15807                     var oDD = this.ids[i][j];
15808                     if (! this.isTypeOfDD(oDD)) {
15809                         continue;
15810                     }
15811                     oDD[sMethod].apply(oDD, args);
15812                 }
15813             }
15814         },
15815
15816         /**
15817          * Drag and drop initialization.  Sets up the global event handlers
15818          * @method _onLoad
15819          * @private
15820          * @static
15821          */
15822         _onLoad: function() {
15823
15824             this.init();
15825
15826
15827             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15828             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15829             Event.on(window,   "unload",    this._onUnload, this, true);
15830             Event.on(window,   "resize",    this._onResize, this, true);
15831             // Event.on(window,   "mouseout",    this._test);
15832
15833         },
15834
15835         /**
15836          * Reset constraints on all drag and drop objs
15837          * @method _onResize
15838          * @private
15839          * @static
15840          */
15841         _onResize: function(e) {
15842             this._execOnAll("resetConstraints", []);
15843         },
15844
15845         /**
15846          * Lock all drag and drop functionality
15847          * @method lock
15848          * @static
15849          */
15850         lock: function() { this.locked = true; },
15851
15852         /**
15853          * Unlock all drag and drop functionality
15854          * @method unlock
15855          * @static
15856          */
15857         unlock: function() { this.locked = false; },
15858
15859         /**
15860          * Is drag and drop locked?
15861          * @method isLocked
15862          * @return {boolean} True if drag and drop is locked, false otherwise.
15863          * @static
15864          */
15865         isLocked: function() { return this.locked; },
15866
15867         /**
15868          * Location cache that is set for all drag drop objects when a drag is
15869          * initiated, cleared when the drag is finished.
15870          * @property locationCache
15871          * @private
15872          * @static
15873          */
15874         locationCache: {},
15875
15876         /**
15877          * Set useCache to false if you want to force object the lookup of each
15878          * drag and drop linked element constantly during a drag.
15879          * @property useCache
15880          * @type boolean
15881          * @static
15882          */
15883         useCache: true,
15884
15885         /**
15886          * The number of pixels that the mouse needs to move after the
15887          * mousedown before the drag is initiated.  Default=3;
15888          * @property clickPixelThresh
15889          * @type int
15890          * @static
15891          */
15892         clickPixelThresh: 3,
15893
15894         /**
15895          * The number of milliseconds after the mousedown event to initiate the
15896          * drag if we don't get a mouseup event. Default=1000
15897          * @property clickTimeThresh
15898          * @type int
15899          * @static
15900          */
15901         clickTimeThresh: 350,
15902
15903         /**
15904          * Flag that indicates that either the drag pixel threshold or the
15905          * mousdown time threshold has been met
15906          * @property dragThreshMet
15907          * @type boolean
15908          * @private
15909          * @static
15910          */
15911         dragThreshMet: false,
15912
15913         /**
15914          * Timeout used for the click time threshold
15915          * @property clickTimeout
15916          * @type Object
15917          * @private
15918          * @static
15919          */
15920         clickTimeout: null,
15921
15922         /**
15923          * The X position of the mousedown event stored for later use when a
15924          * drag threshold is met.
15925          * @property startX
15926          * @type int
15927          * @private
15928          * @static
15929          */
15930         startX: 0,
15931
15932         /**
15933          * The Y position of the mousedown event stored for later use when a
15934          * drag threshold is met.
15935          * @property startY
15936          * @type int
15937          * @private
15938          * @static
15939          */
15940         startY: 0,
15941
15942         /**
15943          * Each DragDrop instance must be registered with the DragDropMgr.
15944          * This is executed in DragDrop.init()
15945          * @method regDragDrop
15946          * @param {DragDrop} oDD the DragDrop object to register
15947          * @param {String} sGroup the name of the group this element belongs to
15948          * @static
15949          */
15950         regDragDrop: function(oDD, sGroup) {
15951             if (!this.initialized) { this.init(); }
15952
15953             if (!this.ids[sGroup]) {
15954                 this.ids[sGroup] = {};
15955             }
15956             this.ids[sGroup][oDD.id] = oDD;
15957         },
15958
15959         /**
15960          * Removes the supplied dd instance from the supplied group. Executed
15961          * by DragDrop.removeFromGroup, so don't call this function directly.
15962          * @method removeDDFromGroup
15963          * @private
15964          * @static
15965          */
15966         removeDDFromGroup: function(oDD, sGroup) {
15967             if (!this.ids[sGroup]) {
15968                 this.ids[sGroup] = {};
15969             }
15970
15971             var obj = this.ids[sGroup];
15972             if (obj && obj[oDD.id]) {
15973                 delete obj[oDD.id];
15974             }
15975         },
15976
15977         /**
15978          * Unregisters a drag and drop item.  This is executed in
15979          * DragDrop.unreg, use that method instead of calling this directly.
15980          * @method _remove
15981          * @private
15982          * @static
15983          */
15984         _remove: function(oDD) {
15985             for (var g in oDD.groups) {
15986                 if (g && this.ids[g][oDD.id]) {
15987                     delete this.ids[g][oDD.id];
15988                 }
15989             }
15990             delete this.handleIds[oDD.id];
15991         },
15992
15993         /**
15994          * Each DragDrop handle element must be registered.  This is done
15995          * automatically when executing DragDrop.setHandleElId()
15996          * @method regHandle
15997          * @param {String} sDDId the DragDrop id this element is a handle for
15998          * @param {String} sHandleId the id of the element that is the drag
15999          * handle
16000          * @static
16001          */
16002         regHandle: function(sDDId, sHandleId) {
16003             if (!this.handleIds[sDDId]) {
16004                 this.handleIds[sDDId] = {};
16005             }
16006             this.handleIds[sDDId][sHandleId] = sHandleId;
16007         },
16008
16009         /**
16010          * Utility function to determine if a given element has been
16011          * registered as a drag drop item.
16012          * @method isDragDrop
16013          * @param {String} id the element id to check
16014          * @return {boolean} true if this element is a DragDrop item,
16015          * false otherwise
16016          * @static
16017          */
16018         isDragDrop: function(id) {
16019             return ( this.getDDById(id) ) ? true : false;
16020         },
16021
16022         /**
16023          * Returns the drag and drop instances that are in all groups the
16024          * passed in instance belongs to.
16025          * @method getRelated
16026          * @param {DragDrop} p_oDD the obj to get related data for
16027          * @param {boolean} bTargetsOnly if true, only return targetable objs
16028          * @return {DragDrop[]} the related instances
16029          * @static
16030          */
16031         getRelated: function(p_oDD, bTargetsOnly) {
16032             var oDDs = [];
16033             for (var i in p_oDD.groups) {
16034                 for (j in this.ids[i]) {
16035                     var dd = this.ids[i][j];
16036                     if (! this.isTypeOfDD(dd)) {
16037                         continue;
16038                     }
16039                     if (!bTargetsOnly || dd.isTarget) {
16040                         oDDs[oDDs.length] = dd;
16041                     }
16042                 }
16043             }
16044
16045             return oDDs;
16046         },
16047
16048         /**
16049          * Returns true if the specified dd target is a legal target for
16050          * the specifice drag obj
16051          * @method isLegalTarget
16052          * @param {DragDrop} the drag obj
16053          * @param {DragDrop} the target
16054          * @return {boolean} true if the target is a legal target for the
16055          * dd obj
16056          * @static
16057          */
16058         isLegalTarget: function (oDD, oTargetDD) {
16059             var targets = this.getRelated(oDD, true);
16060             for (var i=0, len=targets.length;i<len;++i) {
16061                 if (targets[i].id == oTargetDD.id) {
16062                     return true;
16063                 }
16064             }
16065
16066             return false;
16067         },
16068
16069         /**
16070          * My goal is to be able to transparently determine if an object is
16071          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16072          * returns "object", oDD.constructor.toString() always returns
16073          * "DragDrop" and not the name of the subclass.  So for now it just
16074          * evaluates a well-known variable in DragDrop.
16075          * @method isTypeOfDD
16076          * @param {Object} the object to evaluate
16077          * @return {boolean} true if typeof oDD = DragDrop
16078          * @static
16079          */
16080         isTypeOfDD: function (oDD) {
16081             return (oDD && oDD.__ygDragDrop);
16082         },
16083
16084         /**
16085          * Utility function to determine if a given element has been
16086          * registered as a drag drop handle for the given Drag Drop object.
16087          * @method isHandle
16088          * @param {String} id the element id to check
16089          * @return {boolean} true if this element is a DragDrop handle, false
16090          * otherwise
16091          * @static
16092          */
16093         isHandle: function(sDDId, sHandleId) {
16094             return ( this.handleIds[sDDId] &&
16095                             this.handleIds[sDDId][sHandleId] );
16096         },
16097
16098         /**
16099          * Returns the DragDrop instance for a given id
16100          * @method getDDById
16101          * @param {String} id the id of the DragDrop object
16102          * @return {DragDrop} the drag drop object, null if it is not found
16103          * @static
16104          */
16105         getDDById: function(id) {
16106             for (var i in this.ids) {
16107                 if (this.ids[i][id]) {
16108                     return this.ids[i][id];
16109                 }
16110             }
16111             return null;
16112         },
16113
16114         /**
16115          * Fired after a registered DragDrop object gets the mousedown event.
16116          * Sets up the events required to track the object being dragged
16117          * @method handleMouseDown
16118          * @param {Event} e the event
16119          * @param oDD the DragDrop object being dragged
16120          * @private
16121          * @static
16122          */
16123         handleMouseDown: function(e, oDD) {
16124             if(Roo.QuickTips){
16125                 Roo.QuickTips.disable();
16126             }
16127             this.currentTarget = e.getTarget();
16128
16129             this.dragCurrent = oDD;
16130
16131             var el = oDD.getEl();
16132
16133             // track start position
16134             this.startX = e.getPageX();
16135             this.startY = e.getPageY();
16136
16137             this.deltaX = this.startX - el.offsetLeft;
16138             this.deltaY = this.startY - el.offsetTop;
16139
16140             this.dragThreshMet = false;
16141
16142             this.clickTimeout = setTimeout(
16143                     function() {
16144                         var DDM = Roo.dd.DDM;
16145                         DDM.startDrag(DDM.startX, DDM.startY);
16146                     },
16147                     this.clickTimeThresh );
16148         },
16149
16150         /**
16151          * Fired when either the drag pixel threshol or the mousedown hold
16152          * time threshold has been met.
16153          * @method startDrag
16154          * @param x {int} the X position of the original mousedown
16155          * @param y {int} the Y position of the original mousedown
16156          * @static
16157          */
16158         startDrag: function(x, y) {
16159             clearTimeout(this.clickTimeout);
16160             if (this.dragCurrent) {
16161                 this.dragCurrent.b4StartDrag(x, y);
16162                 this.dragCurrent.startDrag(x, y);
16163             }
16164             this.dragThreshMet = true;
16165         },
16166
16167         /**
16168          * Internal function to handle the mouseup event.  Will be invoked
16169          * from the context of the document.
16170          * @method handleMouseUp
16171          * @param {Event} e the event
16172          * @private
16173          * @static
16174          */
16175         handleMouseUp: function(e) {
16176
16177             if(Roo.QuickTips){
16178                 Roo.QuickTips.enable();
16179             }
16180             if (! this.dragCurrent) {
16181                 return;
16182             }
16183
16184             clearTimeout(this.clickTimeout);
16185
16186             if (this.dragThreshMet) {
16187                 this.fireEvents(e, true);
16188             } else {
16189             }
16190
16191             this.stopDrag(e);
16192
16193             this.stopEvent(e);
16194         },
16195
16196         /**
16197          * Utility to stop event propagation and event default, if these
16198          * features are turned on.
16199          * @method stopEvent
16200          * @param {Event} e the event as returned by this.getEvent()
16201          * @static
16202          */
16203         stopEvent: function(e){
16204             if(this.stopPropagation) {
16205                 e.stopPropagation();
16206             }
16207
16208             if (this.preventDefault) {
16209                 e.preventDefault();
16210             }
16211         },
16212
16213         /**
16214          * Internal function to clean up event handlers after the drag
16215          * operation is complete
16216          * @method stopDrag
16217          * @param {Event} e the event
16218          * @private
16219          * @static
16220          */
16221         stopDrag: function(e) {
16222             // Fire the drag end event for the item that was dragged
16223             if (this.dragCurrent) {
16224                 if (this.dragThreshMet) {
16225                     this.dragCurrent.b4EndDrag(e);
16226                     this.dragCurrent.endDrag(e);
16227                 }
16228
16229                 this.dragCurrent.onMouseUp(e);
16230             }
16231
16232             this.dragCurrent = null;
16233             this.dragOvers = {};
16234         },
16235
16236         /**
16237          * Internal function to handle the mousemove event.  Will be invoked
16238          * from the context of the html element.
16239          *
16240          * @TODO figure out what we can do about mouse events lost when the
16241          * user drags objects beyond the window boundary.  Currently we can
16242          * detect this in internet explorer by verifying that the mouse is
16243          * down during the mousemove event.  Firefox doesn't give us the
16244          * button state on the mousemove event.
16245          * @method handleMouseMove
16246          * @param {Event} e the event
16247          * @private
16248          * @static
16249          */
16250         handleMouseMove: function(e) {
16251             if (! this.dragCurrent) {
16252                 return true;
16253             }
16254
16255             // var button = e.which || e.button;
16256
16257             // check for IE mouseup outside of page boundary
16258             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16259                 this.stopEvent(e);
16260                 return this.handleMouseUp(e);
16261             }
16262
16263             if (!this.dragThreshMet) {
16264                 var diffX = Math.abs(this.startX - e.getPageX());
16265                 var diffY = Math.abs(this.startY - e.getPageY());
16266                 if (diffX > this.clickPixelThresh ||
16267                             diffY > this.clickPixelThresh) {
16268                     this.startDrag(this.startX, this.startY);
16269                 }
16270             }
16271
16272             if (this.dragThreshMet) {
16273                 this.dragCurrent.b4Drag(e);
16274                 this.dragCurrent.onDrag(e);
16275                 if(!this.dragCurrent.moveOnly){
16276                     this.fireEvents(e, false);
16277                 }
16278             }
16279
16280             this.stopEvent(e);
16281
16282             return true;
16283         },
16284
16285         /**
16286          * Iterates over all of the DragDrop elements to find ones we are
16287          * hovering over or dropping on
16288          * @method fireEvents
16289          * @param {Event} e the event
16290          * @param {boolean} isDrop is this a drop op or a mouseover op?
16291          * @private
16292          * @static
16293          */
16294         fireEvents: function(e, isDrop) {
16295             var dc = this.dragCurrent;
16296
16297             // If the user did the mouse up outside of the window, we could
16298             // get here even though we have ended the drag.
16299             if (!dc || dc.isLocked()) {
16300                 return;
16301             }
16302
16303             var pt = e.getPoint();
16304
16305             // cache the previous dragOver array
16306             var oldOvers = [];
16307
16308             var outEvts   = [];
16309             var overEvts  = [];
16310             var dropEvts  = [];
16311             var enterEvts = [];
16312
16313             // Check to see if the object(s) we were hovering over is no longer
16314             // being hovered over so we can fire the onDragOut event
16315             for (var i in this.dragOvers) {
16316
16317                 var ddo = this.dragOvers[i];
16318
16319                 if (! this.isTypeOfDD(ddo)) {
16320                     continue;
16321                 }
16322
16323                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16324                     outEvts.push( ddo );
16325                 }
16326
16327                 oldOvers[i] = true;
16328                 delete this.dragOvers[i];
16329             }
16330
16331             for (var sGroup in dc.groups) {
16332
16333                 if ("string" != typeof sGroup) {
16334                     continue;
16335                 }
16336
16337                 for (i in this.ids[sGroup]) {
16338                     var oDD = this.ids[sGroup][i];
16339                     if (! this.isTypeOfDD(oDD)) {
16340                         continue;
16341                     }
16342
16343                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16344                         if (this.isOverTarget(pt, oDD, this.mode)) {
16345                             // look for drop interactions
16346                             if (isDrop) {
16347                                 dropEvts.push( oDD );
16348                             // look for drag enter and drag over interactions
16349                             } else {
16350
16351                                 // initial drag over: dragEnter fires
16352                                 if (!oldOvers[oDD.id]) {
16353                                     enterEvts.push( oDD );
16354                                 // subsequent drag overs: dragOver fires
16355                                 } else {
16356                                     overEvts.push( oDD );
16357                                 }
16358
16359                                 this.dragOvers[oDD.id] = oDD;
16360                             }
16361                         }
16362                     }
16363                 }
16364             }
16365
16366             if (this.mode) {
16367                 if (outEvts.length) {
16368                     dc.b4DragOut(e, outEvts);
16369                     dc.onDragOut(e, outEvts);
16370                 }
16371
16372                 if (enterEvts.length) {
16373                     dc.onDragEnter(e, enterEvts);
16374                 }
16375
16376                 if (overEvts.length) {
16377                     dc.b4DragOver(e, overEvts);
16378                     dc.onDragOver(e, overEvts);
16379                 }
16380
16381                 if (dropEvts.length) {
16382                     dc.b4DragDrop(e, dropEvts);
16383                     dc.onDragDrop(e, dropEvts);
16384                 }
16385
16386             } else {
16387                 // fire dragout events
16388                 var len = 0;
16389                 for (i=0, len=outEvts.length; i<len; ++i) {
16390                     dc.b4DragOut(e, outEvts[i].id);
16391                     dc.onDragOut(e, outEvts[i].id);
16392                 }
16393
16394                 // fire enter events
16395                 for (i=0,len=enterEvts.length; i<len; ++i) {
16396                     // dc.b4DragEnter(e, oDD.id);
16397                     dc.onDragEnter(e, enterEvts[i].id);
16398                 }
16399
16400                 // fire over events
16401                 for (i=0,len=overEvts.length; i<len; ++i) {
16402                     dc.b4DragOver(e, overEvts[i].id);
16403                     dc.onDragOver(e, overEvts[i].id);
16404                 }
16405
16406                 // fire drop events
16407                 for (i=0, len=dropEvts.length; i<len; ++i) {
16408                     dc.b4DragDrop(e, dropEvts[i].id);
16409                     dc.onDragDrop(e, dropEvts[i].id);
16410                 }
16411
16412             }
16413
16414             // notify about a drop that did not find a target
16415             if (isDrop && !dropEvts.length) {
16416                 dc.onInvalidDrop(e);
16417             }
16418
16419         },
16420
16421         /**
16422          * Helper function for getting the best match from the list of drag
16423          * and drop objects returned by the drag and drop events when we are
16424          * in INTERSECT mode.  It returns either the first object that the
16425          * cursor is over, or the object that has the greatest overlap with
16426          * the dragged element.
16427          * @method getBestMatch
16428          * @param  {DragDrop[]} dds The array of drag and drop objects
16429          * targeted
16430          * @return {DragDrop}       The best single match
16431          * @static
16432          */
16433         getBestMatch: function(dds) {
16434             var winner = null;
16435             // Return null if the input is not what we expect
16436             //if (!dds || !dds.length || dds.length == 0) {
16437                // winner = null;
16438             // If there is only one item, it wins
16439             //} else if (dds.length == 1) {
16440
16441             var len = dds.length;
16442
16443             if (len == 1) {
16444                 winner = dds[0];
16445             } else {
16446                 // Loop through the targeted items
16447                 for (var i=0; i<len; ++i) {
16448                     var dd = dds[i];
16449                     // If the cursor is over the object, it wins.  If the
16450                     // cursor is over multiple matches, the first one we come
16451                     // to wins.
16452                     if (dd.cursorIsOver) {
16453                         winner = dd;
16454                         break;
16455                     // Otherwise the object with the most overlap wins
16456                     } else {
16457                         if (!winner ||
16458                             winner.overlap.getArea() < dd.overlap.getArea()) {
16459                             winner = dd;
16460                         }
16461                     }
16462                 }
16463             }
16464
16465             return winner;
16466         },
16467
16468         /**
16469          * Refreshes the cache of the top-left and bottom-right points of the
16470          * drag and drop objects in the specified group(s).  This is in the
16471          * format that is stored in the drag and drop instance, so typical
16472          * usage is:
16473          * <code>
16474          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16475          * </code>
16476          * Alternatively:
16477          * <code>
16478          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16479          * </code>
16480          * @TODO this really should be an indexed array.  Alternatively this
16481          * method could accept both.
16482          * @method refreshCache
16483          * @param {Object} groups an associative array of groups to refresh
16484          * @static
16485          */
16486         refreshCache: function(groups) {
16487             for (var sGroup in groups) {
16488                 if ("string" != typeof sGroup) {
16489                     continue;
16490                 }
16491                 for (var i in this.ids[sGroup]) {
16492                     var oDD = this.ids[sGroup][i];
16493
16494                     if (this.isTypeOfDD(oDD)) {
16495                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16496                         var loc = this.getLocation(oDD);
16497                         if (loc) {
16498                             this.locationCache[oDD.id] = loc;
16499                         } else {
16500                             delete this.locationCache[oDD.id];
16501                             // this will unregister the drag and drop object if
16502                             // the element is not in a usable state
16503                             // oDD.unreg();
16504                         }
16505                     }
16506                 }
16507             }
16508         },
16509
16510         /**
16511          * This checks to make sure an element exists and is in the DOM.  The
16512          * main purpose is to handle cases where innerHTML is used to remove
16513          * drag and drop objects from the DOM.  IE provides an 'unspecified
16514          * error' when trying to access the offsetParent of such an element
16515          * @method verifyEl
16516          * @param {HTMLElement} el the element to check
16517          * @return {boolean} true if the element looks usable
16518          * @static
16519          */
16520         verifyEl: function(el) {
16521             if (el) {
16522                 var parent;
16523                 if(Roo.isIE){
16524                     try{
16525                         parent = el.offsetParent;
16526                     }catch(e){}
16527                 }else{
16528                     parent = el.offsetParent;
16529                 }
16530                 if (parent) {
16531                     return true;
16532                 }
16533             }
16534
16535             return false;
16536         },
16537
16538         /**
16539          * Returns a Region object containing the drag and drop element's position
16540          * and size, including the padding configured for it
16541          * @method getLocation
16542          * @param {DragDrop} oDD the drag and drop object to get the
16543          *                       location for
16544          * @return {Roo.lib.Region} a Region object representing the total area
16545          *                             the element occupies, including any padding
16546          *                             the instance is configured for.
16547          * @static
16548          */
16549         getLocation: function(oDD) {
16550             if (! this.isTypeOfDD(oDD)) {
16551                 return null;
16552             }
16553
16554             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16555
16556             try {
16557                 pos= Roo.lib.Dom.getXY(el);
16558             } catch (e) { }
16559
16560             if (!pos) {
16561                 return null;
16562             }
16563
16564             x1 = pos[0];
16565             x2 = x1 + el.offsetWidth;
16566             y1 = pos[1];
16567             y2 = y1 + el.offsetHeight;
16568
16569             t = y1 - oDD.padding[0];
16570             r = x2 + oDD.padding[1];
16571             b = y2 + oDD.padding[2];
16572             l = x1 - oDD.padding[3];
16573
16574             return new Roo.lib.Region( t, r, b, l );
16575         },
16576
16577         /**
16578          * Checks the cursor location to see if it over the target
16579          * @method isOverTarget
16580          * @param {Roo.lib.Point} pt The point to evaluate
16581          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16582          * @return {boolean} true if the mouse is over the target
16583          * @private
16584          * @static
16585          */
16586         isOverTarget: function(pt, oTarget, intersect) {
16587             // use cache if available
16588             var loc = this.locationCache[oTarget.id];
16589             if (!loc || !this.useCache) {
16590                 loc = this.getLocation(oTarget);
16591                 this.locationCache[oTarget.id] = loc;
16592
16593             }
16594
16595             if (!loc) {
16596                 return false;
16597             }
16598
16599             oTarget.cursorIsOver = loc.contains( pt );
16600
16601             // DragDrop is using this as a sanity check for the initial mousedown
16602             // in this case we are done.  In POINT mode, if the drag obj has no
16603             // contraints, we are also done. Otherwise we need to evaluate the
16604             // location of the target as related to the actual location of the
16605             // dragged element.
16606             var dc = this.dragCurrent;
16607             if (!dc || !dc.getTargetCoord ||
16608                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16609                 return oTarget.cursorIsOver;
16610             }
16611
16612             oTarget.overlap = null;
16613
16614             // Get the current location of the drag element, this is the
16615             // location of the mouse event less the delta that represents
16616             // where the original mousedown happened on the element.  We
16617             // need to consider constraints and ticks as well.
16618             var pos = dc.getTargetCoord(pt.x, pt.y);
16619
16620             var el = dc.getDragEl();
16621             var curRegion = new Roo.lib.Region( pos.y,
16622                                                    pos.x + el.offsetWidth,
16623                                                    pos.y + el.offsetHeight,
16624                                                    pos.x );
16625
16626             var overlap = curRegion.intersect(loc);
16627
16628             if (overlap) {
16629                 oTarget.overlap = overlap;
16630                 return (intersect) ? true : oTarget.cursorIsOver;
16631             } else {
16632                 return false;
16633             }
16634         },
16635
16636         /**
16637          * unload event handler
16638          * @method _onUnload
16639          * @private
16640          * @static
16641          */
16642         _onUnload: function(e, me) {
16643             Roo.dd.DragDropMgr.unregAll();
16644         },
16645
16646         /**
16647          * Cleans up the drag and drop events and objects.
16648          * @method unregAll
16649          * @private
16650          * @static
16651          */
16652         unregAll: function() {
16653
16654             if (this.dragCurrent) {
16655                 this.stopDrag();
16656                 this.dragCurrent = null;
16657             }
16658
16659             this._execOnAll("unreg", []);
16660
16661             for (i in this.elementCache) {
16662                 delete this.elementCache[i];
16663             }
16664
16665             this.elementCache = {};
16666             this.ids = {};
16667         },
16668
16669         /**
16670          * A cache of DOM elements
16671          * @property elementCache
16672          * @private
16673          * @static
16674          */
16675         elementCache: {},
16676
16677         /**
16678          * Get the wrapper for the DOM element specified
16679          * @method getElWrapper
16680          * @param {String} id the id of the element to get
16681          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16682          * @private
16683          * @deprecated This wrapper isn't that useful
16684          * @static
16685          */
16686         getElWrapper: function(id) {
16687             var oWrapper = this.elementCache[id];
16688             if (!oWrapper || !oWrapper.el) {
16689                 oWrapper = this.elementCache[id] =
16690                     new this.ElementWrapper(Roo.getDom(id));
16691             }
16692             return oWrapper;
16693         },
16694
16695         /**
16696          * Returns the actual DOM element
16697          * @method getElement
16698          * @param {String} id the id of the elment to get
16699          * @return {Object} The element
16700          * @deprecated use Roo.getDom instead
16701          * @static
16702          */
16703         getElement: function(id) {
16704             return Roo.getDom(id);
16705         },
16706
16707         /**
16708          * Returns the style property for the DOM element (i.e.,
16709          * document.getElById(id).style)
16710          * @method getCss
16711          * @param {String} id the id of the elment to get
16712          * @return {Object} The style property of the element
16713          * @deprecated use Roo.getDom instead
16714          * @static
16715          */
16716         getCss: function(id) {
16717             var el = Roo.getDom(id);
16718             return (el) ? el.style : null;
16719         },
16720
16721         /**
16722          * Inner class for cached elements
16723          * @class DragDropMgr.ElementWrapper
16724          * @for DragDropMgr
16725          * @private
16726          * @deprecated
16727          */
16728         ElementWrapper: function(el) {
16729                 /**
16730                  * The element
16731                  * @property el
16732                  */
16733                 this.el = el || null;
16734                 /**
16735                  * The element id
16736                  * @property id
16737                  */
16738                 this.id = this.el && el.id;
16739                 /**
16740                  * A reference to the style property
16741                  * @property css
16742                  */
16743                 this.css = this.el && el.style;
16744             },
16745
16746         /**
16747          * Returns the X position of an html element
16748          * @method getPosX
16749          * @param el the element for which to get the position
16750          * @return {int} the X coordinate
16751          * @for DragDropMgr
16752          * @deprecated use Roo.lib.Dom.getX instead
16753          * @static
16754          */
16755         getPosX: function(el) {
16756             return Roo.lib.Dom.getX(el);
16757         },
16758
16759         /**
16760          * Returns the Y position of an html element
16761          * @method getPosY
16762          * @param el the element for which to get the position
16763          * @return {int} the Y coordinate
16764          * @deprecated use Roo.lib.Dom.getY instead
16765          * @static
16766          */
16767         getPosY: function(el) {
16768             return Roo.lib.Dom.getY(el);
16769         },
16770
16771         /**
16772          * Swap two nodes.  In IE, we use the native method, for others we
16773          * emulate the IE behavior
16774          * @method swapNode
16775          * @param n1 the first node to swap
16776          * @param n2 the other node to swap
16777          * @static
16778          */
16779         swapNode: function(n1, n2) {
16780             if (n1.swapNode) {
16781                 n1.swapNode(n2);
16782             } else {
16783                 var p = n2.parentNode;
16784                 var s = n2.nextSibling;
16785
16786                 if (s == n1) {
16787                     p.insertBefore(n1, n2);
16788                 } else if (n2 == n1.nextSibling) {
16789                     p.insertBefore(n2, n1);
16790                 } else {
16791                     n1.parentNode.replaceChild(n2, n1);
16792                     p.insertBefore(n1, s);
16793                 }
16794             }
16795         },
16796
16797         /**
16798          * Returns the current scroll position
16799          * @method getScroll
16800          * @private
16801          * @static
16802          */
16803         getScroll: function () {
16804             var t, l, dde=document.documentElement, db=document.body;
16805             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16806                 t = dde.scrollTop;
16807                 l = dde.scrollLeft;
16808             } else if (db) {
16809                 t = db.scrollTop;
16810                 l = db.scrollLeft;
16811             } else {
16812
16813             }
16814             return { top: t, left: l };
16815         },
16816
16817         /**
16818          * Returns the specified element style property
16819          * @method getStyle
16820          * @param {HTMLElement} el          the element
16821          * @param {string}      styleProp   the style property
16822          * @return {string} The value of the style property
16823          * @deprecated use Roo.lib.Dom.getStyle
16824          * @static
16825          */
16826         getStyle: function(el, styleProp) {
16827             return Roo.fly(el).getStyle(styleProp);
16828         },
16829
16830         /**
16831          * Gets the scrollTop
16832          * @method getScrollTop
16833          * @return {int} the document's scrollTop
16834          * @static
16835          */
16836         getScrollTop: function () { return this.getScroll().top; },
16837
16838         /**
16839          * Gets the scrollLeft
16840          * @method getScrollLeft
16841          * @return {int} the document's scrollTop
16842          * @static
16843          */
16844         getScrollLeft: function () { return this.getScroll().left; },
16845
16846         /**
16847          * Sets the x/y position of an element to the location of the
16848          * target element.
16849          * @method moveToEl
16850          * @param {HTMLElement} moveEl      The element to move
16851          * @param {HTMLElement} targetEl    The position reference element
16852          * @static
16853          */
16854         moveToEl: function (moveEl, targetEl) {
16855             var aCoord = Roo.lib.Dom.getXY(targetEl);
16856             Roo.lib.Dom.setXY(moveEl, aCoord);
16857         },
16858
16859         /**
16860          * Numeric array sort function
16861          * @method numericSort
16862          * @static
16863          */
16864         numericSort: function(a, b) { return (a - b); },
16865
16866         /**
16867          * Internal counter
16868          * @property _timeoutCount
16869          * @private
16870          * @static
16871          */
16872         _timeoutCount: 0,
16873
16874         /**
16875          * Trying to make the load order less important.  Without this we get
16876          * an error if this file is loaded before the Event Utility.
16877          * @method _addListeners
16878          * @private
16879          * @static
16880          */
16881         _addListeners: function() {
16882             var DDM = Roo.dd.DDM;
16883             if ( Roo.lib.Event && document ) {
16884                 DDM._onLoad();
16885             } else {
16886                 if (DDM._timeoutCount > 2000) {
16887                 } else {
16888                     setTimeout(DDM._addListeners, 10);
16889                     if (document && document.body) {
16890                         DDM._timeoutCount += 1;
16891                     }
16892                 }
16893             }
16894         },
16895
16896         /**
16897          * Recursively searches the immediate parent and all child nodes for
16898          * the handle element in order to determine wheter or not it was
16899          * clicked.
16900          * @method handleWasClicked
16901          * @param node the html element to inspect
16902          * @static
16903          */
16904         handleWasClicked: function(node, id) {
16905             if (this.isHandle(id, node.id)) {
16906                 return true;
16907             } else {
16908                 // check to see if this is a text node child of the one we want
16909                 var p = node.parentNode;
16910
16911                 while (p) {
16912                     if (this.isHandle(id, p.id)) {
16913                         return true;
16914                     } else {
16915                         p = p.parentNode;
16916                     }
16917                 }
16918             }
16919
16920             return false;
16921         }
16922
16923     };
16924
16925 }();
16926
16927 // shorter alias, save a few bytes
16928 Roo.dd.DDM = Roo.dd.DragDropMgr;
16929 Roo.dd.DDM._addListeners();
16930
16931 }/*
16932  * Based on:
16933  * Ext JS Library 1.1.1
16934  * Copyright(c) 2006-2007, Ext JS, LLC.
16935  *
16936  * Originally Released Under LGPL - original licence link has changed is not relivant.
16937  *
16938  * Fork - LGPL
16939  * <script type="text/javascript">
16940  */
16941
16942 /**
16943  * @class Roo.dd.DD
16944  * A DragDrop implementation where the linked element follows the
16945  * mouse cursor during a drag.
16946  * @extends Roo.dd.DragDrop
16947  * @constructor
16948  * @param {String} id the id of the linked element
16949  * @param {String} sGroup the group of related DragDrop items
16950  * @param {object} config an object containing configurable attributes
16951  *                Valid properties for DD:
16952  *                    scroll
16953  */
16954 Roo.dd.DD = function(id, sGroup, config) {
16955     if (id) {
16956         this.init(id, sGroup, config);
16957     }
16958 };
16959
16960 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16961
16962     /**
16963      * When set to true, the utility automatically tries to scroll the browser
16964      * window wehn a drag and drop element is dragged near the viewport boundary.
16965      * Defaults to true.
16966      * @property scroll
16967      * @type boolean
16968      */
16969     scroll: true,
16970
16971     /**
16972      * Sets the pointer offset to the distance between the linked element's top
16973      * left corner and the location the element was clicked
16974      * @method autoOffset
16975      * @param {int} iPageX the X coordinate of the click
16976      * @param {int} iPageY the Y coordinate of the click
16977      */
16978     autoOffset: function(iPageX, iPageY) {
16979         var x = iPageX - this.startPageX;
16980         var y = iPageY - this.startPageY;
16981         this.setDelta(x, y);
16982     },
16983
16984     /**
16985      * Sets the pointer offset.  You can call this directly to force the
16986      * offset to be in a particular location (e.g., pass in 0,0 to set it
16987      * to the center of the object)
16988      * @method setDelta
16989      * @param {int} iDeltaX the distance from the left
16990      * @param {int} iDeltaY the distance from the top
16991      */
16992     setDelta: function(iDeltaX, iDeltaY) {
16993         this.deltaX = iDeltaX;
16994         this.deltaY = iDeltaY;
16995     },
16996
16997     /**
16998      * Sets the drag element to the location of the mousedown or click event,
16999      * maintaining the cursor location relative to the location on the element
17000      * that was clicked.  Override this if you want to place the element in a
17001      * location other than where the cursor is.
17002      * @method setDragElPos
17003      * @param {int} iPageX the X coordinate of the mousedown or drag event
17004      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17005      */
17006     setDragElPos: function(iPageX, iPageY) {
17007         // the first time we do this, we are going to check to make sure
17008         // the element has css positioning
17009
17010         var el = this.getDragEl();
17011         this.alignElWithMouse(el, iPageX, iPageY);
17012     },
17013
17014     /**
17015      * Sets the element to the location of the mousedown or click event,
17016      * maintaining the cursor location relative to the location on the element
17017      * that was clicked.  Override this if you want to place the element in a
17018      * location other than where the cursor is.
17019      * @method alignElWithMouse
17020      * @param {HTMLElement} el the element to move
17021      * @param {int} iPageX the X coordinate of the mousedown or drag event
17022      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17023      */
17024     alignElWithMouse: function(el, iPageX, iPageY) {
17025         var oCoord = this.getTargetCoord(iPageX, iPageY);
17026         var fly = el.dom ? el : Roo.fly(el);
17027         if (!this.deltaSetXY) {
17028             var aCoord = [oCoord.x, oCoord.y];
17029             fly.setXY(aCoord);
17030             var newLeft = fly.getLeft(true);
17031             var newTop  = fly.getTop(true);
17032             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17033         } else {
17034             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17035         }
17036
17037         this.cachePosition(oCoord.x, oCoord.y);
17038         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17039         return oCoord;
17040     },
17041
17042     /**
17043      * Saves the most recent position so that we can reset the constraints and
17044      * tick marks on-demand.  We need to know this so that we can calculate the
17045      * number of pixels the element is offset from its original position.
17046      * @method cachePosition
17047      * @param iPageX the current x position (optional, this just makes it so we
17048      * don't have to look it up again)
17049      * @param iPageY the current y position (optional, this just makes it so we
17050      * don't have to look it up again)
17051      */
17052     cachePosition: function(iPageX, iPageY) {
17053         if (iPageX) {
17054             this.lastPageX = iPageX;
17055             this.lastPageY = iPageY;
17056         } else {
17057             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17058             this.lastPageX = aCoord[0];
17059             this.lastPageY = aCoord[1];
17060         }
17061     },
17062
17063     /**
17064      * Auto-scroll the window if the dragged object has been moved beyond the
17065      * visible window boundary.
17066      * @method autoScroll
17067      * @param {int} x the drag element's x position
17068      * @param {int} y the drag element's y position
17069      * @param {int} h the height of the drag element
17070      * @param {int} w the width of the drag element
17071      * @private
17072      */
17073     autoScroll: function(x, y, h, w) {
17074
17075         if (this.scroll) {
17076             // The client height
17077             var clientH = Roo.lib.Dom.getViewWidth();
17078
17079             // The client width
17080             var clientW = Roo.lib.Dom.getViewHeight();
17081
17082             // The amt scrolled down
17083             var st = this.DDM.getScrollTop();
17084
17085             // The amt scrolled right
17086             var sl = this.DDM.getScrollLeft();
17087
17088             // Location of the bottom of the element
17089             var bot = h + y;
17090
17091             // Location of the right of the element
17092             var right = w + x;
17093
17094             // The distance from the cursor to the bottom of the visible area,
17095             // adjusted so that we don't scroll if the cursor is beyond the
17096             // element drag constraints
17097             var toBot = (clientH + st - y - this.deltaY);
17098
17099             // The distance from the cursor to the right of the visible area
17100             var toRight = (clientW + sl - x - this.deltaX);
17101
17102
17103             // How close to the edge the cursor must be before we scroll
17104             // var thresh = (document.all) ? 100 : 40;
17105             var thresh = 40;
17106
17107             // How many pixels to scroll per autoscroll op.  This helps to reduce
17108             // clunky scrolling. IE is more sensitive about this ... it needs this
17109             // value to be higher.
17110             var scrAmt = (document.all) ? 80 : 30;
17111
17112             // Scroll down if we are near the bottom of the visible page and the
17113             // obj extends below the crease
17114             if ( bot > clientH && toBot < thresh ) {
17115                 window.scrollTo(sl, st + scrAmt);
17116             }
17117
17118             // Scroll up if the window is scrolled down and the top of the object
17119             // goes above the top border
17120             if ( y < st && st > 0 && y - st < thresh ) {
17121                 window.scrollTo(sl, st - scrAmt);
17122             }
17123
17124             // Scroll right if the obj is beyond the right border and the cursor is
17125             // near the border.
17126             if ( right > clientW && toRight < thresh ) {
17127                 window.scrollTo(sl + scrAmt, st);
17128             }
17129
17130             // Scroll left if the window has been scrolled to the right and the obj
17131             // extends past the left border
17132             if ( x < sl && sl > 0 && x - sl < thresh ) {
17133                 window.scrollTo(sl - scrAmt, st);
17134             }
17135         }
17136     },
17137
17138     /**
17139      * Finds the location the element should be placed if we want to move
17140      * it to where the mouse location less the click offset would place us.
17141      * @method getTargetCoord
17142      * @param {int} iPageX the X coordinate of the click
17143      * @param {int} iPageY the Y coordinate of the click
17144      * @return an object that contains the coordinates (Object.x and Object.y)
17145      * @private
17146      */
17147     getTargetCoord: function(iPageX, iPageY) {
17148
17149
17150         var x = iPageX - this.deltaX;
17151         var y = iPageY - this.deltaY;
17152
17153         if (this.constrainX) {
17154             if (x < this.minX) { x = this.minX; }
17155             if (x > this.maxX) { x = this.maxX; }
17156         }
17157
17158         if (this.constrainY) {
17159             if (y < this.minY) { y = this.minY; }
17160             if (y > this.maxY) { y = this.maxY; }
17161         }
17162
17163         x = this.getTick(x, this.xTicks);
17164         y = this.getTick(y, this.yTicks);
17165
17166
17167         return {x:x, y:y};
17168     },
17169
17170     /*
17171      * Sets up config options specific to this class. Overrides
17172      * Roo.dd.DragDrop, but all versions of this method through the
17173      * inheritance chain are called
17174      */
17175     applyConfig: function() {
17176         Roo.dd.DD.superclass.applyConfig.call(this);
17177         this.scroll = (this.config.scroll !== false);
17178     },
17179
17180     /*
17181      * Event that fires prior to the onMouseDown event.  Overrides
17182      * Roo.dd.DragDrop.
17183      */
17184     b4MouseDown: function(e) {
17185         // this.resetConstraints();
17186         this.autoOffset(e.getPageX(),
17187                             e.getPageY());
17188     },
17189
17190     /*
17191      * Event that fires prior to the onDrag event.  Overrides
17192      * Roo.dd.DragDrop.
17193      */
17194     b4Drag: function(e) {
17195         this.setDragElPos(e.getPageX(),
17196                             e.getPageY());
17197     },
17198
17199     toString: function() {
17200         return ("DD " + this.id);
17201     }
17202
17203     //////////////////////////////////////////////////////////////////////////
17204     // Debugging ygDragDrop events that can be overridden
17205     //////////////////////////////////////////////////////////////////////////
17206     /*
17207     startDrag: function(x, y) {
17208     },
17209
17210     onDrag: function(e) {
17211     },
17212
17213     onDragEnter: function(e, id) {
17214     },
17215
17216     onDragOver: function(e, id) {
17217     },
17218
17219     onDragOut: function(e, id) {
17220     },
17221
17222     onDragDrop: function(e, id) {
17223     },
17224
17225     endDrag: function(e) {
17226     }
17227
17228     */
17229
17230 });/*
17231  * Based on:
17232  * Ext JS Library 1.1.1
17233  * Copyright(c) 2006-2007, Ext JS, LLC.
17234  *
17235  * Originally Released Under LGPL - original licence link has changed is not relivant.
17236  *
17237  * Fork - LGPL
17238  * <script type="text/javascript">
17239  */
17240
17241 /**
17242  * @class Roo.dd.DDProxy
17243  * A DragDrop implementation that inserts an empty, bordered div into
17244  * the document that follows the cursor during drag operations.  At the time of
17245  * the click, the frame div is resized to the dimensions of the linked html
17246  * element, and moved to the exact location of the linked element.
17247  *
17248  * References to the "frame" element refer to the single proxy element that
17249  * was created to be dragged in place of all DDProxy elements on the
17250  * page.
17251  *
17252  * @extends Roo.dd.DD
17253  * @constructor
17254  * @param {String} id the id of the linked html element
17255  * @param {String} sGroup the group of related DragDrop objects
17256  * @param {object} config an object containing configurable attributes
17257  *                Valid properties for DDProxy in addition to those in DragDrop:
17258  *                   resizeFrame, centerFrame, dragElId
17259  */
17260 Roo.dd.DDProxy = function(id, sGroup, config) {
17261     if (id) {
17262         this.init(id, sGroup, config);
17263         this.initFrame();
17264     }
17265 };
17266
17267 /**
17268  * The default drag frame div id
17269  * @property Roo.dd.DDProxy.dragElId
17270  * @type String
17271  * @static
17272  */
17273 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17274
17275 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17276
17277     /**
17278      * By default we resize the drag frame to be the same size as the element
17279      * we want to drag (this is to get the frame effect).  We can turn it off
17280      * if we want a different behavior.
17281      * @property resizeFrame
17282      * @type boolean
17283      */
17284     resizeFrame: true,
17285
17286     /**
17287      * By default the frame is positioned exactly where the drag element is, so
17288      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17289      * you do not have constraints on the obj is to have the drag frame centered
17290      * around the cursor.  Set centerFrame to true for this effect.
17291      * @property centerFrame
17292      * @type boolean
17293      */
17294     centerFrame: false,
17295
17296     /**
17297      * Creates the proxy element if it does not yet exist
17298      * @method createFrame
17299      */
17300     createFrame: function() {
17301         var self = this;
17302         var body = document.body;
17303
17304         if (!body || !body.firstChild) {
17305             setTimeout( function() { self.createFrame(); }, 50 );
17306             return;
17307         }
17308
17309         var div = this.getDragEl();
17310
17311         if (!div) {
17312             div    = document.createElement("div");
17313             div.id = this.dragElId;
17314             var s  = div.style;
17315
17316             s.position   = "absolute";
17317             s.visibility = "hidden";
17318             s.cursor     = "move";
17319             s.border     = "2px solid #aaa";
17320             s.zIndex     = 999;
17321
17322             // appendChild can blow up IE if invoked prior to the window load event
17323             // while rendering a table.  It is possible there are other scenarios
17324             // that would cause this to happen as well.
17325             body.insertBefore(div, body.firstChild);
17326         }
17327     },
17328
17329     /**
17330      * Initialization for the drag frame element.  Must be called in the
17331      * constructor of all subclasses
17332      * @method initFrame
17333      */
17334     initFrame: function() {
17335         this.createFrame();
17336     },
17337
17338     applyConfig: function() {
17339         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17340
17341         this.resizeFrame = (this.config.resizeFrame !== false);
17342         this.centerFrame = (this.config.centerFrame);
17343         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17344     },
17345
17346     /**
17347      * Resizes the drag frame to the dimensions of the clicked object, positions
17348      * it over the object, and finally displays it
17349      * @method showFrame
17350      * @param {int} iPageX X click position
17351      * @param {int} iPageY Y click position
17352      * @private
17353      */
17354     showFrame: function(iPageX, iPageY) {
17355         var el = this.getEl();
17356         var dragEl = this.getDragEl();
17357         var s = dragEl.style;
17358
17359         this._resizeProxy();
17360
17361         if (this.centerFrame) {
17362             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17363                            Math.round(parseInt(s.height, 10)/2) );
17364         }
17365
17366         this.setDragElPos(iPageX, iPageY);
17367
17368         Roo.fly(dragEl).show();
17369     },
17370
17371     /**
17372      * The proxy is automatically resized to the dimensions of the linked
17373      * element when a drag is initiated, unless resizeFrame is set to false
17374      * @method _resizeProxy
17375      * @private
17376      */
17377     _resizeProxy: function() {
17378         if (this.resizeFrame) {
17379             var el = this.getEl();
17380             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17381         }
17382     },
17383
17384     // overrides Roo.dd.DragDrop
17385     b4MouseDown: function(e) {
17386         var x = e.getPageX();
17387         var y = e.getPageY();
17388         this.autoOffset(x, y);
17389         this.setDragElPos(x, y);
17390     },
17391
17392     // overrides Roo.dd.DragDrop
17393     b4StartDrag: function(x, y) {
17394         // show the drag frame
17395         this.showFrame(x, y);
17396     },
17397
17398     // overrides Roo.dd.DragDrop
17399     b4EndDrag: function(e) {
17400         Roo.fly(this.getDragEl()).hide();
17401     },
17402
17403     // overrides Roo.dd.DragDrop
17404     // By default we try to move the element to the last location of the frame.
17405     // This is so that the default behavior mirrors that of Roo.dd.DD.
17406     endDrag: function(e) {
17407
17408         var lel = this.getEl();
17409         var del = this.getDragEl();
17410
17411         // Show the drag frame briefly so we can get its position
17412         del.style.visibility = "";
17413
17414         this.beforeMove();
17415         // Hide the linked element before the move to get around a Safari
17416         // rendering bug.
17417         lel.style.visibility = "hidden";
17418         Roo.dd.DDM.moveToEl(lel, del);
17419         del.style.visibility = "hidden";
17420         lel.style.visibility = "";
17421
17422         this.afterDrag();
17423     },
17424
17425     beforeMove : function(){
17426
17427     },
17428
17429     afterDrag : function(){
17430
17431     },
17432
17433     toString: function() {
17434         return ("DDProxy " + this.id);
17435     }
17436
17437 });
17438 /*
17439  * Based on:
17440  * Ext JS Library 1.1.1
17441  * Copyright(c) 2006-2007, Ext JS, LLC.
17442  *
17443  * Originally Released Under LGPL - original licence link has changed is not relivant.
17444  *
17445  * Fork - LGPL
17446  * <script type="text/javascript">
17447  */
17448
17449  /**
17450  * @class Roo.dd.DDTarget
17451  * A DragDrop implementation that does not move, but can be a drop
17452  * target.  You would get the same result by simply omitting implementation
17453  * for the event callbacks, but this way we reduce the processing cost of the
17454  * event listener and the callbacks.
17455  * @extends Roo.dd.DragDrop
17456  * @constructor
17457  * @param {String} id the id of the element that is a drop target
17458  * @param {String} sGroup the group of related DragDrop objects
17459  * @param {object} config an object containing configurable attributes
17460  *                 Valid properties for DDTarget in addition to those in
17461  *                 DragDrop:
17462  *                    none
17463  */
17464 Roo.dd.DDTarget = function(id, sGroup, config) {
17465     if (id) {
17466         this.initTarget(id, sGroup, config);
17467     }
17468     if (config.listeners || config.events) { 
17469        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17470             listeners : config.listeners || {}, 
17471             events : config.events || {} 
17472         });    
17473     }
17474 };
17475
17476 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17477 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17478     toString: function() {
17479         return ("DDTarget " + this.id);
17480     }
17481 });
17482 /*
17483  * Based on:
17484  * Ext JS Library 1.1.1
17485  * Copyright(c) 2006-2007, Ext JS, LLC.
17486  *
17487  * Originally Released Under LGPL - original licence link has changed is not relivant.
17488  *
17489  * Fork - LGPL
17490  * <script type="text/javascript">
17491  */
17492  
17493
17494 /**
17495  * @class Roo.dd.ScrollManager
17496  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17497  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17498  * @singleton
17499  */
17500 Roo.dd.ScrollManager = function(){
17501     var ddm = Roo.dd.DragDropMgr;
17502     var els = {};
17503     var dragEl = null;
17504     var proc = {};
17505     
17506     var onStop = function(e){
17507         dragEl = null;
17508         clearProc();
17509     };
17510     
17511     var triggerRefresh = function(){
17512         if(ddm.dragCurrent){
17513              ddm.refreshCache(ddm.dragCurrent.groups);
17514         }
17515     };
17516     
17517     var doScroll = function(){
17518         if(ddm.dragCurrent){
17519             var dds = Roo.dd.ScrollManager;
17520             if(!dds.animate){
17521                 if(proc.el.scroll(proc.dir, dds.increment)){
17522                     triggerRefresh();
17523                 }
17524             }else{
17525                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17526             }
17527         }
17528     };
17529     
17530     var clearProc = function(){
17531         if(proc.id){
17532             clearInterval(proc.id);
17533         }
17534         proc.id = 0;
17535         proc.el = null;
17536         proc.dir = "";
17537     };
17538     
17539     var startProc = function(el, dir){
17540         clearProc();
17541         proc.el = el;
17542         proc.dir = dir;
17543         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17544     };
17545     
17546     var onFire = function(e, isDrop){
17547         if(isDrop || !ddm.dragCurrent){ return; }
17548         var dds = Roo.dd.ScrollManager;
17549         if(!dragEl || dragEl != ddm.dragCurrent){
17550             dragEl = ddm.dragCurrent;
17551             // refresh regions on drag start
17552             dds.refreshCache();
17553         }
17554         
17555         var xy = Roo.lib.Event.getXY(e);
17556         var pt = new Roo.lib.Point(xy[0], xy[1]);
17557         for(var id in els){
17558             var el = els[id], r = el._region;
17559             if(r && r.contains(pt) && el.isScrollable()){
17560                 if(r.bottom - pt.y <= dds.thresh){
17561                     if(proc.el != el){
17562                         startProc(el, "down");
17563                     }
17564                     return;
17565                 }else if(r.right - pt.x <= dds.thresh){
17566                     if(proc.el != el){
17567                         startProc(el, "left");
17568                     }
17569                     return;
17570                 }else if(pt.y - r.top <= dds.thresh){
17571                     if(proc.el != el){
17572                         startProc(el, "up");
17573                     }
17574                     return;
17575                 }else if(pt.x - r.left <= dds.thresh){
17576                     if(proc.el != el){
17577                         startProc(el, "right");
17578                     }
17579                     return;
17580                 }
17581             }
17582         }
17583         clearProc();
17584     };
17585     
17586     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17587     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17588     
17589     return {
17590         /**
17591          * Registers new overflow element(s) to auto scroll
17592          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17593          */
17594         register : function(el){
17595             if(el instanceof Array){
17596                 for(var i = 0, len = el.length; i < len; i++) {
17597                         this.register(el[i]);
17598                 }
17599             }else{
17600                 el = Roo.get(el);
17601                 els[el.id] = el;
17602             }
17603         },
17604         
17605         /**
17606          * Unregisters overflow element(s) so they are no longer scrolled
17607          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17608          */
17609         unregister : function(el){
17610             if(el instanceof Array){
17611                 for(var i = 0, len = el.length; i < len; i++) {
17612                         this.unregister(el[i]);
17613                 }
17614             }else{
17615                 el = Roo.get(el);
17616                 delete els[el.id];
17617             }
17618         },
17619         
17620         /**
17621          * The number of pixels from the edge of a container the pointer needs to be to 
17622          * trigger scrolling (defaults to 25)
17623          * @type Number
17624          */
17625         thresh : 25,
17626         
17627         /**
17628          * The number of pixels to scroll in each scroll increment (defaults to 50)
17629          * @type Number
17630          */
17631         increment : 100,
17632         
17633         /**
17634          * The frequency of scrolls in milliseconds (defaults to 500)
17635          * @type Number
17636          */
17637         frequency : 500,
17638         
17639         /**
17640          * True to animate the scroll (defaults to true)
17641          * @type Boolean
17642          */
17643         animate: true,
17644         
17645         /**
17646          * The animation duration in seconds - 
17647          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17648          * @type Number
17649          */
17650         animDuration: .4,
17651         
17652         /**
17653          * Manually trigger a cache refresh.
17654          */
17655         refreshCache : function(){
17656             for(var id in els){
17657                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17658                     els[id]._region = els[id].getRegion();
17659                 }
17660             }
17661         }
17662     };
17663 }();/*
17664  * Based on:
17665  * Ext JS Library 1.1.1
17666  * Copyright(c) 2006-2007, Ext JS, LLC.
17667  *
17668  * Originally Released Under LGPL - original licence link has changed is not relivant.
17669  *
17670  * Fork - LGPL
17671  * <script type="text/javascript">
17672  */
17673  
17674
17675 /**
17676  * @class Roo.dd.Registry
17677  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17678  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17679  * @singleton
17680  */
17681 Roo.dd.Registry = function(){
17682     var elements = {}; 
17683     var handles = {}; 
17684     var autoIdSeed = 0;
17685
17686     var getId = function(el, autogen){
17687         if(typeof el == "string"){
17688             return el;
17689         }
17690         var id = el.id;
17691         if(!id && autogen !== false){
17692             id = "roodd-" + (++autoIdSeed);
17693             el.id = id;
17694         }
17695         return id;
17696     };
17697     
17698     return {
17699     /**
17700      * Register a drag drop element
17701      * @param {String|HTMLElement} element The id or DOM node to register
17702      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17703      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17704      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17705      * populated in the data object (if applicable):
17706      * <pre>
17707 Value      Description<br />
17708 ---------  ------------------------------------------<br />
17709 handles    Array of DOM nodes that trigger dragging<br />
17710            for the element being registered<br />
17711 isHandle   True if the element passed in triggers<br />
17712            dragging itself, else false
17713 </pre>
17714      */
17715         register : function(el, data){
17716             data = data || {};
17717             if(typeof el == "string"){
17718                 el = document.getElementById(el);
17719             }
17720             data.ddel = el;
17721             elements[getId(el)] = data;
17722             if(data.isHandle !== false){
17723                 handles[data.ddel.id] = data;
17724             }
17725             if(data.handles){
17726                 var hs = data.handles;
17727                 for(var i = 0, len = hs.length; i < len; i++){
17728                         handles[getId(hs[i])] = data;
17729                 }
17730             }
17731         },
17732
17733     /**
17734      * Unregister a drag drop element
17735      * @param {String|HTMLElement}  element The id or DOM node to unregister
17736      */
17737         unregister : function(el){
17738             var id = getId(el, false);
17739             var data = elements[id];
17740             if(data){
17741                 delete elements[id];
17742                 if(data.handles){
17743                     var hs = data.handles;
17744                     for(var i = 0, len = hs.length; i < len; i++){
17745                         delete handles[getId(hs[i], false)];
17746                     }
17747                 }
17748             }
17749         },
17750
17751     /**
17752      * Returns the handle registered for a DOM Node by id
17753      * @param {String|HTMLElement} id The DOM node or id to look up
17754      * @return {Object} handle The custom handle data
17755      */
17756         getHandle : function(id){
17757             if(typeof id != "string"){ // must be element?
17758                 id = id.id;
17759             }
17760             return handles[id];
17761         },
17762
17763     /**
17764      * Returns the handle that is registered for the DOM node that is the target of the event
17765      * @param {Event} e The event
17766      * @return {Object} handle The custom handle data
17767      */
17768         getHandleFromEvent : function(e){
17769             var t = Roo.lib.Event.getTarget(e);
17770             return t ? handles[t.id] : null;
17771         },
17772
17773     /**
17774      * Returns a custom data object that is registered for a DOM node by id
17775      * @param {String|HTMLElement} id The DOM node or id to look up
17776      * @return {Object} data The custom data
17777      */
17778         getTarget : function(id){
17779             if(typeof id != "string"){ // must be element?
17780                 id = id.id;
17781             }
17782             return elements[id];
17783         },
17784
17785     /**
17786      * Returns a custom data object that is registered for the DOM node that is the target of the event
17787      * @param {Event} e The event
17788      * @return {Object} data The custom data
17789      */
17790         getTargetFromEvent : function(e){
17791             var t = Roo.lib.Event.getTarget(e);
17792             return t ? elements[t.id] || handles[t.id] : null;
17793         }
17794     };
17795 }();/*
17796  * Based on:
17797  * Ext JS Library 1.1.1
17798  * Copyright(c) 2006-2007, Ext JS, LLC.
17799  *
17800  * Originally Released Under LGPL - original licence link has changed is not relivant.
17801  *
17802  * Fork - LGPL
17803  * <script type="text/javascript">
17804  */
17805  
17806
17807 /**
17808  * @class Roo.dd.StatusProxy
17809  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17810  * default drag proxy used by all Roo.dd components.
17811  * @constructor
17812  * @param {Object} config
17813  */
17814 Roo.dd.StatusProxy = function(config){
17815     Roo.apply(this, config);
17816     this.id = this.id || Roo.id();
17817     this.el = new Roo.Layer({
17818         dh: {
17819             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17820                 {tag: "div", cls: "x-dd-drop-icon"},
17821                 {tag: "div", cls: "x-dd-drag-ghost"}
17822             ]
17823         }, 
17824         shadow: !config || config.shadow !== false
17825     });
17826     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17827     this.dropStatus = this.dropNotAllowed;
17828 };
17829
17830 Roo.dd.StatusProxy.prototype = {
17831     /**
17832      * @cfg {String} dropAllowed
17833      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17834      */
17835     dropAllowed : "x-dd-drop-ok",
17836     /**
17837      * @cfg {String} dropNotAllowed
17838      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17839      */
17840     dropNotAllowed : "x-dd-drop-nodrop",
17841
17842     /**
17843      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17844      * over the current target element.
17845      * @param {String} cssClass The css class for the new drop status indicator image
17846      */
17847     setStatus : function(cssClass){
17848         cssClass = cssClass || this.dropNotAllowed;
17849         if(this.dropStatus != cssClass){
17850             this.el.replaceClass(this.dropStatus, cssClass);
17851             this.dropStatus = cssClass;
17852         }
17853     },
17854
17855     /**
17856      * Resets the status indicator to the default dropNotAllowed value
17857      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17858      */
17859     reset : function(clearGhost){
17860         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17861         this.dropStatus = this.dropNotAllowed;
17862         if(clearGhost){
17863             this.ghost.update("");
17864         }
17865     },
17866
17867     /**
17868      * Updates the contents of the ghost element
17869      * @param {String} html The html that will replace the current innerHTML of the ghost element
17870      */
17871     update : function(html){
17872         if(typeof html == "string"){
17873             this.ghost.update(html);
17874         }else{
17875             this.ghost.update("");
17876             html.style.margin = "0";
17877             this.ghost.dom.appendChild(html);
17878         }
17879         // ensure float = none set?? cant remember why though.
17880         var el = this.ghost.dom.firstChild;
17881                 if(el){
17882                         Roo.fly(el).setStyle('float', 'none');
17883                 }
17884     },
17885     
17886     /**
17887      * Returns the underlying proxy {@link Roo.Layer}
17888      * @return {Roo.Layer} el
17889     */
17890     getEl : function(){
17891         return this.el;
17892     },
17893
17894     /**
17895      * Returns the ghost element
17896      * @return {Roo.Element} el
17897      */
17898     getGhost : function(){
17899         return this.ghost;
17900     },
17901
17902     /**
17903      * Hides the proxy
17904      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17905      */
17906     hide : function(clear){
17907         this.el.hide();
17908         if(clear){
17909             this.reset(true);
17910         }
17911     },
17912
17913     /**
17914      * Stops the repair animation if it's currently running
17915      */
17916     stop : function(){
17917         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17918             this.anim.stop();
17919         }
17920     },
17921
17922     /**
17923      * Displays this proxy
17924      */
17925     show : function(){
17926         this.el.show();
17927     },
17928
17929     /**
17930      * Force the Layer to sync its shadow and shim positions to the element
17931      */
17932     sync : function(){
17933         this.el.sync();
17934     },
17935
17936     /**
17937      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17938      * invalid drop operation by the item being dragged.
17939      * @param {Array} xy The XY position of the element ([x, y])
17940      * @param {Function} callback The function to call after the repair is complete
17941      * @param {Object} scope The scope in which to execute the callback
17942      */
17943     repair : function(xy, callback, scope){
17944         this.callback = callback;
17945         this.scope = scope;
17946         if(xy && this.animRepair !== false){
17947             this.el.addClass("x-dd-drag-repair");
17948             this.el.hideUnders(true);
17949             this.anim = this.el.shift({
17950                 duration: this.repairDuration || .5,
17951                 easing: 'easeOut',
17952                 xy: xy,
17953                 stopFx: true,
17954                 callback: this.afterRepair,
17955                 scope: this
17956             });
17957         }else{
17958             this.afterRepair();
17959         }
17960     },
17961
17962     // private
17963     afterRepair : function(){
17964         this.hide(true);
17965         if(typeof this.callback == "function"){
17966             this.callback.call(this.scope || this);
17967         }
17968         this.callback = null;
17969         this.scope = null;
17970     }
17971 };/*
17972  * Based on:
17973  * Ext JS Library 1.1.1
17974  * Copyright(c) 2006-2007, Ext JS, LLC.
17975  *
17976  * Originally Released Under LGPL - original licence link has changed is not relivant.
17977  *
17978  * Fork - LGPL
17979  * <script type="text/javascript">
17980  */
17981
17982 /**
17983  * @class Roo.dd.DragSource
17984  * @extends Roo.dd.DDProxy
17985  * A simple class that provides the basic implementation needed to make any element draggable.
17986  * @constructor
17987  * @param {String/HTMLElement/Element} el The container element
17988  * @param {Object} config
17989  */
17990 Roo.dd.DragSource = function(el, config){
17991     this.el = Roo.get(el);
17992     this.dragData = {};
17993     
17994     Roo.apply(this, config);
17995     
17996     if(!this.proxy){
17997         this.proxy = new Roo.dd.StatusProxy();
17998     }
17999
18000     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18001           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18002     
18003     this.dragging = false;
18004 };
18005
18006 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18007     /**
18008      * @cfg {String} dropAllowed
18009      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18010      */
18011     dropAllowed : "x-dd-drop-ok",
18012     /**
18013      * @cfg {String} dropNotAllowed
18014      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18015      */
18016     dropNotAllowed : "x-dd-drop-nodrop",
18017
18018     /**
18019      * Returns the data object associated with this drag source
18020      * @return {Object} data An object containing arbitrary data
18021      */
18022     getDragData : function(e){
18023         return this.dragData;
18024     },
18025
18026     // private
18027     onDragEnter : function(e, id){
18028         var target = Roo.dd.DragDropMgr.getDDById(id);
18029         this.cachedTarget = target;
18030         if(this.beforeDragEnter(target, e, id) !== false){
18031             if(target.isNotifyTarget){
18032                 var status = target.notifyEnter(this, e, this.dragData);
18033                 this.proxy.setStatus(status);
18034             }else{
18035                 this.proxy.setStatus(this.dropAllowed);
18036             }
18037             
18038             if(this.afterDragEnter){
18039                 /**
18040                  * An empty function by default, but provided so that you can perform a custom action
18041                  * when the dragged item enters the drop target by providing an implementation.
18042                  * @param {Roo.dd.DragDrop} target The drop target
18043                  * @param {Event} e The event object
18044                  * @param {String} id The id of the dragged element
18045                  * @method afterDragEnter
18046                  */
18047                 this.afterDragEnter(target, e, id);
18048             }
18049         }
18050     },
18051
18052     /**
18053      * An empty function by default, but provided so that you can perform a custom action
18054      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18055      * @param {Roo.dd.DragDrop} target The drop target
18056      * @param {Event} e The event object
18057      * @param {String} id The id of the dragged element
18058      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18059      */
18060     beforeDragEnter : function(target, e, id){
18061         return true;
18062     },
18063
18064     // private
18065     alignElWithMouse: function() {
18066         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18067         this.proxy.sync();
18068     },
18069
18070     // private
18071     onDragOver : function(e, id){
18072         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18073         if(this.beforeDragOver(target, e, id) !== false){
18074             if(target.isNotifyTarget){
18075                 var status = target.notifyOver(this, e, this.dragData);
18076                 this.proxy.setStatus(status);
18077             }
18078
18079             if(this.afterDragOver){
18080                 /**
18081                  * An empty function by default, but provided so that you can perform a custom action
18082                  * while the dragged item is over the drop target by providing an implementation.
18083                  * @param {Roo.dd.DragDrop} target The drop target
18084                  * @param {Event} e The event object
18085                  * @param {String} id The id of the dragged element
18086                  * @method afterDragOver
18087                  */
18088                 this.afterDragOver(target, e, id);
18089             }
18090         }
18091     },
18092
18093     /**
18094      * An empty function by default, but provided so that you can perform a custom action
18095      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18096      * @param {Roo.dd.DragDrop} target The drop target
18097      * @param {Event} e The event object
18098      * @param {String} id The id of the dragged element
18099      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18100      */
18101     beforeDragOver : function(target, e, id){
18102         return true;
18103     },
18104
18105     // private
18106     onDragOut : function(e, id){
18107         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18108         if(this.beforeDragOut(target, e, id) !== false){
18109             if(target.isNotifyTarget){
18110                 target.notifyOut(this, e, this.dragData);
18111             }
18112             this.proxy.reset();
18113             if(this.afterDragOut){
18114                 /**
18115                  * An empty function by default, but provided so that you can perform a custom action
18116                  * after the dragged item is dragged out of the target without dropping.
18117                  * @param {Roo.dd.DragDrop} target The drop target
18118                  * @param {Event} e The event object
18119                  * @param {String} id The id of the dragged element
18120                  * @method afterDragOut
18121                  */
18122                 this.afterDragOut(target, e, id);
18123             }
18124         }
18125         this.cachedTarget = null;
18126     },
18127
18128     /**
18129      * An empty function by default, but provided so that you can perform a custom action before the dragged
18130      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18131      * @param {Roo.dd.DragDrop} target The drop target
18132      * @param {Event} e The event object
18133      * @param {String} id The id of the dragged element
18134      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18135      */
18136     beforeDragOut : function(target, e, id){
18137         return true;
18138     },
18139     
18140     // private
18141     onDragDrop : function(e, id){
18142         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18143         if(this.beforeDragDrop(target, e, id) !== false){
18144             if(target.isNotifyTarget){
18145                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18146                     this.onValidDrop(target, e, id);
18147                 }else{
18148                     this.onInvalidDrop(target, e, id);
18149                 }
18150             }else{
18151                 this.onValidDrop(target, e, id);
18152             }
18153             
18154             if(this.afterDragDrop){
18155                 /**
18156                  * An empty function by default, but provided so that you can perform a custom action
18157                  * after a valid drag drop has occurred by providing an implementation.
18158                  * @param {Roo.dd.DragDrop} target The drop target
18159                  * @param {Event} e The event object
18160                  * @param {String} id The id of the dropped element
18161                  * @method afterDragDrop
18162                  */
18163                 this.afterDragDrop(target, e, id);
18164             }
18165         }
18166         delete this.cachedTarget;
18167     },
18168
18169     /**
18170      * An empty function by default, but provided so that you can perform a custom action before the dragged
18171      * item is dropped onto the target and optionally cancel the onDragDrop.
18172      * @param {Roo.dd.DragDrop} target The drop target
18173      * @param {Event} e The event object
18174      * @param {String} id The id of the dragged element
18175      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18176      */
18177     beforeDragDrop : function(target, e, id){
18178         return true;
18179     },
18180
18181     // private
18182     onValidDrop : function(target, e, id){
18183         this.hideProxy();
18184         if(this.afterValidDrop){
18185             /**
18186              * An empty function by default, but provided so that you can perform a custom action
18187              * after a valid drop has occurred by providing an implementation.
18188              * @param {Object} target The target DD 
18189              * @param {Event} e The event object
18190              * @param {String} id The id of the dropped element
18191              * @method afterInvalidDrop
18192              */
18193             this.afterValidDrop(target, e, id);
18194         }
18195     },
18196
18197     // private
18198     getRepairXY : function(e, data){
18199         return this.el.getXY();  
18200     },
18201
18202     // private
18203     onInvalidDrop : function(target, e, id){
18204         this.beforeInvalidDrop(target, e, id);
18205         if(this.cachedTarget){
18206             if(this.cachedTarget.isNotifyTarget){
18207                 this.cachedTarget.notifyOut(this, e, this.dragData);
18208             }
18209             this.cacheTarget = null;
18210         }
18211         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18212
18213         if(this.afterInvalidDrop){
18214             /**
18215              * An empty function by default, but provided so that you can perform a custom action
18216              * after an invalid drop has occurred by providing an implementation.
18217              * @param {Event} e The event object
18218              * @param {String} id The id of the dropped element
18219              * @method afterInvalidDrop
18220              */
18221             this.afterInvalidDrop(e, id);
18222         }
18223     },
18224
18225     // private
18226     afterRepair : function(){
18227         if(Roo.enableFx){
18228             this.el.highlight(this.hlColor || "c3daf9");
18229         }
18230         this.dragging = false;
18231     },
18232
18233     /**
18234      * An empty function by default, but provided so that you can perform a custom action after an invalid
18235      * drop has occurred.
18236      * @param {Roo.dd.DragDrop} target The drop target
18237      * @param {Event} e The event object
18238      * @param {String} id The id of the dragged element
18239      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18240      */
18241     beforeInvalidDrop : function(target, e, id){
18242         return true;
18243     },
18244
18245     // private
18246     handleMouseDown : function(e){
18247         if(this.dragging) {
18248             return;
18249         }
18250         var data = this.getDragData(e);
18251         if(data && this.onBeforeDrag(data, e) !== false){
18252             this.dragData = data;
18253             this.proxy.stop();
18254             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18255         } 
18256     },
18257
18258     /**
18259      * An empty function by default, but provided so that you can perform a custom action before the initial
18260      * drag event begins and optionally cancel it.
18261      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18262      * @param {Event} e The event object
18263      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18264      */
18265     onBeforeDrag : function(data, e){
18266         return true;
18267     },
18268
18269     /**
18270      * An empty function by default, but provided so that you can perform a custom action once the initial
18271      * drag event has begun.  The drag cannot be canceled from this function.
18272      * @param {Number} x The x position of the click on the dragged object
18273      * @param {Number} y The y position of the click on the dragged object
18274      */
18275     onStartDrag : Roo.emptyFn,
18276
18277     // private - YUI override
18278     startDrag : function(x, y){
18279         this.proxy.reset();
18280         this.dragging = true;
18281         this.proxy.update("");
18282         this.onInitDrag(x, y);
18283         this.proxy.show();
18284     },
18285
18286     // private
18287     onInitDrag : function(x, y){
18288         var clone = this.el.dom.cloneNode(true);
18289         clone.id = Roo.id(); // prevent duplicate ids
18290         this.proxy.update(clone);
18291         this.onStartDrag(x, y);
18292         return true;
18293     },
18294
18295     /**
18296      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18297      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18298      */
18299     getProxy : function(){
18300         return this.proxy;  
18301     },
18302
18303     /**
18304      * Hides the drag source's {@link Roo.dd.StatusProxy}
18305      */
18306     hideProxy : function(){
18307         this.proxy.hide();  
18308         this.proxy.reset(true);
18309         this.dragging = false;
18310     },
18311
18312     // private
18313     triggerCacheRefresh : function(){
18314         Roo.dd.DDM.refreshCache(this.groups);
18315     },
18316
18317     // private - override to prevent hiding
18318     b4EndDrag: function(e) {
18319     },
18320
18321     // private - override to prevent moving
18322     endDrag : function(e){
18323         this.onEndDrag(this.dragData, e);
18324     },
18325
18326     // private
18327     onEndDrag : function(data, e){
18328     },
18329     
18330     // private - pin to cursor
18331     autoOffset : function(x, y) {
18332         this.setDelta(-12, -20);
18333     }    
18334 });/*
18335  * Based on:
18336  * Ext JS Library 1.1.1
18337  * Copyright(c) 2006-2007, Ext JS, LLC.
18338  *
18339  * Originally Released Under LGPL - original licence link has changed is not relivant.
18340  *
18341  * Fork - LGPL
18342  * <script type="text/javascript">
18343  */
18344
18345
18346 /**
18347  * @class Roo.dd.DropTarget
18348  * @extends Roo.dd.DDTarget
18349  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18350  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18351  * @constructor
18352  * @param {String/HTMLElement/Element} el The container element
18353  * @param {Object} config
18354  */
18355 Roo.dd.DropTarget = function(el, config){
18356     this.el = Roo.get(el);
18357     
18358     var listeners = false; ;
18359     if (config && config.listeners) {
18360         listeners= config.listeners;
18361         delete config.listeners;
18362     }
18363     Roo.apply(this, config);
18364     
18365     if(this.containerScroll){
18366         Roo.dd.ScrollManager.register(this.el);
18367     }
18368     this.addEvents( {
18369          /**
18370          * @scope Roo.dd.DropTarget
18371          */
18372          
18373          /**
18374          * @event enter
18375          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18376          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18377          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18378          * 
18379          * IMPORTANT : it should set this.overClass and this.dropAllowed
18380          * 
18381          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18382          * @param {Event} e The event
18383          * @param {Object} data An object containing arbitrary data supplied by the drag source
18384          */
18385         "enter" : true,
18386         
18387          /**
18388          * @event over
18389          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18390          * This method will be called on every mouse movement while the drag source is over the drop target.
18391          * This default implementation simply returns the dropAllowed config value.
18392          * 
18393          * IMPORTANT : it should set this.dropAllowed
18394          * 
18395          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18396          * @param {Event} e The event
18397          * @param {Object} data An object containing arbitrary data supplied by the drag source
18398          
18399          */
18400         "over" : true,
18401         /**
18402          * @event out
18403          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18404          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18405          * overClass (if any) from the drop element.
18406          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18407          * @param {Event} e The event
18408          * @param {Object} data An object containing arbitrary data supplied by the drag source
18409          */
18410          "out" : true,
18411          
18412         /**
18413          * @event drop
18414          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18415          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18416          * implementation that does something to process the drop event and returns true so that the drag source's
18417          * repair action does not run.
18418          * 
18419          * IMPORTANT : it should set this.success
18420          * 
18421          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18422          * @param {Event} e The event
18423          * @param {Object} data An object containing arbitrary data supplied by the drag source
18424         */
18425          "drop" : true
18426     });
18427             
18428      
18429     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18430         this.el.dom, 
18431         this.ddGroup || this.group,
18432         {
18433             isTarget: true,
18434             listeners : listeners || {} 
18435            
18436         
18437         }
18438     );
18439
18440 };
18441
18442 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18443     /**
18444      * @cfg {String} overClass
18445      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18446      */
18447      /**
18448      * @cfg {String} ddGroup
18449      * The drag drop group to handle drop events for
18450      */
18451      
18452     /**
18453      * @cfg {String} dropAllowed
18454      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18455      */
18456     dropAllowed : "x-dd-drop-ok",
18457     /**
18458      * @cfg {String} dropNotAllowed
18459      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18460      */
18461     dropNotAllowed : "x-dd-drop-nodrop",
18462     /**
18463      * @cfg {boolean} success
18464      * set this after drop listener.. 
18465      */
18466     success : false,
18467     /**
18468      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18469      * if the drop point is valid for over/enter..
18470      */
18471     valid : false,
18472     // private
18473     isTarget : true,
18474
18475     // private
18476     isNotifyTarget : true,
18477     
18478     /**
18479      * @hide
18480      */
18481     notifyEnter : function(dd, e, data)
18482     {
18483         this.valid = true;
18484         this.fireEvent('enter', dd, e, data);
18485         if(this.overClass){
18486             this.el.addClass(this.overClass);
18487         }
18488         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18489             this.valid ? this.dropAllowed : this.dropNotAllowed
18490         );
18491     },
18492
18493     /**
18494      * @hide
18495      */
18496     notifyOver : function(dd, e, data)
18497     {
18498         this.valid = true;
18499         this.fireEvent('over', dd, e, data);
18500         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18501             this.valid ? this.dropAllowed : this.dropNotAllowed
18502         );
18503     },
18504
18505     /**
18506      * @hide
18507      */
18508     notifyOut : function(dd, e, data)
18509     {
18510         this.fireEvent('out', dd, e, data);
18511         if(this.overClass){
18512             this.el.removeClass(this.overClass);
18513         }
18514     },
18515
18516     /**
18517      * @hide
18518      */
18519     notifyDrop : function(dd, e, data)
18520     {
18521         this.success = false;
18522         this.fireEvent('drop', dd, e, data);
18523         return this.success;
18524     }
18525 });/*
18526  * Based on:
18527  * Ext JS Library 1.1.1
18528  * Copyright(c) 2006-2007, Ext JS, LLC.
18529  *
18530  * Originally Released Under LGPL - original licence link has changed is not relivant.
18531  *
18532  * Fork - LGPL
18533  * <script type="text/javascript">
18534  */
18535
18536
18537 /**
18538  * @class Roo.dd.DragZone
18539  * @extends Roo.dd.DragSource
18540  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18541  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18542  * @constructor
18543  * @param {String/HTMLElement/Element} el The container element
18544  * @param {Object} config
18545  */
18546 Roo.dd.DragZone = function(el, config){
18547     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18548     if(this.containerScroll){
18549         Roo.dd.ScrollManager.register(this.el);
18550     }
18551 };
18552
18553 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18554     /**
18555      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18556      * for auto scrolling during drag operations.
18557      */
18558     /**
18559      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18560      * method after a failed drop (defaults to "c3daf9" - light blue)
18561      */
18562
18563     /**
18564      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18565      * for a valid target to drag based on the mouse down. Override this method
18566      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18567      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18568      * @param {EventObject} e The mouse down event
18569      * @return {Object} The dragData
18570      */
18571     getDragData : function(e){
18572         return Roo.dd.Registry.getHandleFromEvent(e);
18573     },
18574     
18575     /**
18576      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18577      * this.dragData.ddel
18578      * @param {Number} x The x position of the click on the dragged object
18579      * @param {Number} y The y position of the click on the dragged object
18580      * @return {Boolean} true to continue the drag, false to cancel
18581      */
18582     onInitDrag : function(x, y){
18583         this.proxy.update(this.dragData.ddel.cloneNode(true));
18584         this.onStartDrag(x, y);
18585         return true;
18586     },
18587     
18588     /**
18589      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18590      */
18591     afterRepair : function(){
18592         if(Roo.enableFx){
18593             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18594         }
18595         this.dragging = false;
18596     },
18597
18598     /**
18599      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18600      * the XY of this.dragData.ddel
18601      * @param {EventObject} e The mouse up event
18602      * @return {Array} The xy location (e.g. [100, 200])
18603      */
18604     getRepairXY : function(e){
18605         return Roo.Element.fly(this.dragData.ddel).getXY();  
18606     }
18607 });/*
18608  * Based on:
18609  * Ext JS Library 1.1.1
18610  * Copyright(c) 2006-2007, Ext JS, LLC.
18611  *
18612  * Originally Released Under LGPL - original licence link has changed is not relivant.
18613  *
18614  * Fork - LGPL
18615  * <script type="text/javascript">
18616  */
18617 /**
18618  * @class Roo.dd.DropZone
18619  * @extends Roo.dd.DropTarget
18620  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18621  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18622  * @constructor
18623  * @param {String/HTMLElement/Element} el The container element
18624  * @param {Object} config
18625  */
18626 Roo.dd.DropZone = function(el, config){
18627     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18628 };
18629
18630 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18631     /**
18632      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18633      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18634      * provide your own custom lookup.
18635      * @param {Event} e The event
18636      * @return {Object} data The custom data
18637      */
18638     getTargetFromEvent : function(e){
18639         return Roo.dd.Registry.getTargetFromEvent(e);
18640     },
18641
18642     /**
18643      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18644      * that it has registered.  This method has no default implementation and should be overridden to provide
18645      * node-specific processing if necessary.
18646      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18647      * {@link #getTargetFromEvent} for this node)
18648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18649      * @param {Event} e The event
18650      * @param {Object} data An object containing arbitrary data supplied by the drag source
18651      */
18652     onNodeEnter : function(n, dd, e, data){
18653         
18654     },
18655
18656     /**
18657      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18658      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18659      * overridden to provide the proper feedback.
18660      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18661      * {@link #getTargetFromEvent} for this node)
18662      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18663      * @param {Event} e The event
18664      * @param {Object} data An object containing arbitrary data supplied by the drag source
18665      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18666      * underlying {@link Roo.dd.StatusProxy} can be updated
18667      */
18668     onNodeOver : function(n, dd, e, data){
18669         return this.dropAllowed;
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18674      * the drop node without dropping.  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     onNodeOut : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18688      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18689      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
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 {Boolean} True if the drop was valid, else false
18696      */
18697     onNodeDrop : function(n, dd, e, data){
18698         return false;
18699     },
18700
18701     /**
18702      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18703      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18704      * it should be overridden to provide the proper feedback if necessary.
18705      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18706      * @param {Event} e The event
18707      * @param {Object} data An object containing arbitrary data supplied by the drag source
18708      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18709      * underlying {@link Roo.dd.StatusProxy} can be updated
18710      */
18711     onContainerOver : function(dd, e, data){
18712         return this.dropNotAllowed;
18713     },
18714
18715     /**
18716      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18717      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18718      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18719      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18720      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18721      * @param {Event} e The event
18722      * @param {Object} data An object containing arbitrary data supplied by the drag source
18723      * @return {Boolean} True if the drop was valid, else false
18724      */
18725     onContainerDrop : function(dd, e, data){
18726         return false;
18727     },
18728
18729     /**
18730      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18731      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18732      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18733      * you should override this method and provide a custom implementation.
18734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18735      * @param {Event} e The event
18736      * @param {Object} data An object containing arbitrary data supplied by the drag source
18737      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18738      * underlying {@link Roo.dd.StatusProxy} can be updated
18739      */
18740     notifyEnter : function(dd, e, data){
18741         return this.dropNotAllowed;
18742     },
18743
18744     /**
18745      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18746      * This method will be called on every mouse movement while the drag source is over the drop zone.
18747      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18748      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18749      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18750      * registered node, it will call {@link #onContainerOver}.
18751      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18752      * @param {Event} e The event
18753      * @param {Object} data An object containing arbitrary data supplied by the drag source
18754      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18755      * underlying {@link Roo.dd.StatusProxy} can be updated
18756      */
18757     notifyOver : function(dd, e, data){
18758         var n = this.getTargetFromEvent(e);
18759         if(!n){ // not over valid drop target
18760             if(this.lastOverNode){
18761                 this.onNodeOut(this.lastOverNode, dd, e, data);
18762                 this.lastOverNode = null;
18763             }
18764             return this.onContainerOver(dd, e, data);
18765         }
18766         if(this.lastOverNode != n){
18767             if(this.lastOverNode){
18768                 this.onNodeOut(this.lastOverNode, dd, e, data);
18769             }
18770             this.onNodeEnter(n, dd, e, data);
18771             this.lastOverNode = n;
18772         }
18773         return this.onNodeOver(n, dd, e, data);
18774     },
18775
18776     /**
18777      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18778      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18779      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18780      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18781      * @param {Event} e The event
18782      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18783      */
18784     notifyOut : function(dd, e, data){
18785         if(this.lastOverNode){
18786             this.onNodeOut(this.lastOverNode, dd, e, data);
18787             this.lastOverNode = null;
18788         }
18789     },
18790
18791     /**
18792      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18793      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18794      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18795      * otherwise it will call {@link #onContainerDrop}.
18796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18797      * @param {Event} e The event
18798      * @param {Object} data An object containing arbitrary data supplied by the drag source
18799      * @return {Boolean} True if the drop was valid, else false
18800      */
18801     notifyDrop : function(dd, e, data){
18802         if(this.lastOverNode){
18803             this.onNodeOut(this.lastOverNode, dd, e, data);
18804             this.lastOverNode = null;
18805         }
18806         var n = this.getTargetFromEvent(e);
18807         return n ?
18808             this.onNodeDrop(n, dd, e, data) :
18809             this.onContainerDrop(dd, e, data);
18810     },
18811
18812     // private
18813     triggerCacheRefresh : function(){
18814         Roo.dd.DDM.refreshCache(this.groups);
18815     }  
18816 });/*
18817  * Based on:
18818  * Ext JS Library 1.1.1
18819  * Copyright(c) 2006-2007, Ext JS, LLC.
18820  *
18821  * Originally Released Under LGPL - original licence link has changed is not relivant.
18822  *
18823  * Fork - LGPL
18824  * <script type="text/javascript">
18825  */
18826
18827
18828 /**
18829  * @class Roo.data.SortTypes
18830  * @singleton
18831  * Defines the default sorting (casting?) comparison functions used when sorting data.
18832  */
18833 Roo.data.SortTypes = {
18834     /**
18835      * Default sort that does nothing
18836      * @param {Mixed} s The value being converted
18837      * @return {Mixed} The comparison value
18838      */
18839     none : function(s){
18840         return s;
18841     },
18842     
18843     /**
18844      * The regular expression used to strip tags
18845      * @type {RegExp}
18846      * @property
18847      */
18848     stripTagsRE : /<\/?[^>]+>/gi,
18849     
18850     /**
18851      * Strips all HTML tags to sort on text only
18852      * @param {Mixed} s The value being converted
18853      * @return {String} The comparison value
18854      */
18855     asText : function(s){
18856         return String(s).replace(this.stripTagsRE, "");
18857     },
18858     
18859     /**
18860      * Strips all HTML tags to sort on text only - Case insensitive
18861      * @param {Mixed} s The value being converted
18862      * @return {String} The comparison value
18863      */
18864     asUCText : function(s){
18865         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18866     },
18867     
18868     /**
18869      * Case insensitive string
18870      * @param {Mixed} s The value being converted
18871      * @return {String} The comparison value
18872      */
18873     asUCString : function(s) {
18874         return String(s).toUpperCase();
18875     },
18876     
18877     /**
18878      * Date sorting
18879      * @param {Mixed} s The value being converted
18880      * @return {Number} The comparison value
18881      */
18882     asDate : function(s) {
18883         if(!s){
18884             return 0;
18885         }
18886         if(s instanceof Date){
18887             return s.getTime();
18888         }
18889         return Date.parse(String(s));
18890     },
18891     
18892     /**
18893      * Float sorting
18894      * @param {Mixed} s The value being converted
18895      * @return {Float} The comparison value
18896      */
18897     asFloat : function(s) {
18898         var val = parseFloat(String(s).replace(/,/g, ""));
18899         if(isNaN(val)) val = 0;
18900         return val;
18901     },
18902     
18903     /**
18904      * Integer sorting
18905      * @param {Mixed} s The value being converted
18906      * @return {Number} The comparison value
18907      */
18908     asInt : function(s) {
18909         var val = parseInt(String(s).replace(/,/g, ""));
18910         if(isNaN(val)) val = 0;
18911         return val;
18912     }
18913 };/*
18914  * Based on:
18915  * Ext JS Library 1.1.1
18916  * Copyright(c) 2006-2007, Ext JS, LLC.
18917  *
18918  * Originally Released Under LGPL - original licence link has changed is not relivant.
18919  *
18920  * Fork - LGPL
18921  * <script type="text/javascript">
18922  */
18923
18924 /**
18925 * @class Roo.data.Record
18926  * Instances of this class encapsulate both record <em>definition</em> information, and record
18927  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18928  * to access Records cached in an {@link Roo.data.Store} object.<br>
18929  * <p>
18930  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18931  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18932  * objects.<br>
18933  * <p>
18934  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18935  * @constructor
18936  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18937  * {@link #create}. The parameters are the same.
18938  * @param {Array} data An associative Array of data values keyed by the field name.
18939  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18940  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18941  * not specified an integer id is generated.
18942  */
18943 Roo.data.Record = function(data, id){
18944     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18945     this.data = data;
18946 };
18947
18948 /**
18949  * Generate a constructor for a specific record layout.
18950  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18951  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18952  * Each field definition object may contain the following properties: <ul>
18953  * <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,
18954  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18955  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18956  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18957  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18958  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18959  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18960  * this may be omitted.</p></li>
18961  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18962  * <ul><li>auto (Default, implies no conversion)</li>
18963  * <li>string</li>
18964  * <li>int</li>
18965  * <li>float</li>
18966  * <li>boolean</li>
18967  * <li>date</li></ul></p></li>
18968  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18969  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18970  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18971  * by the Reader into an object that will be stored in the Record. It is passed the
18972  * following parameters:<ul>
18973  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18974  * </ul></p></li>
18975  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18976  * </ul>
18977  * <br>usage:<br><pre><code>
18978 var TopicRecord = Roo.data.Record.create(
18979     {name: 'title', mapping: 'topic_title'},
18980     {name: 'author', mapping: 'username'},
18981     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18982     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18983     {name: 'lastPoster', mapping: 'user2'},
18984     {name: 'excerpt', mapping: 'post_text'}
18985 );
18986
18987 var myNewRecord = new TopicRecord({
18988     title: 'Do my job please',
18989     author: 'noobie',
18990     totalPosts: 1,
18991     lastPost: new Date(),
18992     lastPoster: 'Animal',
18993     excerpt: 'No way dude!'
18994 });
18995 myStore.add(myNewRecord);
18996 </code></pre>
18997  * @method create
18998  * @static
18999  */
19000 Roo.data.Record.create = function(o){
19001     var f = function(){
19002         f.superclass.constructor.apply(this, arguments);
19003     };
19004     Roo.extend(f, Roo.data.Record);
19005     var p = f.prototype;
19006     p.fields = new Roo.util.MixedCollection(false, function(field){
19007         return field.name;
19008     });
19009     for(var i = 0, len = o.length; i < len; i++){
19010         p.fields.add(new Roo.data.Field(o[i]));
19011     }
19012     f.getField = function(name){
19013         return p.fields.get(name);  
19014     };
19015     return f;
19016 };
19017
19018 Roo.data.Record.AUTO_ID = 1000;
19019 Roo.data.Record.EDIT = 'edit';
19020 Roo.data.Record.REJECT = 'reject';
19021 Roo.data.Record.COMMIT = 'commit';
19022
19023 Roo.data.Record.prototype = {
19024     /**
19025      * Readonly flag - true if this record has been modified.
19026      * @type Boolean
19027      */
19028     dirty : false,
19029     editing : false,
19030     error: null,
19031     modified: null,
19032
19033     // private
19034     join : function(store){
19035         this.store = store;
19036     },
19037
19038     /**
19039      * Set the named field to the specified value.
19040      * @param {String} name The name of the field to set.
19041      * @param {Object} value The value to set the field to.
19042      */
19043     set : function(name, value){
19044         if(this.data[name] == value){
19045             return;
19046         }
19047         this.dirty = true;
19048         if(!this.modified){
19049             this.modified = {};
19050         }
19051         if(typeof this.modified[name] == 'undefined'){
19052             this.modified[name] = this.data[name];
19053         }
19054         this.data[name] = value;
19055         if(!this.editing && this.store){
19056             this.store.afterEdit(this);
19057         }       
19058     },
19059
19060     /**
19061      * Get the value of the named field.
19062      * @param {String} name The name of the field to get the value of.
19063      * @return {Object} The value of the field.
19064      */
19065     get : function(name){
19066         return this.data[name]; 
19067     },
19068
19069     // private
19070     beginEdit : function(){
19071         this.editing = true;
19072         this.modified = {}; 
19073     },
19074
19075     // private
19076     cancelEdit : function(){
19077         this.editing = false;
19078         delete this.modified;
19079     },
19080
19081     // private
19082     endEdit : function(){
19083         this.editing = false;
19084         if(this.dirty && this.store){
19085             this.store.afterEdit(this);
19086         }
19087     },
19088
19089     /**
19090      * Usually called by the {@link Roo.data.Store} which owns the Record.
19091      * Rejects all changes made to the Record since either creation, or the last commit operation.
19092      * Modified fields are reverted to their original values.
19093      * <p>
19094      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19095      * of reject operations.
19096      */
19097     reject : function(){
19098         var m = this.modified;
19099         for(var n in m){
19100             if(typeof m[n] != "function"){
19101                 this.data[n] = m[n];
19102             }
19103         }
19104         this.dirty = false;
19105         delete this.modified;
19106         this.editing = false;
19107         if(this.store){
19108             this.store.afterReject(this);
19109         }
19110     },
19111
19112     /**
19113      * Usually called by the {@link Roo.data.Store} which owns the Record.
19114      * Commits all changes made to the Record since either creation, or the last commit operation.
19115      * <p>
19116      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19117      * of commit operations.
19118      */
19119     commit : function(){
19120         this.dirty = false;
19121         delete this.modified;
19122         this.editing = false;
19123         if(this.store){
19124             this.store.afterCommit(this);
19125         }
19126     },
19127
19128     // private
19129     hasError : function(){
19130         return this.error != null;
19131     },
19132
19133     // private
19134     clearError : function(){
19135         this.error = null;
19136     },
19137
19138     /**
19139      * Creates a copy of this record.
19140      * @param {String} id (optional) A new record id if you don't want to use this record's id
19141      * @return {Record}
19142      */
19143     copy : function(newId) {
19144         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19145     }
19146 };/*
19147  * Based on:
19148  * Ext JS Library 1.1.1
19149  * Copyright(c) 2006-2007, Ext JS, LLC.
19150  *
19151  * Originally Released Under LGPL - original licence link has changed is not relivant.
19152  *
19153  * Fork - LGPL
19154  * <script type="text/javascript">
19155  */
19156
19157
19158
19159 /**
19160  * @class Roo.data.Store
19161  * @extends Roo.util.Observable
19162  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19163  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19164  * <p>
19165  * 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
19166  * has no knowledge of the format of the data returned by the Proxy.<br>
19167  * <p>
19168  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19169  * instances from the data object. These records are cached and made available through accessor functions.
19170  * @constructor
19171  * Creates a new Store.
19172  * @param {Object} config A config object containing the objects needed for the Store to access data,
19173  * and read the data into Records.
19174  */
19175 Roo.data.Store = function(config){
19176     this.data = new Roo.util.MixedCollection(false);
19177     this.data.getKey = function(o){
19178         return o.id;
19179     };
19180     this.baseParams = {};
19181     // private
19182     this.paramNames = {
19183         "start" : "start",
19184         "limit" : "limit",
19185         "sort" : "sort",
19186         "dir" : "dir",
19187         "multisort" : "_multisort"
19188     };
19189
19190     if(config && config.data){
19191         this.inlineData = config.data;
19192         delete config.data;
19193     }
19194
19195     Roo.apply(this, config);
19196     
19197     if(this.reader){ // reader passed
19198         this.reader = Roo.factory(this.reader, Roo.data);
19199         this.reader.xmodule = this.xmodule || false;
19200         if(!this.recordType){
19201             this.recordType = this.reader.recordType;
19202         }
19203         if(this.reader.onMetaChange){
19204             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19205         }
19206     }
19207
19208     if(this.recordType){
19209         this.fields = this.recordType.prototype.fields;
19210     }
19211     this.modified = [];
19212
19213     this.addEvents({
19214         /**
19215          * @event datachanged
19216          * Fires when the data cache has changed, and a widget which is using this Store
19217          * as a Record cache should refresh its view.
19218          * @param {Store} this
19219          */
19220         datachanged : true,
19221         /**
19222          * @event metachange
19223          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19224          * @param {Store} this
19225          * @param {Object} meta The JSON metadata
19226          */
19227         metachange : true,
19228         /**
19229          * @event add
19230          * Fires when Records have been added to the Store
19231          * @param {Store} this
19232          * @param {Roo.data.Record[]} records The array of Records added
19233          * @param {Number} index The index at which the record(s) were added
19234          */
19235         add : true,
19236         /**
19237          * @event remove
19238          * Fires when a Record has been removed from the Store
19239          * @param {Store} this
19240          * @param {Roo.data.Record} record The Record that was removed
19241          * @param {Number} index The index at which the record was removed
19242          */
19243         remove : true,
19244         /**
19245          * @event update
19246          * Fires when a Record has been updated
19247          * @param {Store} this
19248          * @param {Roo.data.Record} record The Record that was updated
19249          * @param {String} operation The update operation being performed.  Value may be one of:
19250          * <pre><code>
19251  Roo.data.Record.EDIT
19252  Roo.data.Record.REJECT
19253  Roo.data.Record.COMMIT
19254          * </code></pre>
19255          */
19256         update : true,
19257         /**
19258          * @event clear
19259          * Fires when the data cache has been cleared.
19260          * @param {Store} this
19261          */
19262         clear : true,
19263         /**
19264          * @event beforeload
19265          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19266          * the load action will be canceled.
19267          * @param {Store} this
19268          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19269          */
19270         beforeload : true,
19271         /**
19272          * @event load
19273          * Fires after a new set of Records has been loaded.
19274          * @param {Store} this
19275          * @param {Roo.data.Record[]} records The Records that were loaded
19276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19277          */
19278         load : true,
19279         /**
19280          * @event loadexception
19281          * Fires if an exception occurs in the Proxy during loading.
19282          * Called with the signature of the Proxy's "loadexception" event.
19283          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19284          * 
19285          * @param {Proxy} 
19286          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19287          * @param {Object} load options 
19288          * @param {Object} jsonData from your request (normally this contains the Exception)
19289          */
19290         loadexception : true
19291     });
19292     
19293     if(this.proxy){
19294         this.proxy = Roo.factory(this.proxy, Roo.data);
19295         this.proxy.xmodule = this.xmodule || false;
19296         this.relayEvents(this.proxy,  ["loadexception"]);
19297     }
19298     this.sortToggle = {};
19299     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19300
19301     Roo.data.Store.superclass.constructor.call(this);
19302
19303     if(this.inlineData){
19304         this.loadData(this.inlineData);
19305         delete this.inlineData;
19306     }
19307 };
19308 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19309      /**
19310     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19311     * without a remote query - used by combo/forms at present.
19312     */
19313     
19314     /**
19315     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19316     */
19317     /**
19318     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19319     */
19320     /**
19321     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19322     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19323     */
19324     /**
19325     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19326     * on any HTTP request
19327     */
19328     /**
19329     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19330     */
19331     /**
19332     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19333     */
19334     multiSort: false,
19335     /**
19336     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19337     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19338     */
19339     remoteSort : false,
19340
19341     /**
19342     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19343      * loaded or when a record is removed. (defaults to false).
19344     */
19345     pruneModifiedRecords : false,
19346
19347     // private
19348     lastOptions : null,
19349
19350     /**
19351      * Add Records to the Store and fires the add event.
19352      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19353      */
19354     add : function(records){
19355         records = [].concat(records);
19356         for(var i = 0, len = records.length; i < len; i++){
19357             records[i].join(this);
19358         }
19359         var index = this.data.length;
19360         this.data.addAll(records);
19361         this.fireEvent("add", this, records, index);
19362     },
19363
19364     /**
19365      * Remove a Record from the Store and fires the remove event.
19366      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19367      */
19368     remove : function(record){
19369         var index = this.data.indexOf(record);
19370         this.data.removeAt(index);
19371         if(this.pruneModifiedRecords){
19372             this.modified.remove(record);
19373         }
19374         this.fireEvent("remove", this, record, index);
19375     },
19376
19377     /**
19378      * Remove all Records from the Store and fires the clear event.
19379      */
19380     removeAll : function(){
19381         this.data.clear();
19382         if(this.pruneModifiedRecords){
19383             this.modified = [];
19384         }
19385         this.fireEvent("clear", this);
19386     },
19387
19388     /**
19389      * Inserts Records to the Store at the given index and fires the add event.
19390      * @param {Number} index The start index at which to insert the passed Records.
19391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19392      */
19393     insert : function(index, records){
19394         records = [].concat(records);
19395         for(var i = 0, len = records.length; i < len; i++){
19396             this.data.insert(index, records[i]);
19397             records[i].join(this);
19398         }
19399         this.fireEvent("add", this, records, index);
19400     },
19401
19402     /**
19403      * Get the index within the cache of the passed Record.
19404      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19405      * @return {Number} The index of the passed Record. Returns -1 if not found.
19406      */
19407     indexOf : function(record){
19408         return this.data.indexOf(record);
19409     },
19410
19411     /**
19412      * Get the index within the cache of the Record with the passed id.
19413      * @param {String} id The id of the Record to find.
19414      * @return {Number} The index of the Record. Returns -1 if not found.
19415      */
19416     indexOfId : function(id){
19417         return this.data.indexOfKey(id);
19418     },
19419
19420     /**
19421      * Get the Record with the specified id.
19422      * @param {String} id The id of the Record to find.
19423      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19424      */
19425     getById : function(id){
19426         return this.data.key(id);
19427     },
19428
19429     /**
19430      * Get the Record at the specified index.
19431      * @param {Number} index The index of the Record to find.
19432      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19433      */
19434     getAt : function(index){
19435         return this.data.itemAt(index);
19436     },
19437
19438     /**
19439      * Returns a range of Records between specified indices.
19440      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19441      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19442      * @return {Roo.data.Record[]} An array of Records
19443      */
19444     getRange : function(start, end){
19445         return this.data.getRange(start, end);
19446     },
19447
19448     // private
19449     storeOptions : function(o){
19450         o = Roo.apply({}, o);
19451         delete o.callback;
19452         delete o.scope;
19453         this.lastOptions = o;
19454     },
19455
19456     /**
19457      * Loads the Record cache from the configured Proxy using the configured Reader.
19458      * <p>
19459      * If using remote paging, then the first load call must specify the <em>start</em>
19460      * and <em>limit</em> properties in the options.params property to establish the initial
19461      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19462      * <p>
19463      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19464      * and this call will return before the new data has been loaded. Perform any post-processing
19465      * in a callback function, or in a "load" event handler.</strong>
19466      * <p>
19467      * @param {Object} options An object containing properties which control loading options:<ul>
19468      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19469      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19470      * passed the following arguments:<ul>
19471      * <li>r : Roo.data.Record[]</li>
19472      * <li>options: Options object from the load call</li>
19473      * <li>success: Boolean success indicator</li></ul></li>
19474      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19475      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19476      * </ul>
19477      */
19478     load : function(options){
19479         options = options || {};
19480         if(this.fireEvent("beforeload", this, options) !== false){
19481             this.storeOptions(options);
19482             var p = Roo.apply(options.params || {}, this.baseParams);
19483             // if meta was not loaded from remote source.. try requesting it.
19484             if (!this.reader.metaFromRemote) {
19485                 p._requestMeta = 1;
19486             }
19487             if(this.sortInfo && this.remoteSort){
19488                 var pn = this.paramNames;
19489                 p[pn["sort"]] = this.sortInfo.field;
19490                 p[pn["dir"]] = this.sortInfo.direction;
19491             }
19492             if (this.multiSort) {
19493                 var pn = this.paramNames;
19494                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19495             }
19496             
19497             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19498         }
19499     },
19500
19501     /**
19502      * Reloads the Record cache from the configured Proxy using the configured Reader and
19503      * the options from the last load operation performed.
19504      * @param {Object} options (optional) An object containing properties which may override the options
19505      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19506      * the most recently used options are reused).
19507      */
19508     reload : function(options){
19509         this.load(Roo.applyIf(options||{}, this.lastOptions));
19510     },
19511
19512     // private
19513     // Called as a callback by the Reader during a load operation.
19514     loadRecords : function(o, options, success){
19515         if(!o || success === false){
19516             if(success !== false){
19517                 this.fireEvent("load", this, [], options);
19518             }
19519             if(options.callback){
19520                 options.callback.call(options.scope || this, [], options, false);
19521             }
19522             return;
19523         }
19524         // if data returned failure - throw an exception.
19525         if (o.success === false) {
19526             // show a message if no listener is registered.
19527             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
19528                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
19529             }
19530             // loadmask wil be hooked into this..
19531             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19532             return;
19533         }
19534         var r = o.records, t = o.totalRecords || r.length;
19535         if(!options || options.add !== true){
19536             if(this.pruneModifiedRecords){
19537                 this.modified = [];
19538             }
19539             for(var i = 0, len = r.length; i < len; i++){
19540                 r[i].join(this);
19541             }
19542             if(this.snapshot){
19543                 this.data = this.snapshot;
19544                 delete this.snapshot;
19545             }
19546             this.data.clear();
19547             this.data.addAll(r);
19548             this.totalLength = t;
19549             this.applySort();
19550             this.fireEvent("datachanged", this);
19551         }else{
19552             this.totalLength = Math.max(t, this.data.length+r.length);
19553             this.add(r);
19554         }
19555         this.fireEvent("load", this, r, options);
19556         if(options.callback){
19557             options.callback.call(options.scope || this, r, options, true);
19558         }
19559     },
19560
19561
19562     /**
19563      * Loads data from a passed data block. A Reader which understands the format of the data
19564      * must have been configured in the constructor.
19565      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19566      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19567      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19568      */
19569     loadData : function(o, append){
19570         var r = this.reader.readRecords(o);
19571         this.loadRecords(r, {add: append}, true);
19572     },
19573
19574     /**
19575      * Gets the number of cached records.
19576      * <p>
19577      * <em>If using paging, this may not be the total size of the dataset. If the data object
19578      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19579      * the data set size</em>
19580      */
19581     getCount : function(){
19582         return this.data.length || 0;
19583     },
19584
19585     /**
19586      * Gets the total number of records in the dataset as returned by the server.
19587      * <p>
19588      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19589      * the dataset size</em>
19590      */
19591     getTotalCount : function(){
19592         return this.totalLength || 0;
19593     },
19594
19595     /**
19596      * Returns the sort state of the Store as an object with two properties:
19597      * <pre><code>
19598  field {String} The name of the field by which the Records are sorted
19599  direction {String} The sort order, "ASC" or "DESC"
19600      * </code></pre>
19601      */
19602     getSortState : function(){
19603         return this.sortInfo;
19604     },
19605
19606     // private
19607     applySort : function(){
19608         if(this.sortInfo && !this.remoteSort){
19609             var s = this.sortInfo, f = s.field;
19610             var st = this.fields.get(f).sortType;
19611             var fn = function(r1, r2){
19612                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19613                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19614             };
19615             this.data.sort(s.direction, fn);
19616             if(this.snapshot && this.snapshot != this.data){
19617                 this.snapshot.sort(s.direction, fn);
19618             }
19619         }
19620     },
19621
19622     /**
19623      * Sets the default sort column and order to be used by the next load operation.
19624      * @param {String} fieldName The name of the field to sort by.
19625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19626      */
19627     setDefaultSort : function(field, dir){
19628         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19629     },
19630
19631     /**
19632      * Sort the Records.
19633      * If remote sorting is used, the sort is performed on the server, and the cache is
19634      * reloaded. If local sorting is used, the cache is sorted internally.
19635      * @param {String} fieldName The name of the field to sort by.
19636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19637      */
19638     sort : function(fieldName, dir){
19639         var f = this.fields.get(fieldName);
19640         if(!dir){
19641             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19642             
19643             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19644                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19645             }else{
19646                 dir = f.sortDir;
19647             }
19648         }
19649         this.sortToggle[f.name] = dir;
19650         this.sortInfo = {field: f.name, direction: dir};
19651         if(!this.remoteSort){
19652             this.applySort();
19653             this.fireEvent("datachanged", this);
19654         }else{
19655             this.load(this.lastOptions);
19656         }
19657     },
19658
19659     /**
19660      * Calls the specified function for each of the Records in the cache.
19661      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19662      * Returning <em>false</em> aborts and exits the iteration.
19663      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19664      */
19665     each : function(fn, scope){
19666         this.data.each(fn, scope);
19667     },
19668
19669     /**
19670      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19671      * (e.g., during paging).
19672      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19673      */
19674     getModifiedRecords : function(){
19675         return this.modified;
19676     },
19677
19678     // private
19679     createFilterFn : function(property, value, anyMatch){
19680         if(!value.exec){ // not a regex
19681             value = String(value);
19682             if(value.length == 0){
19683                 return false;
19684             }
19685             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19686         }
19687         return function(r){
19688             return value.test(r.data[property]);
19689         };
19690     },
19691
19692     /**
19693      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19694      * @param {String} property A field on your records
19695      * @param {Number} start The record index to start at (defaults to 0)
19696      * @param {Number} end The last record index to include (defaults to length - 1)
19697      * @return {Number} The sum
19698      */
19699     sum : function(property, start, end){
19700         var rs = this.data.items, v = 0;
19701         start = start || 0;
19702         end = (end || end === 0) ? end : rs.length-1;
19703
19704         for(var i = start; i <= end; i++){
19705             v += (rs[i].data[property] || 0);
19706         }
19707         return v;
19708     },
19709
19710     /**
19711      * Filter the records by a specified property.
19712      * @param {String} field A field on your records
19713      * @param {String/RegExp} value Either a string that the field
19714      * should start with or a RegExp to test against the field
19715      * @param {Boolean} anyMatch True to match any part not just the beginning
19716      */
19717     filter : function(property, value, anyMatch){
19718         var fn = this.createFilterFn(property, value, anyMatch);
19719         return fn ? this.filterBy(fn) : this.clearFilter();
19720     },
19721
19722     /**
19723      * Filter by a function. The specified function will be called with each
19724      * record in this data source. If the function returns true the record is included,
19725      * otherwise it is filtered.
19726      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19727      * @param {Object} scope (optional) The scope of the function (defaults to this)
19728      */
19729     filterBy : function(fn, scope){
19730         this.snapshot = this.snapshot || this.data;
19731         this.data = this.queryBy(fn, scope||this);
19732         this.fireEvent("datachanged", this);
19733     },
19734
19735     /**
19736      * Query the records by a specified property.
19737      * @param {String} field A field on your records
19738      * @param {String/RegExp} value Either a string that the field
19739      * should start with or a RegExp to test against the field
19740      * @param {Boolean} anyMatch True to match any part not just the beginning
19741      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19742      */
19743     query : function(property, value, anyMatch){
19744         var fn = this.createFilterFn(property, value, anyMatch);
19745         return fn ? this.queryBy(fn) : this.data.clone();
19746     },
19747
19748     /**
19749      * Query by a function. The specified function will be called with each
19750      * record in this data source. If the function returns true the record is included
19751      * in the results.
19752      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19753      * @param {Object} scope (optional) The scope of the function (defaults to this)
19754       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19755      **/
19756     queryBy : function(fn, scope){
19757         var data = this.snapshot || this.data;
19758         return data.filterBy(fn, scope||this);
19759     },
19760
19761     /**
19762      * Collects unique values for a particular dataIndex from this store.
19763      * @param {String} dataIndex The property to collect
19764      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19765      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19766      * @return {Array} An array of the unique values
19767      **/
19768     collect : function(dataIndex, allowNull, bypassFilter){
19769         var d = (bypassFilter === true && this.snapshot) ?
19770                 this.snapshot.items : this.data.items;
19771         var v, sv, r = [], l = {};
19772         for(var i = 0, len = d.length; i < len; i++){
19773             v = d[i].data[dataIndex];
19774             sv = String(v);
19775             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19776                 l[sv] = true;
19777                 r[r.length] = v;
19778             }
19779         }
19780         return r;
19781     },
19782
19783     /**
19784      * Revert to a view of the Record cache with no filtering applied.
19785      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19786      */
19787     clearFilter : function(suppressEvent){
19788         if(this.snapshot && this.snapshot != this.data){
19789             this.data = this.snapshot;
19790             delete this.snapshot;
19791             if(suppressEvent !== true){
19792                 this.fireEvent("datachanged", this);
19793             }
19794         }
19795     },
19796
19797     // private
19798     afterEdit : function(record){
19799         if(this.modified.indexOf(record) == -1){
19800             this.modified.push(record);
19801         }
19802         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19803     },
19804     
19805     // private
19806     afterReject : function(record){
19807         this.modified.remove(record);
19808         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19809     },
19810
19811     // private
19812     afterCommit : function(record){
19813         this.modified.remove(record);
19814         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19815     },
19816
19817     /**
19818      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19819      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19820      */
19821     commitChanges : function(){
19822         var m = this.modified.slice(0);
19823         this.modified = [];
19824         for(var i = 0, len = m.length; i < len; i++){
19825             m[i].commit();
19826         }
19827     },
19828
19829     /**
19830      * Cancel outstanding changes on all changed records.
19831      */
19832     rejectChanges : function(){
19833         var m = this.modified.slice(0);
19834         this.modified = [];
19835         for(var i = 0, len = m.length; i < len; i++){
19836             m[i].reject();
19837         }
19838     },
19839
19840     onMetaChange : function(meta, rtype, o){
19841         this.recordType = rtype;
19842         this.fields = rtype.prototype.fields;
19843         delete this.snapshot;
19844         this.sortInfo = meta.sortInfo || this.sortInfo;
19845         this.modified = [];
19846         this.fireEvent('metachange', this, this.reader.meta);
19847     }
19848 });/*
19849  * Based on:
19850  * Ext JS Library 1.1.1
19851  * Copyright(c) 2006-2007, Ext JS, LLC.
19852  *
19853  * Originally Released Under LGPL - original licence link has changed is not relivant.
19854  *
19855  * Fork - LGPL
19856  * <script type="text/javascript">
19857  */
19858
19859 /**
19860  * @class Roo.data.SimpleStore
19861  * @extends Roo.data.Store
19862  * Small helper class to make creating Stores from Array data easier.
19863  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19864  * @cfg {Array} fields An array of field definition objects, or field name strings.
19865  * @cfg {Array} data The multi-dimensional array of data
19866  * @constructor
19867  * @param {Object} config
19868  */
19869 Roo.data.SimpleStore = function(config){
19870     Roo.data.SimpleStore.superclass.constructor.call(this, {
19871         isLocal : true,
19872         reader: new Roo.data.ArrayReader({
19873                 id: config.id
19874             },
19875             Roo.data.Record.create(config.fields)
19876         ),
19877         proxy : new Roo.data.MemoryProxy(config.data)
19878     });
19879     this.load();
19880 };
19881 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
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 /**
19894  * @extends Roo.data.Store
19895  * @class Roo.data.JsonStore
19896  * Small helper class to make creating Stores for JSON data easier. <br/>
19897 <pre><code>
19898 var store = new Roo.data.JsonStore({
19899     url: 'get-images.php',
19900     root: 'images',
19901     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19902 });
19903 </code></pre>
19904  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19905  * JsonReader and HttpProxy (unless inline data is provided).</b>
19906  * @cfg {Array} fields An array of field definition objects, or field name strings.
19907  * @constructor
19908  * @param {Object} config
19909  */
19910 Roo.data.JsonStore = function(c){
19911     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19912         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19913         reader: new Roo.data.JsonReader(c, c.fields)
19914     }));
19915 };
19916 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19917  * Based on:
19918  * Ext JS Library 1.1.1
19919  * Copyright(c) 2006-2007, Ext JS, LLC.
19920  *
19921  * Originally Released Under LGPL - original licence link has changed is not relivant.
19922  *
19923  * Fork - LGPL
19924  * <script type="text/javascript">
19925  */
19926
19927  
19928 Roo.data.Field = function(config){
19929     if(typeof config == "string"){
19930         config = {name: config};
19931     }
19932     Roo.apply(this, config);
19933     
19934     if(!this.type){
19935         this.type = "auto";
19936     }
19937     
19938     var st = Roo.data.SortTypes;
19939     // named sortTypes are supported, here we look them up
19940     if(typeof this.sortType == "string"){
19941         this.sortType = st[this.sortType];
19942     }
19943     
19944     // set default sortType for strings and dates
19945     if(!this.sortType){
19946         switch(this.type){
19947             case "string":
19948                 this.sortType = st.asUCString;
19949                 break;
19950             case "date":
19951                 this.sortType = st.asDate;
19952                 break;
19953             default:
19954                 this.sortType = st.none;
19955         }
19956     }
19957
19958     // define once
19959     var stripRe = /[\$,%]/g;
19960
19961     // prebuilt conversion function for this field, instead of
19962     // switching every time we're reading a value
19963     if(!this.convert){
19964         var cv, dateFormat = this.dateFormat;
19965         switch(this.type){
19966             case "":
19967             case "auto":
19968             case undefined:
19969                 cv = function(v){ return v; };
19970                 break;
19971             case "string":
19972                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19973                 break;
19974             case "int":
19975                 cv = function(v){
19976                     return v !== undefined && v !== null && v !== '' ?
19977                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19978                     };
19979                 break;
19980             case "float":
19981                 cv = function(v){
19982                     return v !== undefined && v !== null && v !== '' ?
19983                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19984                     };
19985                 break;
19986             case "bool":
19987             case "boolean":
19988                 cv = function(v){ return v === true || v === "true" || v == 1; };
19989                 break;
19990             case "date":
19991                 cv = function(v){
19992                     if(!v){
19993                         return '';
19994                     }
19995                     if(v instanceof Date){
19996                         return v;
19997                     }
19998                     if(dateFormat){
19999                         if(dateFormat == "timestamp"){
20000                             return new Date(v*1000);
20001                         }
20002                         return Date.parseDate(v, dateFormat);
20003                     }
20004                     var parsed = Date.parse(v);
20005                     return parsed ? new Date(parsed) : null;
20006                 };
20007              break;
20008             
20009         }
20010         this.convert = cv;
20011     }
20012 };
20013
20014 Roo.data.Field.prototype = {
20015     dateFormat: null,
20016     defaultValue: "",
20017     mapping: null,
20018     sortType : null,
20019     sortDir : "ASC"
20020 };/*
20021  * Based on:
20022  * Ext JS Library 1.1.1
20023  * Copyright(c) 2006-2007, Ext JS, LLC.
20024  *
20025  * Originally Released Under LGPL - original licence link has changed is not relivant.
20026  *
20027  * Fork - LGPL
20028  * <script type="text/javascript">
20029  */
20030  
20031 // Base class for reading structured data from a data source.  This class is intended to be
20032 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20033
20034 /**
20035  * @class Roo.data.DataReader
20036  * Base class for reading structured data from a data source.  This class is intended to be
20037  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20038  */
20039
20040 Roo.data.DataReader = function(meta, recordType){
20041     
20042     this.meta = meta;
20043     
20044     this.recordType = recordType instanceof Array ? 
20045         Roo.data.Record.create(recordType) : recordType;
20046 };
20047
20048 Roo.data.DataReader.prototype = {
20049      /**
20050      * Create an empty record
20051      * @param {Object} data (optional) - overlay some values
20052      * @return {Roo.data.Record} record created.
20053      */
20054     newRow :  function(d) {
20055         var da =  {};
20056         this.recordType.prototype.fields.each(function(c) {
20057             switch( c.type) {
20058                 case 'int' : da[c.name] = 0; break;
20059                 case 'date' : da[c.name] = new Date(); break;
20060                 case 'float' : da[c.name] = 0.0; break;
20061                 case 'boolean' : da[c.name] = false; break;
20062                 default : da[c.name] = ""; break;
20063             }
20064             
20065         });
20066         return new this.recordType(Roo.apply(da, d));
20067     }
20068     
20069 };/*
20070  * Based on:
20071  * Ext JS Library 1.1.1
20072  * Copyright(c) 2006-2007, Ext JS, LLC.
20073  *
20074  * Originally Released Under LGPL - original licence link has changed is not relivant.
20075  *
20076  * Fork - LGPL
20077  * <script type="text/javascript">
20078  */
20079
20080 /**
20081  * @class Roo.data.DataProxy
20082  * @extends Roo.data.Observable
20083  * This class is an abstract base class for implementations which provide retrieval of
20084  * unformatted data objects.<br>
20085  * <p>
20086  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20087  * (of the appropriate type which knows how to parse the data object) to provide a block of
20088  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20089  * <p>
20090  * Custom implementations must implement the load method as described in
20091  * {@link Roo.data.HttpProxy#load}.
20092  */
20093 Roo.data.DataProxy = function(){
20094     this.addEvents({
20095         /**
20096          * @event beforeload
20097          * Fires before a network request is made to retrieve a data object.
20098          * @param {Object} This DataProxy object.
20099          * @param {Object} params The params parameter to the load function.
20100          */
20101         beforeload : true,
20102         /**
20103          * @event load
20104          * Fires before the load method's callback is called.
20105          * @param {Object} This DataProxy object.
20106          * @param {Object} o The data object.
20107          * @param {Object} arg The callback argument object passed to the load function.
20108          */
20109         load : true,
20110         /**
20111          * @event loadexception
20112          * Fires if an Exception occurs during data retrieval.
20113          * @param {Object} This DataProxy object.
20114          * @param {Object} o The data object.
20115          * @param {Object} arg The callback argument object passed to the load function.
20116          * @param {Object} e The Exception.
20117          */
20118         loadexception : true
20119     });
20120     Roo.data.DataProxy.superclass.constructor.call(this);
20121 };
20122
20123 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20124
20125     /**
20126      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20127      */
20128 /*
20129  * Based on:
20130  * Ext JS Library 1.1.1
20131  * Copyright(c) 2006-2007, Ext JS, LLC.
20132  *
20133  * Originally Released Under LGPL - original licence link has changed is not relivant.
20134  *
20135  * Fork - LGPL
20136  * <script type="text/javascript">
20137  */
20138 /**
20139  * @class Roo.data.MemoryProxy
20140  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20141  * to the Reader when its load method is called.
20142  * @constructor
20143  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20144  */
20145 Roo.data.MemoryProxy = function(data){
20146     if (data.data) {
20147         data = data.data;
20148     }
20149     Roo.data.MemoryProxy.superclass.constructor.call(this);
20150     this.data = data;
20151 };
20152
20153 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20154     /**
20155      * Load data from the requested source (in this case an in-memory
20156      * data object passed to the constructor), read the data object into
20157      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20158      * process that block using the passed callback.
20159      * @param {Object} params This parameter is not used by the MemoryProxy class.
20160      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20161      * object into a block of Roo.data.Records.
20162      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20163      * The function must be passed <ul>
20164      * <li>The Record block object</li>
20165      * <li>The "arg" argument from the load function</li>
20166      * <li>A boolean success indicator</li>
20167      * </ul>
20168      * @param {Object} scope The scope in which to call the callback
20169      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20170      */
20171     load : function(params, reader, callback, scope, arg){
20172         params = params || {};
20173         var result;
20174         try {
20175             result = reader.readRecords(this.data);
20176         }catch(e){
20177             this.fireEvent("loadexception", this, arg, null, e);
20178             callback.call(scope, null, arg, false);
20179             return;
20180         }
20181         callback.call(scope, result, arg, true);
20182     },
20183     
20184     // private
20185     update : function(params, records){
20186         
20187     }
20188 });/*
20189  * Based on:
20190  * Ext JS Library 1.1.1
20191  * Copyright(c) 2006-2007, Ext JS, LLC.
20192  *
20193  * Originally Released Under LGPL - original licence link has changed is not relivant.
20194  *
20195  * Fork - LGPL
20196  * <script type="text/javascript">
20197  */
20198 /**
20199  * @class Roo.data.HttpProxy
20200  * @extends Roo.data.DataProxy
20201  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20202  * configured to reference a certain URL.<br><br>
20203  * <p>
20204  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20205  * from which the running page was served.<br><br>
20206  * <p>
20207  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20208  * <p>
20209  * Be aware that to enable the browser to parse an XML document, the server must set
20210  * the Content-Type header in the HTTP response to "text/xml".
20211  * @constructor
20212  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20213  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20214  * will be used to make the request.
20215  */
20216 Roo.data.HttpProxy = function(conn){
20217     Roo.data.HttpProxy.superclass.constructor.call(this);
20218     // is conn a conn config or a real conn?
20219     this.conn = conn;
20220     this.useAjax = !conn || !conn.events;
20221   
20222 };
20223
20224 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20225     // thse are take from connection...
20226     
20227     /**
20228      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20229      */
20230     /**
20231      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20232      * extra parameters to each request made by this object. (defaults to undefined)
20233      */
20234     /**
20235      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20236      *  to each request made by this object. (defaults to undefined)
20237      */
20238     /**
20239      * @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)
20240      */
20241     /**
20242      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20243      */
20244      /**
20245      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20246      * @type Boolean
20247      */
20248   
20249
20250     /**
20251      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20252      * @type Boolean
20253      */
20254     /**
20255      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20256      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20257      * a finer-grained basis than the DataProxy events.
20258      */
20259     getConnection : function(){
20260         return this.useAjax ? Roo.Ajax : this.conn;
20261     },
20262
20263     /**
20264      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20265      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20266      * process that block using the passed callback.
20267      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20268      * for the request to the remote server.
20269      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20270      * object into a block of Roo.data.Records.
20271      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20272      * The function must be passed <ul>
20273      * <li>The Record block object</li>
20274      * <li>The "arg" argument from the load function</li>
20275      * <li>A boolean success indicator</li>
20276      * </ul>
20277      * @param {Object} scope The scope in which to call the callback
20278      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20279      */
20280     load : function(params, reader, callback, scope, arg){
20281         if(this.fireEvent("beforeload", this, params) !== false){
20282             var  o = {
20283                 params : params || {},
20284                 request: {
20285                     callback : callback,
20286                     scope : scope,
20287                     arg : arg
20288                 },
20289                 reader: reader,
20290                 callback : this.loadResponse,
20291                 scope: this
20292             };
20293             if(this.useAjax){
20294                 Roo.applyIf(o, this.conn);
20295                 if(this.activeRequest){
20296                     Roo.Ajax.abort(this.activeRequest);
20297                 }
20298                 this.activeRequest = Roo.Ajax.request(o);
20299             }else{
20300                 this.conn.request(o);
20301             }
20302         }else{
20303             callback.call(scope||this, null, arg, false);
20304         }
20305     },
20306
20307     // private
20308     loadResponse : function(o, success, response){
20309         delete this.activeRequest;
20310         if(!success){
20311             this.fireEvent("loadexception", this, o, response);
20312             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20313             return;
20314         }
20315         var result;
20316         try {
20317             result = o.reader.read(response);
20318         }catch(e){
20319             this.fireEvent("loadexception", this, o, response, e);
20320             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20321             return;
20322         }
20323         
20324         this.fireEvent("load", this, o, o.request.arg);
20325         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20326     },
20327
20328     // private
20329     update : function(dataSet){
20330
20331     },
20332
20333     // private
20334     updateResponse : function(dataSet){
20335
20336     }
20337 });/*
20338  * Based on:
20339  * Ext JS Library 1.1.1
20340  * Copyright(c) 2006-2007, Ext JS, LLC.
20341  *
20342  * Originally Released Under LGPL - original licence link has changed is not relivant.
20343  *
20344  * Fork - LGPL
20345  * <script type="text/javascript">
20346  */
20347
20348 /**
20349  * @class Roo.data.ScriptTagProxy
20350  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20351  * other than the originating domain of the running page.<br><br>
20352  * <p>
20353  * <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
20354  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20355  * <p>
20356  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20357  * source code that is used as the source inside a &lt;script> tag.<br><br>
20358  * <p>
20359  * In order for the browser to process the returned data, the server must wrap the data object
20360  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20361  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20362  * depending on whether the callback name was passed:
20363  * <p>
20364  * <pre><code>
20365 boolean scriptTag = false;
20366 String cb = request.getParameter("callback");
20367 if (cb != null) {
20368     scriptTag = true;
20369     response.setContentType("text/javascript");
20370 } else {
20371     response.setContentType("application/x-json");
20372 }
20373 Writer out = response.getWriter();
20374 if (scriptTag) {
20375     out.write(cb + "(");
20376 }
20377 out.print(dataBlock.toJsonString());
20378 if (scriptTag) {
20379     out.write(");");
20380 }
20381 </pre></code>
20382  *
20383  * @constructor
20384  * @param {Object} config A configuration object.
20385  */
20386 Roo.data.ScriptTagProxy = function(config){
20387     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20388     Roo.apply(this, config);
20389     this.head = document.getElementsByTagName("head")[0];
20390 };
20391
20392 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20393
20394 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20395     /**
20396      * @cfg {String} url The URL from which to request the data object.
20397      */
20398     /**
20399      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20400      */
20401     timeout : 30000,
20402     /**
20403      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20404      * the server the name of the callback function set up by the load call to process the returned data object.
20405      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20406      * javascript output which calls this named function passing the data object as its only parameter.
20407      */
20408     callbackParam : "callback",
20409     /**
20410      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20411      * name to the request.
20412      */
20413     nocache : true,
20414
20415     /**
20416      * Load data from the configured URL, read the data object into
20417      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20418      * process that block using the passed callback.
20419      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20420      * for the request to the remote server.
20421      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20422      * object into a block of Roo.data.Records.
20423      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20424      * The function must be passed <ul>
20425      * <li>The Record block object</li>
20426      * <li>The "arg" argument from the load function</li>
20427      * <li>A boolean success indicator</li>
20428      * </ul>
20429      * @param {Object} scope The scope in which to call the callback
20430      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20431      */
20432     load : function(params, reader, callback, scope, arg){
20433         if(this.fireEvent("beforeload", this, params) !== false){
20434
20435             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20436
20437             var url = this.url;
20438             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20439             if(this.nocache){
20440                 url += "&_dc=" + (new Date().getTime());
20441             }
20442             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20443             var trans = {
20444                 id : transId,
20445                 cb : "stcCallback"+transId,
20446                 scriptId : "stcScript"+transId,
20447                 params : params,
20448                 arg : arg,
20449                 url : url,
20450                 callback : callback,
20451                 scope : scope,
20452                 reader : reader
20453             };
20454             var conn = this;
20455
20456             window[trans.cb] = function(o){
20457                 conn.handleResponse(o, trans);
20458             };
20459
20460             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20461
20462             if(this.autoAbort !== false){
20463                 this.abort();
20464             }
20465
20466             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20467
20468             var script = document.createElement("script");
20469             script.setAttribute("src", url);
20470             script.setAttribute("type", "text/javascript");
20471             script.setAttribute("id", trans.scriptId);
20472             this.head.appendChild(script);
20473
20474             this.trans = trans;
20475         }else{
20476             callback.call(scope||this, null, arg, false);
20477         }
20478     },
20479
20480     // private
20481     isLoading : function(){
20482         return this.trans ? true : false;
20483     },
20484
20485     /**
20486      * Abort the current server request.
20487      */
20488     abort : function(){
20489         if(this.isLoading()){
20490             this.destroyTrans(this.trans);
20491         }
20492     },
20493
20494     // private
20495     destroyTrans : function(trans, isLoaded){
20496         this.head.removeChild(document.getElementById(trans.scriptId));
20497         clearTimeout(trans.timeoutId);
20498         if(isLoaded){
20499             window[trans.cb] = undefined;
20500             try{
20501                 delete window[trans.cb];
20502             }catch(e){}
20503         }else{
20504             // if hasn't been loaded, wait for load to remove it to prevent script error
20505             window[trans.cb] = function(){
20506                 window[trans.cb] = undefined;
20507                 try{
20508                     delete window[trans.cb];
20509                 }catch(e){}
20510             };
20511         }
20512     },
20513
20514     // private
20515     handleResponse : function(o, trans){
20516         this.trans = false;
20517         this.destroyTrans(trans, true);
20518         var result;
20519         try {
20520             result = trans.reader.readRecords(o);
20521         }catch(e){
20522             this.fireEvent("loadexception", this, o, trans.arg, e);
20523             trans.callback.call(trans.scope||window, null, trans.arg, false);
20524             return;
20525         }
20526         this.fireEvent("load", this, o, trans.arg);
20527         trans.callback.call(trans.scope||window, result, trans.arg, true);
20528     },
20529
20530     // private
20531     handleFailure : function(trans){
20532         this.trans = false;
20533         this.destroyTrans(trans, false);
20534         this.fireEvent("loadexception", this, null, trans.arg);
20535         trans.callback.call(trans.scope||window, null, trans.arg, false);
20536     }
20537 });/*
20538  * Based on:
20539  * Ext JS Library 1.1.1
20540  * Copyright(c) 2006-2007, Ext JS, LLC.
20541  *
20542  * Originally Released Under LGPL - original licence link has changed is not relivant.
20543  *
20544  * Fork - LGPL
20545  * <script type="text/javascript">
20546  */
20547
20548 /**
20549  * @class Roo.data.JsonReader
20550  * @extends Roo.data.DataReader
20551  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20552  * based on mappings in a provided Roo.data.Record constructor.
20553  * 
20554  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20555  * in the reply previously. 
20556  * 
20557  * <p>
20558  * Example code:
20559  * <pre><code>
20560 var RecordDef = Roo.data.Record.create([
20561     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20562     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20563 ]);
20564 var myReader = new Roo.data.JsonReader({
20565     totalProperty: "results",    // The property which contains the total dataset size (optional)
20566     root: "rows",                // The property which contains an Array of row objects
20567     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20568 }, RecordDef);
20569 </code></pre>
20570  * <p>
20571  * This would consume a JSON file like this:
20572  * <pre><code>
20573 { 'results': 2, 'rows': [
20574     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20575     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20576 }
20577 </code></pre>
20578  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20579  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20580  * paged from the remote server.
20581  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20582  * @cfg {String} root name of the property which contains the Array of row objects.
20583  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20584  * @constructor
20585  * Create a new JsonReader
20586  * @param {Object} meta Metadata configuration options
20587  * @param {Object} recordType Either an Array of field definition objects,
20588  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20589  */
20590 Roo.data.JsonReader = function(meta, recordType){
20591     
20592     meta = meta || {};
20593     // set some defaults:
20594     Roo.applyIf(meta, {
20595         totalProperty: 'total',
20596         successProperty : 'success',
20597         root : 'data',
20598         id : 'id'
20599     });
20600     
20601     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20602 };
20603 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20604     
20605     /**
20606      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20607      * Used by Store query builder to append _requestMeta to params.
20608      * 
20609      */
20610     metaFromRemote : false,
20611     /**
20612      * This method is only used by a DataProxy which has retrieved data from a remote server.
20613      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20614      * @return {Object} data A data block which is used by an Roo.data.Store object as
20615      * a cache of Roo.data.Records.
20616      */
20617     read : function(response){
20618         var json = response.responseText;
20619        
20620         var o = /* eval:var:o */ eval("("+json+")");
20621         if(!o) {
20622             throw {message: "JsonReader.read: Json object not found"};
20623         }
20624         
20625         if(o.metaData){
20626             
20627             delete this.ef;
20628             this.metaFromRemote = true;
20629             this.meta = o.metaData;
20630             this.recordType = Roo.data.Record.create(o.metaData.fields);
20631             this.onMetaChange(this.meta, this.recordType, o);
20632         }
20633         return this.readRecords(o);
20634     },
20635
20636     // private function a store will implement
20637     onMetaChange : function(meta, recordType, o){
20638
20639     },
20640
20641     /**
20642          * @ignore
20643          */
20644     simpleAccess: function(obj, subsc) {
20645         return obj[subsc];
20646     },
20647
20648         /**
20649          * @ignore
20650          */
20651     getJsonAccessor: function(){
20652         var re = /[\[\.]/;
20653         return function(expr) {
20654             try {
20655                 return(re.test(expr))
20656                     ? new Function("obj", "return obj." + expr)
20657                     : function(obj){
20658                         return obj[expr];
20659                     };
20660             } catch(e){}
20661             return Roo.emptyFn;
20662         };
20663     }(),
20664
20665     /**
20666      * Create a data block containing Roo.data.Records from an XML document.
20667      * @param {Object} o An object which contains an Array of row objects in the property specified
20668      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20669      * which contains the total size of the dataset.
20670      * @return {Object} data A data block which is used by an Roo.data.Store object as
20671      * a cache of Roo.data.Records.
20672      */
20673     readRecords : function(o){
20674         /**
20675          * After any data loads, the raw JSON data is available for further custom processing.
20676          * @type Object
20677          */
20678         this.jsonData = o;
20679         var s = this.meta, Record = this.recordType,
20680             f = Record.prototype.fields, fi = f.items, fl = f.length;
20681
20682 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20683         if (!this.ef) {
20684             if(s.totalProperty) {
20685                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20686                 }
20687                 if(s.successProperty) {
20688                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20689                 }
20690                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20691                 if (s.id) {
20692                         var g = this.getJsonAccessor(s.id);
20693                         this.getId = function(rec) {
20694                                 var r = g(rec);
20695                                 return (r === undefined || r === "") ? null : r;
20696                         };
20697                 } else {
20698                         this.getId = function(){return null;};
20699                 }
20700             this.ef = [];
20701             for(var jj = 0; jj < fl; jj++){
20702                 f = fi[jj];
20703                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20704                 this.ef[jj] = this.getJsonAccessor(map);
20705             }
20706         }
20707
20708         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20709         if(s.totalProperty){
20710             var vt = parseInt(this.getTotal(o), 10);
20711             if(!isNaN(vt)){
20712                 totalRecords = vt;
20713             }
20714         }
20715         if(s.successProperty){
20716             var vs = this.getSuccess(o);
20717             if(vs === false || vs === 'false'){
20718                 success = false;
20719             }
20720         }
20721         var records = [];
20722             for(var i = 0; i < c; i++){
20723                     var n = root[i];
20724                 var values = {};
20725                 var id = this.getId(n);
20726                 for(var j = 0; j < fl; j++){
20727                     f = fi[j];
20728                 var v = this.ef[j](n);
20729                 if (!f.convert) {
20730                     Roo.log('missing convert for ' + f.name);
20731                     Roo.log(f);
20732                     continue;
20733                 }
20734                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20735                 }
20736                 var record = new Record(values, id);
20737                 record.json = n;
20738                 records[i] = record;
20739             }
20740             return {
20741                 success : success,
20742                 records : records,
20743                 totalRecords : totalRecords
20744             };
20745     }
20746 });/*
20747  * Based on:
20748  * Ext JS Library 1.1.1
20749  * Copyright(c) 2006-2007, Ext JS, LLC.
20750  *
20751  * Originally Released Under LGPL - original licence link has changed is not relivant.
20752  *
20753  * Fork - LGPL
20754  * <script type="text/javascript">
20755  */
20756
20757 /**
20758  * @class Roo.data.XmlReader
20759  * @extends Roo.data.DataReader
20760  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20761  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20762  * <p>
20763  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20764  * header in the HTTP response must be set to "text/xml".</em>
20765  * <p>
20766  * Example code:
20767  * <pre><code>
20768 var RecordDef = Roo.data.Record.create([
20769    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20770    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20771 ]);
20772 var myReader = new Roo.data.XmlReader({
20773    totalRecords: "results", // The element which contains the total dataset size (optional)
20774    record: "row",           // The repeated element which contains row information
20775    id: "id"                 // The element within the row that provides an ID for the record (optional)
20776 }, RecordDef);
20777 </code></pre>
20778  * <p>
20779  * This would consume an XML file like this:
20780  * <pre><code>
20781 &lt;?xml?>
20782 &lt;dataset>
20783  &lt;results>2&lt;/results>
20784  &lt;row>
20785    &lt;id>1&lt;/id>
20786    &lt;name>Bill&lt;/name>
20787    &lt;occupation>Gardener&lt;/occupation>
20788  &lt;/row>
20789  &lt;row>
20790    &lt;id>2&lt;/id>
20791    &lt;name>Ben&lt;/name>
20792    &lt;occupation>Horticulturalist&lt;/occupation>
20793  &lt;/row>
20794 &lt;/dataset>
20795 </code></pre>
20796  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20798  * paged from the remote server.
20799  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20800  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20801  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20802  * a record identifier value.
20803  * @constructor
20804  * Create a new XmlReader
20805  * @param {Object} meta Metadata configuration options
20806  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20807  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20808  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20809  */
20810 Roo.data.XmlReader = function(meta, recordType){
20811     meta = meta || {};
20812     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20813 };
20814 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20815     /**
20816      * This method is only used by a DataProxy which has retrieved data from a remote server.
20817          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20818          * to contain a method called 'responseXML' that returns an XML document object.
20819      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20820      * a cache of Roo.data.Records.
20821      */
20822     read : function(response){
20823         var doc = response.responseXML;
20824         if(!doc) {
20825             throw {message: "XmlReader.read: XML Document not available"};
20826         }
20827         return this.readRecords(doc);
20828     },
20829
20830     /**
20831      * Create a data block containing Roo.data.Records from an XML document.
20832          * @param {Object} doc A parsed XML document.
20833      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20834      * a cache of Roo.data.Records.
20835      */
20836     readRecords : function(doc){
20837         /**
20838          * After any data loads/reads, the raw XML Document is available for further custom processing.
20839          * @type XMLDocument
20840          */
20841         this.xmlData = doc;
20842         var root = doc.documentElement || doc;
20843         var q = Roo.DomQuery;
20844         var recordType = this.recordType, fields = recordType.prototype.fields;
20845         var sid = this.meta.id;
20846         var totalRecords = 0, success = true;
20847         if(this.meta.totalRecords){
20848             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20849         }
20850         
20851         if(this.meta.success){
20852             var sv = q.selectValue(this.meta.success, root, true);
20853             success = sv !== false && sv !== 'false';
20854         }
20855         var records = [];
20856         var ns = q.select(this.meta.record, root);
20857         for(var i = 0, len = ns.length; i < len; i++) {
20858                 var n = ns[i];
20859                 var values = {};
20860                 var id = sid ? q.selectValue(sid, n) : undefined;
20861                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20862                     var f = fields.items[j];
20863                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20864                     v = f.convert(v);
20865                     values[f.name] = v;
20866                 }
20867                 var record = new recordType(values, id);
20868                 record.node = n;
20869                 records[records.length] = record;
20870             }
20871
20872             return {
20873                 success : success,
20874                 records : records,
20875                 totalRecords : totalRecords || records.length
20876             };
20877     }
20878 });/*
20879  * Based on:
20880  * Ext JS Library 1.1.1
20881  * Copyright(c) 2006-2007, Ext JS, LLC.
20882  *
20883  * Originally Released Under LGPL - original licence link has changed is not relivant.
20884  *
20885  * Fork - LGPL
20886  * <script type="text/javascript">
20887  */
20888
20889 /**
20890  * @class Roo.data.ArrayReader
20891  * @extends Roo.data.DataReader
20892  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20893  * Each element of that Array represents a row of data fields. The
20894  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20895  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20896  * <p>
20897  * Example code:.
20898  * <pre><code>
20899 var RecordDef = Roo.data.Record.create([
20900     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20901     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20902 ]);
20903 var myReader = new Roo.data.ArrayReader({
20904     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20905 }, RecordDef);
20906 </code></pre>
20907  * <p>
20908  * This would consume an Array like this:
20909  * <pre><code>
20910 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20911   </code></pre>
20912  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20913  * @constructor
20914  * Create a new JsonReader
20915  * @param {Object} meta Metadata configuration options.
20916  * @param {Object} recordType Either an Array of field definition objects
20917  * as specified to {@link Roo.data.Record#create},
20918  * or an {@link Roo.data.Record} object
20919  * created using {@link Roo.data.Record#create}.
20920  */
20921 Roo.data.ArrayReader = function(meta, recordType){
20922     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20923 };
20924
20925 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20926     /**
20927      * Create a data block containing Roo.data.Records from an XML document.
20928      * @param {Object} o An Array of row objects which represents the dataset.
20929      * @return {Object} data A data block which is used by an Roo.data.Store object as
20930      * a cache of Roo.data.Records.
20931      */
20932     readRecords : function(o){
20933         var sid = this.meta ? this.meta.id : null;
20934         var recordType = this.recordType, fields = recordType.prototype.fields;
20935         var records = [];
20936         var root = o;
20937             for(var i = 0; i < root.length; i++){
20938                     var n = root[i];
20939                 var values = {};
20940                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20941                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20942                 var f = fields.items[j];
20943                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20944                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20945                 v = f.convert(v);
20946                 values[f.name] = v;
20947             }
20948                 var record = new recordType(values, id);
20949                 record.json = n;
20950                 records[records.length] = record;
20951             }
20952             return {
20953                 records : records,
20954                 totalRecords : records.length
20955             };
20956     }
20957 });/*
20958  * Based on:
20959  * Ext JS Library 1.1.1
20960  * Copyright(c) 2006-2007, Ext JS, LLC.
20961  *
20962  * Originally Released Under LGPL - original licence link has changed is not relivant.
20963  *
20964  * Fork - LGPL
20965  * <script type="text/javascript">
20966  */
20967
20968
20969 /**
20970  * @class Roo.data.Tree
20971  * @extends Roo.util.Observable
20972  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20973  * in the tree have most standard DOM functionality.
20974  * @constructor
20975  * @param {Node} root (optional) The root node
20976  */
20977 Roo.data.Tree = function(root){
20978    this.nodeHash = {};
20979    /**
20980     * The root node for this tree
20981     * @type Node
20982     */
20983    this.root = null;
20984    if(root){
20985        this.setRootNode(root);
20986    }
20987    this.addEvents({
20988        /**
20989         * @event append
20990         * Fires when a new child node is appended to a node in this tree.
20991         * @param {Tree} tree The owner tree
20992         * @param {Node} parent The parent node
20993         * @param {Node} node The newly appended node
20994         * @param {Number} index The index of the newly appended node
20995         */
20996        "append" : true,
20997        /**
20998         * @event remove
20999         * Fires when a child node is removed from a node in this tree.
21000         * @param {Tree} tree The owner tree
21001         * @param {Node} parent The parent node
21002         * @param {Node} node The child node removed
21003         */
21004        "remove" : true,
21005        /**
21006         * @event move
21007         * Fires when a node is moved to a new location in the tree
21008         * @param {Tree} tree The owner tree
21009         * @param {Node} node The node moved
21010         * @param {Node} oldParent The old parent of this node
21011         * @param {Node} newParent The new parent of this node
21012         * @param {Number} index The index it was moved to
21013         */
21014        "move" : true,
21015        /**
21016         * @event insert
21017         * Fires when a new child node is inserted in a node in this tree.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node inserted
21021         * @param {Node} refNode The child node the node was inserted before
21022         */
21023        "insert" : true,
21024        /**
21025         * @event beforeappend
21026         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21027         * @param {Tree} tree The owner tree
21028         * @param {Node} parent The parent node
21029         * @param {Node} node The child node to be appended
21030         */
21031        "beforeappend" : true,
21032        /**
21033         * @event beforeremove
21034         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21035         * @param {Tree} tree The owner tree
21036         * @param {Node} parent The parent node
21037         * @param {Node} node The child node to be removed
21038         */
21039        "beforeremove" : true,
21040        /**
21041         * @event beforemove
21042         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21043         * @param {Tree} tree The owner tree
21044         * @param {Node} node The node being moved
21045         * @param {Node} oldParent The parent of the node
21046         * @param {Node} newParent The new parent the node is moving to
21047         * @param {Number} index The index it is being moved to
21048         */
21049        "beforemove" : true,
21050        /**
21051         * @event beforeinsert
21052         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21053         * @param {Tree} tree The owner tree
21054         * @param {Node} parent The parent node
21055         * @param {Node} node The child node to be inserted
21056         * @param {Node} refNode The child node the node is being inserted before
21057         */
21058        "beforeinsert" : true
21059    });
21060
21061     Roo.data.Tree.superclass.constructor.call(this);
21062 };
21063
21064 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21065     pathSeparator: "/",
21066
21067     proxyNodeEvent : function(){
21068         return this.fireEvent.apply(this, arguments);
21069     },
21070
21071     /**
21072      * Returns the root node for this tree.
21073      * @return {Node}
21074      */
21075     getRootNode : function(){
21076         return this.root;
21077     },
21078
21079     /**
21080      * Sets the root node for this tree.
21081      * @param {Node} node
21082      * @return {Node}
21083      */
21084     setRootNode : function(node){
21085         this.root = node;
21086         node.ownerTree = this;
21087         node.isRoot = true;
21088         this.registerNode(node);
21089         return node;
21090     },
21091
21092     /**
21093      * Gets a node in this tree by its id.
21094      * @param {String} id
21095      * @return {Node}
21096      */
21097     getNodeById : function(id){
21098         return this.nodeHash[id];
21099     },
21100
21101     registerNode : function(node){
21102         this.nodeHash[node.id] = node;
21103     },
21104
21105     unregisterNode : function(node){
21106         delete this.nodeHash[node.id];
21107     },
21108
21109     toString : function(){
21110         return "[Tree"+(this.id?" "+this.id:"")+"]";
21111     }
21112 });
21113
21114 /**
21115  * @class Roo.data.Node
21116  * @extends Roo.util.Observable
21117  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21118  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21119  * @constructor
21120  * @param {Object} attributes The attributes/config for the node
21121  */
21122 Roo.data.Node = function(attributes){
21123     /**
21124      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21125      * @type {Object}
21126      */
21127     this.attributes = attributes || {};
21128     this.leaf = this.attributes.leaf;
21129     /**
21130      * The node id. @type String
21131      */
21132     this.id = this.attributes.id;
21133     if(!this.id){
21134         this.id = Roo.id(null, "ynode-");
21135         this.attributes.id = this.id;
21136     }
21137     /**
21138      * All child nodes of this node. @type Array
21139      */
21140     this.childNodes = [];
21141     if(!this.childNodes.indexOf){ // indexOf is a must
21142         this.childNodes.indexOf = function(o){
21143             for(var i = 0, len = this.length; i < len; i++){
21144                 if(this[i] == o) {
21145                     return i;
21146                 }
21147             }
21148             return -1;
21149         };
21150     }
21151     /**
21152      * The parent node for this node. @type Node
21153      */
21154     this.parentNode = null;
21155     /**
21156      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21157      */
21158     this.firstChild = null;
21159     /**
21160      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21161      */
21162     this.lastChild = null;
21163     /**
21164      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21165      */
21166     this.previousSibling = null;
21167     /**
21168      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21169      */
21170     this.nextSibling = null;
21171
21172     this.addEvents({
21173        /**
21174         * @event append
21175         * Fires when a new child node is appended
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The newly appended node
21179         * @param {Number} index The index of the newly appended node
21180         */
21181        "append" : true,
21182        /**
21183         * @event remove
21184         * Fires when a child node is removed
21185         * @param {Tree} tree The owner tree
21186         * @param {Node} this This node
21187         * @param {Node} node The removed node
21188         */
21189        "remove" : true,
21190        /**
21191         * @event move
21192         * Fires when this node is moved to a new location in the tree
21193         * @param {Tree} tree The owner tree
21194         * @param {Node} this This node
21195         * @param {Node} oldParent The old parent of this node
21196         * @param {Node} newParent The new parent of this node
21197         * @param {Number} index The index it was moved to
21198         */
21199        "move" : true,
21200        /**
21201         * @event insert
21202         * Fires when a new child node is inserted.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node inserted
21206         * @param {Node} refNode The child node the node was inserted before
21207         */
21208        "insert" : true,
21209        /**
21210         * @event beforeappend
21211         * Fires before a new child is appended, return false to cancel the append.
21212         * @param {Tree} tree The owner tree
21213         * @param {Node} this This node
21214         * @param {Node} node The child node to be appended
21215         */
21216        "beforeappend" : true,
21217        /**
21218         * @event beforeremove
21219         * Fires before a child is removed, return false to cancel the remove.
21220         * @param {Tree} tree The owner tree
21221         * @param {Node} this This node
21222         * @param {Node} node The child node to be removed
21223         */
21224        "beforeremove" : true,
21225        /**
21226         * @event beforemove
21227         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21228         * @param {Tree} tree The owner tree
21229         * @param {Node} this This node
21230         * @param {Node} oldParent The parent of this node
21231         * @param {Node} newParent The new parent this node is moving to
21232         * @param {Number} index The index it is being moved to
21233         */
21234        "beforemove" : true,
21235        /**
21236         * @event beforeinsert
21237         * Fires before a new child is inserted, return false to cancel the insert.
21238         * @param {Tree} tree The owner tree
21239         * @param {Node} this This node
21240         * @param {Node} node The child node to be inserted
21241         * @param {Node} refNode The child node the node is being inserted before
21242         */
21243        "beforeinsert" : true
21244    });
21245     this.listeners = this.attributes.listeners;
21246     Roo.data.Node.superclass.constructor.call(this);
21247 };
21248
21249 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21250     fireEvent : function(evtName){
21251         // first do standard event for this node
21252         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21253             return false;
21254         }
21255         // then bubble it up to the tree if the event wasn't cancelled
21256         var ot = this.getOwnerTree();
21257         if(ot){
21258             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21259                 return false;
21260             }
21261         }
21262         return true;
21263     },
21264
21265     /**
21266      * Returns true if this node is a leaf
21267      * @return {Boolean}
21268      */
21269     isLeaf : function(){
21270         return this.leaf === true;
21271     },
21272
21273     // private
21274     setFirstChild : function(node){
21275         this.firstChild = node;
21276     },
21277
21278     //private
21279     setLastChild : function(node){
21280         this.lastChild = node;
21281     },
21282
21283
21284     /**
21285      * Returns true if this node is the last child of its parent
21286      * @return {Boolean}
21287      */
21288     isLast : function(){
21289        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21290     },
21291
21292     /**
21293      * Returns true if this node is the first child of its parent
21294      * @return {Boolean}
21295      */
21296     isFirst : function(){
21297        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21298     },
21299
21300     hasChildNodes : function(){
21301         return !this.isLeaf() && this.childNodes.length > 0;
21302     },
21303
21304     /**
21305      * Insert node(s) as the last child node of this node.
21306      * @param {Node/Array} node The node or Array of nodes to append
21307      * @return {Node} The appended node if single append, or null if an array was passed
21308      */
21309     appendChild : function(node){
21310         var multi = false;
21311         if(node instanceof Array){
21312             multi = node;
21313         }else if(arguments.length > 1){
21314             multi = arguments;
21315         }
21316         // if passed an array or multiple args do them one by one
21317         if(multi){
21318             for(var i = 0, len = multi.length; i < len; i++) {
21319                 this.appendChild(multi[i]);
21320             }
21321         }else{
21322             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21323                 return false;
21324             }
21325             var index = this.childNodes.length;
21326             var oldParent = node.parentNode;
21327             // it's a move, make sure we move it cleanly
21328             if(oldParent){
21329                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21330                     return false;
21331                 }
21332                 oldParent.removeChild(node);
21333             }
21334             index = this.childNodes.length;
21335             if(index == 0){
21336                 this.setFirstChild(node);
21337             }
21338             this.childNodes.push(node);
21339             node.parentNode = this;
21340             var ps = this.childNodes[index-1];
21341             if(ps){
21342                 node.previousSibling = ps;
21343                 ps.nextSibling = node;
21344             }else{
21345                 node.previousSibling = null;
21346             }
21347             node.nextSibling = null;
21348             this.setLastChild(node);
21349             node.setOwnerTree(this.getOwnerTree());
21350             this.fireEvent("append", this.ownerTree, this, node, index);
21351             if(oldParent){
21352                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21353             }
21354             return node;
21355         }
21356     },
21357
21358     /**
21359      * Removes a child node from this node.
21360      * @param {Node} node The node to remove
21361      * @return {Node} The removed node
21362      */
21363     removeChild : function(node){
21364         var index = this.childNodes.indexOf(node);
21365         if(index == -1){
21366             return false;
21367         }
21368         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21369             return false;
21370         }
21371
21372         // remove it from childNodes collection
21373         this.childNodes.splice(index, 1);
21374
21375         // update siblings
21376         if(node.previousSibling){
21377             node.previousSibling.nextSibling = node.nextSibling;
21378         }
21379         if(node.nextSibling){
21380             node.nextSibling.previousSibling = node.previousSibling;
21381         }
21382
21383         // update child refs
21384         if(this.firstChild == node){
21385             this.setFirstChild(node.nextSibling);
21386         }
21387         if(this.lastChild == node){
21388             this.setLastChild(node.previousSibling);
21389         }
21390
21391         node.setOwnerTree(null);
21392         // clear any references from the node
21393         node.parentNode = null;
21394         node.previousSibling = null;
21395         node.nextSibling = null;
21396         this.fireEvent("remove", this.ownerTree, this, node);
21397         return node;
21398     },
21399
21400     /**
21401      * Inserts the first node before the second node in this nodes childNodes collection.
21402      * @param {Node} node The node to insert
21403      * @param {Node} refNode The node to insert before (if null the node is appended)
21404      * @return {Node} The inserted node
21405      */
21406     insertBefore : function(node, refNode){
21407         if(!refNode){ // like standard Dom, refNode can be null for append
21408             return this.appendChild(node);
21409         }
21410         // nothing to do
21411         if(node == refNode){
21412             return false;
21413         }
21414
21415         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21416             return false;
21417         }
21418         var index = this.childNodes.indexOf(refNode);
21419         var oldParent = node.parentNode;
21420         var refIndex = index;
21421
21422         // when moving internally, indexes will change after remove
21423         if(oldParent == this && this.childNodes.indexOf(node) < index){
21424             refIndex--;
21425         }
21426
21427         // it's a move, make sure we move it cleanly
21428         if(oldParent){
21429             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21430                 return false;
21431             }
21432             oldParent.removeChild(node);
21433         }
21434         if(refIndex == 0){
21435             this.setFirstChild(node);
21436         }
21437         this.childNodes.splice(refIndex, 0, node);
21438         node.parentNode = this;
21439         var ps = this.childNodes[refIndex-1];
21440         if(ps){
21441             node.previousSibling = ps;
21442             ps.nextSibling = node;
21443         }else{
21444             node.previousSibling = null;
21445         }
21446         node.nextSibling = refNode;
21447         refNode.previousSibling = node;
21448         node.setOwnerTree(this.getOwnerTree());
21449         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21450         if(oldParent){
21451             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21452         }
21453         return node;
21454     },
21455
21456     /**
21457      * Returns the child node at the specified index.
21458      * @param {Number} index
21459      * @return {Node}
21460      */
21461     item : function(index){
21462         return this.childNodes[index];
21463     },
21464
21465     /**
21466      * Replaces one child node in this node with another.
21467      * @param {Node} newChild The replacement node
21468      * @param {Node} oldChild The node to replace
21469      * @return {Node} The replaced node
21470      */
21471     replaceChild : function(newChild, oldChild){
21472         this.insertBefore(newChild, oldChild);
21473         this.removeChild(oldChild);
21474         return oldChild;
21475     },
21476
21477     /**
21478      * Returns the index of a child node
21479      * @param {Node} node
21480      * @return {Number} The index of the node or -1 if it was not found
21481      */
21482     indexOf : function(child){
21483         return this.childNodes.indexOf(child);
21484     },
21485
21486     /**
21487      * Returns the tree this node is in.
21488      * @return {Tree}
21489      */
21490     getOwnerTree : function(){
21491         // if it doesn't have one, look for one
21492         if(!this.ownerTree){
21493             var p = this;
21494             while(p){
21495                 if(p.ownerTree){
21496                     this.ownerTree = p.ownerTree;
21497                     break;
21498                 }
21499                 p = p.parentNode;
21500             }
21501         }
21502         return this.ownerTree;
21503     },
21504
21505     /**
21506      * Returns depth of this node (the root node has a depth of 0)
21507      * @return {Number}
21508      */
21509     getDepth : function(){
21510         var depth = 0;
21511         var p = this;
21512         while(p.parentNode){
21513             ++depth;
21514             p = p.parentNode;
21515         }
21516         return depth;
21517     },
21518
21519     // private
21520     setOwnerTree : function(tree){
21521         // if it's move, we need to update everyone
21522         if(tree != this.ownerTree){
21523             if(this.ownerTree){
21524                 this.ownerTree.unregisterNode(this);
21525             }
21526             this.ownerTree = tree;
21527             var cs = this.childNodes;
21528             for(var i = 0, len = cs.length; i < len; i++) {
21529                 cs[i].setOwnerTree(tree);
21530             }
21531             if(tree){
21532                 tree.registerNode(this);
21533             }
21534         }
21535     },
21536
21537     /**
21538      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21539      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21540      * @return {String} The path
21541      */
21542     getPath : function(attr){
21543         attr = attr || "id";
21544         var p = this.parentNode;
21545         var b = [this.attributes[attr]];
21546         while(p){
21547             b.unshift(p.attributes[attr]);
21548             p = p.parentNode;
21549         }
21550         var sep = this.getOwnerTree().pathSeparator;
21551         return sep + b.join(sep);
21552     },
21553
21554     /**
21555      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21556      * function call will be the scope provided or the current node. The arguments to the function
21557      * will be the args provided or the current node. If the function returns false at any point,
21558      * the bubble is stopped.
21559      * @param {Function} fn The function to call
21560      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21561      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21562      */
21563     bubble : function(fn, scope, args){
21564         var p = this;
21565         while(p){
21566             if(fn.call(scope || p, args || p) === false){
21567                 break;
21568             }
21569             p = p.parentNode;
21570         }
21571     },
21572
21573     /**
21574      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21575      * function call will be the scope provided or the current node. The arguments to the function
21576      * will be the args provided or the current node. If the function returns false at any point,
21577      * the cascade is stopped on that branch.
21578      * @param {Function} fn The function to call
21579      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21580      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21581      */
21582     cascade : function(fn, scope, args){
21583         if(fn.call(scope || this, args || this) !== false){
21584             var cs = this.childNodes;
21585             for(var i = 0, len = cs.length; i < len; i++) {
21586                 cs[i].cascade(fn, scope, args);
21587             }
21588         }
21589     },
21590
21591     /**
21592      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21593      * function call will be the scope provided or the current node. The arguments to the function
21594      * will be the args provided or the current node. If the function returns false at any point,
21595      * the iteration stops.
21596      * @param {Function} fn The function to call
21597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21599      */
21600     eachChild : function(fn, scope, args){
21601         var cs = this.childNodes;
21602         for(var i = 0, len = cs.length; i < len; i++) {
21603                 if(fn.call(scope || this, args || cs[i]) === false){
21604                     break;
21605                 }
21606         }
21607     },
21608
21609     /**
21610      * Finds the first child that has the attribute with the specified value.
21611      * @param {String} attribute The attribute name
21612      * @param {Mixed} value The value to search for
21613      * @return {Node} The found child or null if none was found
21614      */
21615     findChild : function(attribute, value){
21616         var cs = this.childNodes;
21617         for(var i = 0, len = cs.length; i < len; i++) {
21618                 if(cs[i].attributes[attribute] == value){
21619                     return cs[i];
21620                 }
21621         }
21622         return null;
21623     },
21624
21625     /**
21626      * Finds the first child by a custom function. The child matches if the function passed
21627      * returns true.
21628      * @param {Function} fn
21629      * @param {Object} scope (optional)
21630      * @return {Node} The found child or null if none was found
21631      */
21632     findChildBy : function(fn, scope){
21633         var cs = this.childNodes;
21634         for(var i = 0, len = cs.length; i < len; i++) {
21635                 if(fn.call(scope||cs[i], cs[i]) === true){
21636                     return cs[i];
21637                 }
21638         }
21639         return null;
21640     },
21641
21642     /**
21643      * Sorts this nodes children using the supplied sort function
21644      * @param {Function} fn
21645      * @param {Object} scope (optional)
21646      */
21647     sort : function(fn, scope){
21648         var cs = this.childNodes;
21649         var len = cs.length;
21650         if(len > 0){
21651             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21652             cs.sort(sortFn);
21653             for(var i = 0; i < len; i++){
21654                 var n = cs[i];
21655                 n.previousSibling = cs[i-1];
21656                 n.nextSibling = cs[i+1];
21657                 if(i == 0){
21658                     this.setFirstChild(n);
21659                 }
21660                 if(i == len-1){
21661                     this.setLastChild(n);
21662                 }
21663             }
21664         }
21665     },
21666
21667     /**
21668      * Returns true if this node is an ancestor (at any point) of the passed node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     contains : function(node){
21673         return node.isAncestor(this);
21674     },
21675
21676     /**
21677      * Returns true if the passed node is an ancestor (at any point) of this node.
21678      * @param {Node} node
21679      * @return {Boolean}
21680      */
21681     isAncestor : function(node){
21682         var p = this.parentNode;
21683         while(p){
21684             if(p == node){
21685                 return true;
21686             }
21687             p = p.parentNode;
21688         }
21689         return false;
21690     },
21691
21692     toString : function(){
21693         return "[Node"+(this.id?" "+this.id:"")+"]";
21694     }
21695 });/*
21696  * Based on:
21697  * Ext JS Library 1.1.1
21698  * Copyright(c) 2006-2007, Ext JS, LLC.
21699  *
21700  * Originally Released Under LGPL - original licence link has changed is not relivant.
21701  *
21702  * Fork - LGPL
21703  * <script type="text/javascript">
21704  */
21705  
21706
21707 /**
21708  * @class Roo.ComponentMgr
21709  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21710  * @singleton
21711  */
21712 Roo.ComponentMgr = function(){
21713     var all = new Roo.util.MixedCollection();
21714
21715     return {
21716         /**
21717          * Registers a component.
21718          * @param {Roo.Component} c The component
21719          */
21720         register : function(c){
21721             all.add(c);
21722         },
21723
21724         /**
21725          * Unregisters a component.
21726          * @param {Roo.Component} c The component
21727          */
21728         unregister : function(c){
21729             all.remove(c);
21730         },
21731
21732         /**
21733          * Returns a component by id
21734          * @param {String} id The component id
21735          */
21736         get : function(id){
21737             return all.get(id);
21738         },
21739
21740         /**
21741          * Registers a function that will be called when a specified component is added to ComponentMgr
21742          * @param {String} id The component id
21743          * @param {Funtction} fn The callback function
21744          * @param {Object} scope The scope of the callback
21745          */
21746         onAvailable : function(id, fn, scope){
21747             all.on("add", function(index, o){
21748                 if(o.id == id){
21749                     fn.call(scope || o, o);
21750                     all.un("add", fn, scope);
21751                 }
21752             });
21753         }
21754     };
21755 }();/*
21756  * Based on:
21757  * Ext JS Library 1.1.1
21758  * Copyright(c) 2006-2007, Ext JS, LLC.
21759  *
21760  * Originally Released Under LGPL - original licence link has changed is not relivant.
21761  *
21762  * Fork - LGPL
21763  * <script type="text/javascript">
21764  */
21765  
21766 /**
21767  * @class Roo.Component
21768  * @extends Roo.util.Observable
21769  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21770  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21771  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21772  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21773  * All visual components (widgets) that require rendering into a layout should subclass Component.
21774  * @constructor
21775  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21776  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21777  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21778  */
21779 Roo.Component = function(config){
21780     config = config || {};
21781     if(config.tagName || config.dom || typeof config == "string"){ // element object
21782         config = {el: config, id: config.id || config};
21783     }
21784     this.initialConfig = config;
21785
21786     Roo.apply(this, config);
21787     this.addEvents({
21788         /**
21789          * @event disable
21790          * Fires after the component is disabled.
21791              * @param {Roo.Component} this
21792              */
21793         disable : true,
21794         /**
21795          * @event enable
21796          * Fires after the component is enabled.
21797              * @param {Roo.Component} this
21798              */
21799         enable : true,
21800         /**
21801          * @event beforeshow
21802          * Fires before the component is shown.  Return false to stop the show.
21803              * @param {Roo.Component} this
21804              */
21805         beforeshow : true,
21806         /**
21807          * @event show
21808          * Fires after the component is shown.
21809              * @param {Roo.Component} this
21810              */
21811         show : true,
21812         /**
21813          * @event beforehide
21814          * Fires before the component is hidden. Return false to stop the hide.
21815              * @param {Roo.Component} this
21816              */
21817         beforehide : true,
21818         /**
21819          * @event hide
21820          * Fires after the component is hidden.
21821              * @param {Roo.Component} this
21822              */
21823         hide : true,
21824         /**
21825          * @event beforerender
21826          * Fires before the component is rendered. Return false to stop the render.
21827              * @param {Roo.Component} this
21828              */
21829         beforerender : true,
21830         /**
21831          * @event render
21832          * Fires after the component is rendered.
21833              * @param {Roo.Component} this
21834              */
21835         render : true,
21836         /**
21837          * @event beforedestroy
21838          * Fires before the component is destroyed. Return false to stop the destroy.
21839              * @param {Roo.Component} this
21840              */
21841         beforedestroy : true,
21842         /**
21843          * @event destroy
21844          * Fires after the component is destroyed.
21845              * @param {Roo.Component} this
21846              */
21847         destroy : true
21848     });
21849     if(!this.id){
21850         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21851     }
21852     Roo.ComponentMgr.register(this);
21853     Roo.Component.superclass.constructor.call(this);
21854     this.initComponent();
21855     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21856         this.render(this.renderTo);
21857         delete this.renderTo;
21858     }
21859 };
21860
21861 /** @private */
21862 Roo.Component.AUTO_ID = 1000;
21863
21864 Roo.extend(Roo.Component, Roo.util.Observable, {
21865     /**
21866      * @scope Roo.Component.prototype
21867      * @type {Boolean}
21868      * true if this component is hidden. Read-only.
21869      */
21870     hidden : false,
21871     /**
21872      * @type {Boolean}
21873      * true if this component is disabled. Read-only.
21874      */
21875     disabled : false,
21876     /**
21877      * @type {Boolean}
21878      * true if this component has been rendered. Read-only.
21879      */
21880     rendered : false,
21881     
21882     /** @cfg {String} disableClass
21883      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21884      */
21885     disabledClass : "x-item-disabled",
21886         /** @cfg {Boolean} allowDomMove
21887          * Whether the component can move the Dom node when rendering (defaults to true).
21888          */
21889     allowDomMove : true,
21890     /** @cfg {String} hideMode
21891      * How this component should hidden. Supported values are
21892      * "visibility" (css visibility), "offsets" (negative offset position) and
21893      * "display" (css display) - defaults to "display".
21894      */
21895     hideMode: 'display',
21896
21897     /** @private */
21898     ctype : "Roo.Component",
21899
21900     /**
21901      * @cfg {String} actionMode 
21902      * which property holds the element that used for  hide() / show() / disable() / enable()
21903      * default is 'el' 
21904      */
21905     actionMode : "el",
21906
21907     /** @private */
21908     getActionEl : function(){
21909         return this[this.actionMode];
21910     },
21911
21912     initComponent : Roo.emptyFn,
21913     /**
21914      * If this is a lazy rendering component, render it to its container element.
21915      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21916      */
21917     render : function(container, position){
21918         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21919             if(!container && this.el){
21920                 this.el = Roo.get(this.el);
21921                 container = this.el.dom.parentNode;
21922                 this.allowDomMove = false;
21923             }
21924             this.container = Roo.get(container);
21925             this.rendered = true;
21926             if(position !== undefined){
21927                 if(typeof position == 'number'){
21928                     position = this.container.dom.childNodes[position];
21929                 }else{
21930                     position = Roo.getDom(position);
21931                 }
21932             }
21933             this.onRender(this.container, position || null);
21934             if(this.cls){
21935                 this.el.addClass(this.cls);
21936                 delete this.cls;
21937             }
21938             if(this.style){
21939                 this.el.applyStyles(this.style);
21940                 delete this.style;
21941             }
21942             this.fireEvent("render", this);
21943             this.afterRender(this.container);
21944             if(this.hidden){
21945                 this.hide();
21946             }
21947             if(this.disabled){
21948                 this.disable();
21949             }
21950         }
21951         return this;
21952     },
21953
21954     /** @private */
21955     // default function is not really useful
21956     onRender : function(ct, position){
21957         if(this.el){
21958             this.el = Roo.get(this.el);
21959             if(this.allowDomMove !== false){
21960                 ct.dom.insertBefore(this.el.dom, position);
21961             }
21962         }
21963     },
21964
21965     /** @private */
21966     getAutoCreate : function(){
21967         var cfg = typeof this.autoCreate == "object" ?
21968                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21969         if(this.id && !cfg.id){
21970             cfg.id = this.id;
21971         }
21972         return cfg;
21973     },
21974
21975     /** @private */
21976     afterRender : Roo.emptyFn,
21977
21978     /**
21979      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21980      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21981      */
21982     destroy : function(){
21983         if(this.fireEvent("beforedestroy", this) !== false){
21984             this.purgeListeners();
21985             this.beforeDestroy();
21986             if(this.rendered){
21987                 this.el.removeAllListeners();
21988                 this.el.remove();
21989                 if(this.actionMode == "container"){
21990                     this.container.remove();
21991                 }
21992             }
21993             this.onDestroy();
21994             Roo.ComponentMgr.unregister(this);
21995             this.fireEvent("destroy", this);
21996         }
21997     },
21998
21999         /** @private */
22000     beforeDestroy : function(){
22001
22002     },
22003
22004         /** @private */
22005         onDestroy : function(){
22006
22007     },
22008
22009     /**
22010      * Returns the underlying {@link Roo.Element}.
22011      * @return {Roo.Element} The element
22012      */
22013     getEl : function(){
22014         return this.el;
22015     },
22016
22017     /**
22018      * Returns the id of this component.
22019      * @return {String}
22020      */
22021     getId : function(){
22022         return this.id;
22023     },
22024
22025     /**
22026      * Try to focus this component.
22027      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22028      * @return {Roo.Component} this
22029      */
22030     focus : function(selectText){
22031         if(this.rendered){
22032             this.el.focus();
22033             if(selectText === true){
22034                 this.el.dom.select();
22035             }
22036         }
22037         return this;
22038     },
22039
22040     /** @private */
22041     blur : function(){
22042         if(this.rendered){
22043             this.el.blur();
22044         }
22045         return this;
22046     },
22047
22048     /**
22049      * Disable this component.
22050      * @return {Roo.Component} this
22051      */
22052     disable : function(){
22053         if(this.rendered){
22054             this.onDisable();
22055         }
22056         this.disabled = true;
22057         this.fireEvent("disable", this);
22058         return this;
22059     },
22060
22061         // private
22062     onDisable : function(){
22063         this.getActionEl().addClass(this.disabledClass);
22064         this.el.dom.disabled = true;
22065     },
22066
22067     /**
22068      * Enable this component.
22069      * @return {Roo.Component} this
22070      */
22071     enable : function(){
22072         if(this.rendered){
22073             this.onEnable();
22074         }
22075         this.disabled = false;
22076         this.fireEvent("enable", this);
22077         return this;
22078     },
22079
22080         // private
22081     onEnable : function(){
22082         this.getActionEl().removeClass(this.disabledClass);
22083         this.el.dom.disabled = false;
22084     },
22085
22086     /**
22087      * Convenience function for setting disabled/enabled by boolean.
22088      * @param {Boolean} disabled
22089      */
22090     setDisabled : function(disabled){
22091         this[disabled ? "disable" : "enable"]();
22092     },
22093
22094     /**
22095      * Show this component.
22096      * @return {Roo.Component} this
22097      */
22098     show: function(){
22099         if(this.fireEvent("beforeshow", this) !== false){
22100             this.hidden = false;
22101             if(this.rendered){
22102                 this.onShow();
22103             }
22104             this.fireEvent("show", this);
22105         }
22106         return this;
22107     },
22108
22109     // private
22110     onShow : function(){
22111         var ae = this.getActionEl();
22112         if(this.hideMode == 'visibility'){
22113             ae.dom.style.visibility = "visible";
22114         }else if(this.hideMode == 'offsets'){
22115             ae.removeClass('x-hidden');
22116         }else{
22117             ae.dom.style.display = "";
22118         }
22119     },
22120
22121     /**
22122      * Hide this component.
22123      * @return {Roo.Component} this
22124      */
22125     hide: function(){
22126         if(this.fireEvent("beforehide", this) !== false){
22127             this.hidden = true;
22128             if(this.rendered){
22129                 this.onHide();
22130             }
22131             this.fireEvent("hide", this);
22132         }
22133         return this;
22134     },
22135
22136     // private
22137     onHide : function(){
22138         var ae = this.getActionEl();
22139         if(this.hideMode == 'visibility'){
22140             ae.dom.style.visibility = "hidden";
22141         }else if(this.hideMode == 'offsets'){
22142             ae.addClass('x-hidden');
22143         }else{
22144             ae.dom.style.display = "none";
22145         }
22146     },
22147
22148     /**
22149      * Convenience function to hide or show this component by boolean.
22150      * @param {Boolean} visible True to show, false to hide
22151      * @return {Roo.Component} this
22152      */
22153     setVisible: function(visible){
22154         if(visible) {
22155             this.show();
22156         }else{
22157             this.hide();
22158         }
22159         return this;
22160     },
22161
22162     /**
22163      * Returns true if this component is visible.
22164      */
22165     isVisible : function(){
22166         return this.getActionEl().isVisible();
22167     },
22168
22169     cloneConfig : function(overrides){
22170         overrides = overrides || {};
22171         var id = overrides.id || Roo.id();
22172         var cfg = Roo.applyIf(overrides, this.initialConfig);
22173         cfg.id = id; // prevent dup id
22174         return new this.constructor(cfg);
22175     }
22176 });/*
22177  * Based on:
22178  * Ext JS Library 1.1.1
22179  * Copyright(c) 2006-2007, Ext JS, LLC.
22180  *
22181  * Originally Released Under LGPL - original licence link has changed is not relivant.
22182  *
22183  * Fork - LGPL
22184  * <script type="text/javascript">
22185  */
22186  (function(){ 
22187 /**
22188  * @class Roo.Layer
22189  * @extends Roo.Element
22190  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22191  * automatic maintaining of shadow/shim positions.
22192  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22193  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22194  * you can pass a string with a CSS class name. False turns off the shadow.
22195  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22196  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22197  * @cfg {String} cls CSS class to add to the element
22198  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22199  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22200  * @constructor
22201  * @param {Object} config An object with config options.
22202  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22203  */
22204
22205 Roo.Layer = function(config, existingEl){
22206     config = config || {};
22207     var dh = Roo.DomHelper;
22208     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22209     if(existingEl){
22210         this.dom = Roo.getDom(existingEl);
22211     }
22212     if(!this.dom){
22213         var o = config.dh || {tag: "div", cls: "x-layer"};
22214         this.dom = dh.append(pel, o);
22215     }
22216     if(config.cls){
22217         this.addClass(config.cls);
22218     }
22219     this.constrain = config.constrain !== false;
22220     this.visibilityMode = Roo.Element.VISIBILITY;
22221     if(config.id){
22222         this.id = this.dom.id = config.id;
22223     }else{
22224         this.id = Roo.id(this.dom);
22225     }
22226     this.zindex = config.zindex || this.getZIndex();
22227     this.position("absolute", this.zindex);
22228     if(config.shadow){
22229         this.shadowOffset = config.shadowOffset || 4;
22230         this.shadow = new Roo.Shadow({
22231             offset : this.shadowOffset,
22232             mode : config.shadow
22233         });
22234     }else{
22235         this.shadowOffset = 0;
22236     }
22237     this.useShim = config.shim !== false && Roo.useShims;
22238     this.useDisplay = config.useDisplay;
22239     this.hide();
22240 };
22241
22242 var supr = Roo.Element.prototype;
22243
22244 // shims are shared among layer to keep from having 100 iframes
22245 var shims = [];
22246
22247 Roo.extend(Roo.Layer, Roo.Element, {
22248
22249     getZIndex : function(){
22250         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22251     },
22252
22253     getShim : function(){
22254         if(!this.useShim){
22255             return null;
22256         }
22257         if(this.shim){
22258             return this.shim;
22259         }
22260         var shim = shims.shift();
22261         if(!shim){
22262             shim = this.createShim();
22263             shim.enableDisplayMode('block');
22264             shim.dom.style.display = 'none';
22265             shim.dom.style.visibility = 'visible';
22266         }
22267         var pn = this.dom.parentNode;
22268         if(shim.dom.parentNode != pn){
22269             pn.insertBefore(shim.dom, this.dom);
22270         }
22271         shim.setStyle('z-index', this.getZIndex()-2);
22272         this.shim = shim;
22273         return shim;
22274     },
22275
22276     hideShim : function(){
22277         if(this.shim){
22278             this.shim.setDisplayed(false);
22279             shims.push(this.shim);
22280             delete this.shim;
22281         }
22282     },
22283
22284     disableShadow : function(){
22285         if(this.shadow){
22286             this.shadowDisabled = true;
22287             this.shadow.hide();
22288             this.lastShadowOffset = this.shadowOffset;
22289             this.shadowOffset = 0;
22290         }
22291     },
22292
22293     enableShadow : function(show){
22294         if(this.shadow){
22295             this.shadowDisabled = false;
22296             this.shadowOffset = this.lastShadowOffset;
22297             delete this.lastShadowOffset;
22298             if(show){
22299                 this.sync(true);
22300             }
22301         }
22302     },
22303
22304     // private
22305     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22306     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22307     sync : function(doShow){
22308         var sw = this.shadow;
22309         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22310             var sh = this.getShim();
22311
22312             var w = this.getWidth(),
22313                 h = this.getHeight();
22314
22315             var l = this.getLeft(true),
22316                 t = this.getTop(true);
22317
22318             if(sw && !this.shadowDisabled){
22319                 if(doShow && !sw.isVisible()){
22320                     sw.show(this);
22321                 }else{
22322                     sw.realign(l, t, w, h);
22323                 }
22324                 if(sh){
22325                     if(doShow){
22326                        sh.show();
22327                     }
22328                     // fit the shim behind the shadow, so it is shimmed too
22329                     var a = sw.adjusts, s = sh.dom.style;
22330                     s.left = (Math.min(l, l+a.l))+"px";
22331                     s.top = (Math.min(t, t+a.t))+"px";
22332                     s.width = (w+a.w)+"px";
22333                     s.height = (h+a.h)+"px";
22334                 }
22335             }else if(sh){
22336                 if(doShow){
22337                    sh.show();
22338                 }
22339                 sh.setSize(w, h);
22340                 sh.setLeftTop(l, t);
22341             }
22342             
22343         }
22344     },
22345
22346     // private
22347     destroy : function(){
22348         this.hideShim();
22349         if(this.shadow){
22350             this.shadow.hide();
22351         }
22352         this.removeAllListeners();
22353         var pn = this.dom.parentNode;
22354         if(pn){
22355             pn.removeChild(this.dom);
22356         }
22357         Roo.Element.uncache(this.id);
22358     },
22359
22360     remove : function(){
22361         this.destroy();
22362     },
22363
22364     // private
22365     beginUpdate : function(){
22366         this.updating = true;
22367     },
22368
22369     // private
22370     endUpdate : function(){
22371         this.updating = false;
22372         this.sync(true);
22373     },
22374
22375     // private
22376     hideUnders : function(negOffset){
22377         if(this.shadow){
22378             this.shadow.hide();
22379         }
22380         this.hideShim();
22381     },
22382
22383     // private
22384     constrainXY : function(){
22385         if(this.constrain){
22386             var vw = Roo.lib.Dom.getViewWidth(),
22387                 vh = Roo.lib.Dom.getViewHeight();
22388             var s = Roo.get(document).getScroll();
22389
22390             var xy = this.getXY();
22391             var x = xy[0], y = xy[1];   
22392             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22393             // only move it if it needs it
22394             var moved = false;
22395             // first validate right/bottom
22396             if((x + w) > vw+s.left){
22397                 x = vw - w - this.shadowOffset;
22398                 moved = true;
22399             }
22400             if((y + h) > vh+s.top){
22401                 y = vh - h - this.shadowOffset;
22402                 moved = true;
22403             }
22404             // then make sure top/left isn't negative
22405             if(x < s.left){
22406                 x = s.left;
22407                 moved = true;
22408             }
22409             if(y < s.top){
22410                 y = s.top;
22411                 moved = true;
22412             }
22413             if(moved){
22414                 if(this.avoidY){
22415                     var ay = this.avoidY;
22416                     if(y <= ay && (y+h) >= ay){
22417                         y = ay-h-5;   
22418                     }
22419                 }
22420                 xy = [x, y];
22421                 this.storeXY(xy);
22422                 supr.setXY.call(this, xy);
22423                 this.sync();
22424             }
22425         }
22426     },
22427
22428     isVisible : function(){
22429         return this.visible;    
22430     },
22431
22432     // private
22433     showAction : function(){
22434         this.visible = true; // track visibility to prevent getStyle calls
22435         if(this.useDisplay === true){
22436             this.setDisplayed("");
22437         }else if(this.lastXY){
22438             supr.setXY.call(this, this.lastXY);
22439         }else if(this.lastLT){
22440             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22441         }
22442     },
22443
22444     // private
22445     hideAction : function(){
22446         this.visible = false;
22447         if(this.useDisplay === true){
22448             this.setDisplayed(false);
22449         }else{
22450             this.setLeftTop(-10000,-10000);
22451         }
22452     },
22453
22454     // overridden Element method
22455     setVisible : function(v, a, d, c, e){
22456         if(v){
22457             this.showAction();
22458         }
22459         if(a && v){
22460             var cb = function(){
22461                 this.sync(true);
22462                 if(c){
22463                     c();
22464                 }
22465             }.createDelegate(this);
22466             supr.setVisible.call(this, true, true, d, cb, e);
22467         }else{
22468             if(!v){
22469                 this.hideUnders(true);
22470             }
22471             var cb = c;
22472             if(a){
22473                 cb = function(){
22474                     this.hideAction();
22475                     if(c){
22476                         c();
22477                     }
22478                 }.createDelegate(this);
22479             }
22480             supr.setVisible.call(this, v, a, d, cb, e);
22481             if(v){
22482                 this.sync(true);
22483             }else if(!a){
22484                 this.hideAction();
22485             }
22486         }
22487     },
22488
22489     storeXY : function(xy){
22490         delete this.lastLT;
22491         this.lastXY = xy;
22492     },
22493
22494     storeLeftTop : function(left, top){
22495         delete this.lastXY;
22496         this.lastLT = [left, top];
22497     },
22498
22499     // private
22500     beforeFx : function(){
22501         this.beforeAction();
22502         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22503     },
22504
22505     // private
22506     afterFx : function(){
22507         Roo.Layer.superclass.afterFx.apply(this, arguments);
22508         this.sync(this.isVisible());
22509     },
22510
22511     // private
22512     beforeAction : function(){
22513         if(!this.updating && this.shadow){
22514             this.shadow.hide();
22515         }
22516     },
22517
22518     // overridden Element method
22519     setLeft : function(left){
22520         this.storeLeftTop(left, this.getTop(true));
22521         supr.setLeft.apply(this, arguments);
22522         this.sync();
22523     },
22524
22525     setTop : function(top){
22526         this.storeLeftTop(this.getLeft(true), top);
22527         supr.setTop.apply(this, arguments);
22528         this.sync();
22529     },
22530
22531     setLeftTop : function(left, top){
22532         this.storeLeftTop(left, top);
22533         supr.setLeftTop.apply(this, arguments);
22534         this.sync();
22535     },
22536
22537     setXY : function(xy, a, d, c, e){
22538         this.fixDisplay();
22539         this.beforeAction();
22540         this.storeXY(xy);
22541         var cb = this.createCB(c);
22542         supr.setXY.call(this, xy, a, d, cb, e);
22543         if(!a){
22544             cb();
22545         }
22546     },
22547
22548     // private
22549     createCB : function(c){
22550         var el = this;
22551         return function(){
22552             el.constrainXY();
22553             el.sync(true);
22554             if(c){
22555                 c();
22556             }
22557         };
22558     },
22559
22560     // overridden Element method
22561     setX : function(x, a, d, c, e){
22562         this.setXY([x, this.getY()], a, d, c, e);
22563     },
22564
22565     // overridden Element method
22566     setY : function(y, a, d, c, e){
22567         this.setXY([this.getX(), y], a, d, c, e);
22568     },
22569
22570     // overridden Element method
22571     setSize : function(w, h, a, d, c, e){
22572         this.beforeAction();
22573         var cb = this.createCB(c);
22574         supr.setSize.call(this, w, h, a, d, cb, e);
22575         if(!a){
22576             cb();
22577         }
22578     },
22579
22580     // overridden Element method
22581     setWidth : function(w, a, d, c, e){
22582         this.beforeAction();
22583         var cb = this.createCB(c);
22584         supr.setWidth.call(this, w, a, d, cb, e);
22585         if(!a){
22586             cb();
22587         }
22588     },
22589
22590     // overridden Element method
22591     setHeight : function(h, a, d, c, e){
22592         this.beforeAction();
22593         var cb = this.createCB(c);
22594         supr.setHeight.call(this, h, a, d, cb, e);
22595         if(!a){
22596             cb();
22597         }
22598     },
22599
22600     // overridden Element method
22601     setBounds : function(x, y, w, h, a, d, c, e){
22602         this.beforeAction();
22603         var cb = this.createCB(c);
22604         if(!a){
22605             this.storeXY([x, y]);
22606             supr.setXY.call(this, [x, y]);
22607             supr.setSize.call(this, w, h, a, d, cb, e);
22608             cb();
22609         }else{
22610             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22611         }
22612         return this;
22613     },
22614     
22615     /**
22616      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22617      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22618      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22619      * @param {Number} zindex The new z-index to set
22620      * @return {this} The Layer
22621      */
22622     setZIndex : function(zindex){
22623         this.zindex = zindex;
22624         this.setStyle("z-index", zindex + 2);
22625         if(this.shadow){
22626             this.shadow.setZIndex(zindex + 1);
22627         }
22628         if(this.shim){
22629             this.shim.setStyle("z-index", zindex);
22630         }
22631     }
22632 });
22633 })();/*
22634  * Based on:
22635  * Ext JS Library 1.1.1
22636  * Copyright(c) 2006-2007, Ext JS, LLC.
22637  *
22638  * Originally Released Under LGPL - original licence link has changed is not relivant.
22639  *
22640  * Fork - LGPL
22641  * <script type="text/javascript">
22642  */
22643
22644
22645 /**
22646  * @class Roo.Shadow
22647  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22648  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22649  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22650  * @constructor
22651  * Create a new Shadow
22652  * @param {Object} config The config object
22653  */
22654 Roo.Shadow = function(config){
22655     Roo.apply(this, config);
22656     if(typeof this.mode != "string"){
22657         this.mode = this.defaultMode;
22658     }
22659     var o = this.offset, a = {h: 0};
22660     var rad = Math.floor(this.offset/2);
22661     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22662         case "drop":
22663             a.w = 0;
22664             a.l = a.t = o;
22665             a.t -= 1;
22666             if(Roo.isIE){
22667                 a.l -= this.offset + rad;
22668                 a.t -= this.offset + rad;
22669                 a.w -= rad;
22670                 a.h -= rad;
22671                 a.t += 1;
22672             }
22673         break;
22674         case "sides":
22675             a.w = (o*2);
22676             a.l = -o;
22677             a.t = o-1;
22678             if(Roo.isIE){
22679                 a.l -= (this.offset - rad);
22680                 a.t -= this.offset + rad;
22681                 a.l += 1;
22682                 a.w -= (this.offset - rad)*2;
22683                 a.w -= rad + 1;
22684                 a.h -= 1;
22685             }
22686         break;
22687         case "frame":
22688             a.w = a.h = (o*2);
22689             a.l = a.t = -o;
22690             a.t += 1;
22691             a.h -= 2;
22692             if(Roo.isIE){
22693                 a.l -= (this.offset - rad);
22694                 a.t -= (this.offset - rad);
22695                 a.l += 1;
22696                 a.w -= (this.offset + rad + 1);
22697                 a.h -= (this.offset + rad);
22698                 a.h += 1;
22699             }
22700         break;
22701     };
22702
22703     this.adjusts = a;
22704 };
22705
22706 Roo.Shadow.prototype = {
22707     /**
22708      * @cfg {String} mode
22709      * The shadow display mode.  Supports the following options:<br />
22710      * sides: Shadow displays on both sides and bottom only<br />
22711      * frame: Shadow displays equally on all four sides<br />
22712      * drop: Traditional bottom-right drop shadow (default)
22713      */
22714     /**
22715      * @cfg {String} offset
22716      * The number of pixels to offset the shadow from the element (defaults to 4)
22717      */
22718     offset: 4,
22719
22720     // private
22721     defaultMode: "drop",
22722
22723     /**
22724      * Displays the shadow under the target element
22725      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22726      */
22727     show : function(target){
22728         target = Roo.get(target);
22729         if(!this.el){
22730             this.el = Roo.Shadow.Pool.pull();
22731             if(this.el.dom.nextSibling != target.dom){
22732                 this.el.insertBefore(target);
22733             }
22734         }
22735         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22736         if(Roo.isIE){
22737             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22738         }
22739         this.realign(
22740             target.getLeft(true),
22741             target.getTop(true),
22742             target.getWidth(),
22743             target.getHeight()
22744         );
22745         this.el.dom.style.display = "block";
22746     },
22747
22748     /**
22749      * Returns true if the shadow is visible, else false
22750      */
22751     isVisible : function(){
22752         return this.el ? true : false;  
22753     },
22754
22755     /**
22756      * Direct alignment when values are already available. Show must be called at least once before
22757      * calling this method to ensure it is initialized.
22758      * @param {Number} left The target element left position
22759      * @param {Number} top The target element top position
22760      * @param {Number} width The target element width
22761      * @param {Number} height The target element height
22762      */
22763     realign : function(l, t, w, h){
22764         if(!this.el){
22765             return;
22766         }
22767         var a = this.adjusts, d = this.el.dom, s = d.style;
22768         var iea = 0;
22769         s.left = (l+a.l)+"px";
22770         s.top = (t+a.t)+"px";
22771         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22772  
22773         if(s.width != sws || s.height != shs){
22774             s.width = sws;
22775             s.height = shs;
22776             if(!Roo.isIE){
22777                 var cn = d.childNodes;
22778                 var sww = Math.max(0, (sw-12))+"px";
22779                 cn[0].childNodes[1].style.width = sww;
22780                 cn[1].childNodes[1].style.width = sww;
22781                 cn[2].childNodes[1].style.width = sww;
22782                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22783             }
22784         }
22785     },
22786
22787     /**
22788      * Hides this shadow
22789      */
22790     hide : function(){
22791         if(this.el){
22792             this.el.dom.style.display = "none";
22793             Roo.Shadow.Pool.push(this.el);
22794             delete this.el;
22795         }
22796     },
22797
22798     /**
22799      * Adjust the z-index of this shadow
22800      * @param {Number} zindex The new z-index
22801      */
22802     setZIndex : function(z){
22803         this.zIndex = z;
22804         if(this.el){
22805             this.el.setStyle("z-index", z);
22806         }
22807     }
22808 };
22809
22810 // Private utility class that manages the internal Shadow cache
22811 Roo.Shadow.Pool = function(){
22812     var p = [];
22813     var markup = Roo.isIE ?
22814                  '<div class="x-ie-shadow"></div>' :
22815                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22816     return {
22817         pull : function(){
22818             var sh = p.shift();
22819             if(!sh){
22820                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22821                 sh.autoBoxAdjust = false;
22822             }
22823             return sh;
22824         },
22825
22826         push : function(sh){
22827             p.push(sh);
22828         }
22829     };
22830 }();/*
22831  * Based on:
22832  * Ext JS Library 1.1.1
22833  * Copyright(c) 2006-2007, Ext JS, LLC.
22834  *
22835  * Originally Released Under LGPL - original licence link has changed is not relivant.
22836  *
22837  * Fork - LGPL
22838  * <script type="text/javascript">
22839  */
22840
22841 /**
22842  * @class Roo.BoxComponent
22843  * @extends Roo.Component
22844  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22845  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22846  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22847  * layout containers.
22848  * @constructor
22849  * @param {Roo.Element/String/Object} config The configuration options.
22850  */
22851 Roo.BoxComponent = function(config){
22852     Roo.Component.call(this, config);
22853     this.addEvents({
22854         /**
22855          * @event resize
22856          * Fires after the component is resized.
22857              * @param {Roo.Component} this
22858              * @param {Number} adjWidth The box-adjusted width that was set
22859              * @param {Number} adjHeight The box-adjusted height that was set
22860              * @param {Number} rawWidth The width that was originally specified
22861              * @param {Number} rawHeight The height that was originally specified
22862              */
22863         resize : true,
22864         /**
22865          * @event move
22866          * Fires after the component is moved.
22867              * @param {Roo.Component} this
22868              * @param {Number} x The new x position
22869              * @param {Number} y The new y position
22870              */
22871         move : true
22872     });
22873 };
22874
22875 Roo.extend(Roo.BoxComponent, Roo.Component, {
22876     // private, set in afterRender to signify that the component has been rendered
22877     boxReady : false,
22878     // private, used to defer height settings to subclasses
22879     deferHeight: false,
22880     /** @cfg {Number} width
22881      * width (optional) size of component
22882      */
22883      /** @cfg {Number} height
22884      * height (optional) size of component
22885      */
22886      
22887     /**
22888      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22889      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22890      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22891      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22892      * @return {Roo.BoxComponent} this
22893      */
22894     setSize : function(w, h){
22895         // support for standard size objects
22896         if(typeof w == 'object'){
22897             h = w.height;
22898             w = w.width;
22899         }
22900         // not rendered
22901         if(!this.boxReady){
22902             this.width = w;
22903             this.height = h;
22904             return this;
22905         }
22906
22907         // prevent recalcs when not needed
22908         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22909             return this;
22910         }
22911         this.lastSize = {width: w, height: h};
22912
22913         var adj = this.adjustSize(w, h);
22914         var aw = adj.width, ah = adj.height;
22915         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22916             var rz = this.getResizeEl();
22917             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22918                 rz.setSize(aw, ah);
22919             }else if(!this.deferHeight && ah !== undefined){
22920                 rz.setHeight(ah);
22921             }else if(aw !== undefined){
22922                 rz.setWidth(aw);
22923             }
22924             this.onResize(aw, ah, w, h);
22925             this.fireEvent('resize', this, aw, ah, w, h);
22926         }
22927         return this;
22928     },
22929
22930     /**
22931      * Gets the current size of the component's underlying element.
22932      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22933      */
22934     getSize : function(){
22935         return this.el.getSize();
22936     },
22937
22938     /**
22939      * Gets the current XY position of the component's underlying element.
22940      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22941      * @return {Array} The XY position of the element (e.g., [100, 200])
22942      */
22943     getPosition : function(local){
22944         if(local === true){
22945             return [this.el.getLeft(true), this.el.getTop(true)];
22946         }
22947         return this.xy || this.el.getXY();
22948     },
22949
22950     /**
22951      * Gets the current box measurements of the component's underlying element.
22952      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22953      * @returns {Object} box An object in the format {x, y, width, height}
22954      */
22955     getBox : function(local){
22956         var s = this.el.getSize();
22957         if(local){
22958             s.x = this.el.getLeft(true);
22959             s.y = this.el.getTop(true);
22960         }else{
22961             var xy = this.xy || this.el.getXY();
22962             s.x = xy[0];
22963             s.y = xy[1];
22964         }
22965         return s;
22966     },
22967
22968     /**
22969      * Sets the current box measurements of the component's underlying element.
22970      * @param {Object} box An object in the format {x, y, width, height}
22971      * @returns {Roo.BoxComponent} this
22972      */
22973     updateBox : function(box){
22974         this.setSize(box.width, box.height);
22975         this.setPagePosition(box.x, box.y);
22976         return this;
22977     },
22978
22979     // protected
22980     getResizeEl : function(){
22981         return this.resizeEl || this.el;
22982     },
22983
22984     // protected
22985     getPositionEl : function(){
22986         return this.positionEl || this.el;
22987     },
22988
22989     /**
22990      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22991      * This method fires the move event.
22992      * @param {Number} left The new left
22993      * @param {Number} top The new top
22994      * @returns {Roo.BoxComponent} this
22995      */
22996     setPosition : function(x, y){
22997         this.x = x;
22998         this.y = y;
22999         if(!this.boxReady){
23000             return this;
23001         }
23002         var adj = this.adjustPosition(x, y);
23003         var ax = adj.x, ay = adj.y;
23004
23005         var el = this.getPositionEl();
23006         if(ax !== undefined || ay !== undefined){
23007             if(ax !== undefined && ay !== undefined){
23008                 el.setLeftTop(ax, ay);
23009             }else if(ax !== undefined){
23010                 el.setLeft(ax);
23011             }else if(ay !== undefined){
23012                 el.setTop(ay);
23013             }
23014             this.onPosition(ax, ay);
23015             this.fireEvent('move', this, ax, ay);
23016         }
23017         return this;
23018     },
23019
23020     /**
23021      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23022      * This method fires the move event.
23023      * @param {Number} x The new x position
23024      * @param {Number} y The new y position
23025      * @returns {Roo.BoxComponent} this
23026      */
23027     setPagePosition : function(x, y){
23028         this.pageX = x;
23029         this.pageY = y;
23030         if(!this.boxReady){
23031             return;
23032         }
23033         if(x === undefined || y === undefined){ // cannot translate undefined points
23034             return;
23035         }
23036         var p = this.el.translatePoints(x, y);
23037         this.setPosition(p.left, p.top);
23038         return this;
23039     },
23040
23041     // private
23042     onRender : function(ct, position){
23043         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23044         if(this.resizeEl){
23045             this.resizeEl = Roo.get(this.resizeEl);
23046         }
23047         if(this.positionEl){
23048             this.positionEl = Roo.get(this.positionEl);
23049         }
23050     },
23051
23052     // private
23053     afterRender : function(){
23054         Roo.BoxComponent.superclass.afterRender.call(this);
23055         this.boxReady = true;
23056         this.setSize(this.width, this.height);
23057         if(this.x || this.y){
23058             this.setPosition(this.x, this.y);
23059         }
23060         if(this.pageX || this.pageY){
23061             this.setPagePosition(this.pageX, this.pageY);
23062         }
23063     },
23064
23065     /**
23066      * Force the component's size to recalculate based on the underlying element's current height and width.
23067      * @returns {Roo.BoxComponent} this
23068      */
23069     syncSize : function(){
23070         delete this.lastSize;
23071         this.setSize(this.el.getWidth(), this.el.getHeight());
23072         return this;
23073     },
23074
23075     /**
23076      * Called after the component is resized, this method is empty by default but can be implemented by any
23077      * subclass that needs to perform custom logic after a resize occurs.
23078      * @param {Number} adjWidth The box-adjusted width that was set
23079      * @param {Number} adjHeight The box-adjusted height that was set
23080      * @param {Number} rawWidth The width that was originally specified
23081      * @param {Number} rawHeight The height that was originally specified
23082      */
23083     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23084
23085     },
23086
23087     /**
23088      * Called after the component is moved, this method is empty by default but can be implemented by any
23089      * subclass that needs to perform custom logic after a move occurs.
23090      * @param {Number} x The new x position
23091      * @param {Number} y The new y position
23092      */
23093     onPosition : function(x, y){
23094
23095     },
23096
23097     // private
23098     adjustSize : function(w, h){
23099         if(this.autoWidth){
23100             w = 'auto';
23101         }
23102         if(this.autoHeight){
23103             h = 'auto';
23104         }
23105         return {width : w, height: h};
23106     },
23107
23108     // private
23109     adjustPosition : function(x, y){
23110         return {x : x, y: y};
23111     }
23112 });/*
23113  * Based on:
23114  * Ext JS Library 1.1.1
23115  * Copyright(c) 2006-2007, Ext JS, LLC.
23116  *
23117  * Originally Released Under LGPL - original licence link has changed is not relivant.
23118  *
23119  * Fork - LGPL
23120  * <script type="text/javascript">
23121  */
23122
23123
23124 /**
23125  * @class Roo.SplitBar
23126  * @extends Roo.util.Observable
23127  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23128  * <br><br>
23129  * Usage:
23130  * <pre><code>
23131 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23132                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23133 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23134 split.minSize = 100;
23135 split.maxSize = 600;
23136 split.animate = true;
23137 split.on('moved', splitterMoved);
23138 </code></pre>
23139  * @constructor
23140  * Create a new SplitBar
23141  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23142  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23143  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23144  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23145                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23146                         position of the SplitBar).
23147  */
23148 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23149     
23150     /** @private */
23151     this.el = Roo.get(dragElement, true);
23152     this.el.dom.unselectable = "on";
23153     /** @private */
23154     this.resizingEl = Roo.get(resizingElement, true);
23155
23156     /**
23157      * @private
23158      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23159      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23160      * @type Number
23161      */
23162     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23163     
23164     /**
23165      * The minimum size of the resizing element. (Defaults to 0)
23166      * @type Number
23167      */
23168     this.minSize = 0;
23169     
23170     /**
23171      * The maximum size of the resizing element. (Defaults to 2000)
23172      * @type Number
23173      */
23174     this.maxSize = 2000;
23175     
23176     /**
23177      * Whether to animate the transition to the new size
23178      * @type Boolean
23179      */
23180     this.animate = false;
23181     
23182     /**
23183      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23184      * @type Boolean
23185      */
23186     this.useShim = false;
23187     
23188     /** @private */
23189     this.shim = null;
23190     
23191     if(!existingProxy){
23192         /** @private */
23193         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23194     }else{
23195         this.proxy = Roo.get(existingProxy).dom;
23196     }
23197     /** @private */
23198     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23199     
23200     /** @private */
23201     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23202     
23203     /** @private */
23204     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23205     
23206     /** @private */
23207     this.dragSpecs = {};
23208     
23209     /**
23210      * @private The adapter to use to positon and resize elements
23211      */
23212     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23213     this.adapter.init(this);
23214     
23215     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23216         /** @private */
23217         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23218         this.el.addClass("x-splitbar-h");
23219     }else{
23220         /** @private */
23221         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23222         this.el.addClass("x-splitbar-v");
23223     }
23224     
23225     this.addEvents({
23226         /**
23227          * @event resize
23228          * Fires when the splitter is moved (alias for {@link #event-moved})
23229          * @param {Roo.SplitBar} this
23230          * @param {Number} newSize the new width or height
23231          */
23232         "resize" : true,
23233         /**
23234          * @event moved
23235          * Fires when the splitter is moved
23236          * @param {Roo.SplitBar} this
23237          * @param {Number} newSize the new width or height
23238          */
23239         "moved" : true,
23240         /**
23241          * @event beforeresize
23242          * Fires before the splitter is dragged
23243          * @param {Roo.SplitBar} this
23244          */
23245         "beforeresize" : true,
23246
23247         "beforeapply" : true
23248     });
23249
23250     Roo.util.Observable.call(this);
23251 };
23252
23253 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23254     onStartProxyDrag : function(x, y){
23255         this.fireEvent("beforeresize", this);
23256         if(!this.overlay){
23257             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23258             o.unselectable();
23259             o.enableDisplayMode("block");
23260             // all splitbars share the same overlay
23261             Roo.SplitBar.prototype.overlay = o;
23262         }
23263         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23264         this.overlay.show();
23265         Roo.get(this.proxy).setDisplayed("block");
23266         var size = this.adapter.getElementSize(this);
23267         this.activeMinSize = this.getMinimumSize();;
23268         this.activeMaxSize = this.getMaximumSize();;
23269         var c1 = size - this.activeMinSize;
23270         var c2 = Math.max(this.activeMaxSize - size, 0);
23271         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23272             this.dd.resetConstraints();
23273             this.dd.setXConstraint(
23274                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23275                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23276             );
23277             this.dd.setYConstraint(0, 0);
23278         }else{
23279             this.dd.resetConstraints();
23280             this.dd.setXConstraint(0, 0);
23281             this.dd.setYConstraint(
23282                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23283                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23284             );
23285          }
23286         this.dragSpecs.startSize = size;
23287         this.dragSpecs.startPoint = [x, y];
23288         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23289     },
23290     
23291     /** 
23292      * @private Called after the drag operation by the DDProxy
23293      */
23294     onEndProxyDrag : function(e){
23295         Roo.get(this.proxy).setDisplayed(false);
23296         var endPoint = Roo.lib.Event.getXY(e);
23297         if(this.overlay){
23298             this.overlay.hide();
23299         }
23300         var newSize;
23301         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23302             newSize = this.dragSpecs.startSize + 
23303                 (this.placement == Roo.SplitBar.LEFT ?
23304                     endPoint[0] - this.dragSpecs.startPoint[0] :
23305                     this.dragSpecs.startPoint[0] - endPoint[0]
23306                 );
23307         }else{
23308             newSize = this.dragSpecs.startSize + 
23309                 (this.placement == Roo.SplitBar.TOP ?
23310                     endPoint[1] - this.dragSpecs.startPoint[1] :
23311                     this.dragSpecs.startPoint[1] - endPoint[1]
23312                 );
23313         }
23314         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23315         if(newSize != this.dragSpecs.startSize){
23316             if(this.fireEvent('beforeapply', this, newSize) !== false){
23317                 this.adapter.setElementSize(this, newSize);
23318                 this.fireEvent("moved", this, newSize);
23319                 this.fireEvent("resize", this, newSize);
23320             }
23321         }
23322     },
23323     
23324     /**
23325      * Get the adapter this SplitBar uses
23326      * @return The adapter object
23327      */
23328     getAdapter : function(){
23329         return this.adapter;
23330     },
23331     
23332     /**
23333      * Set the adapter this SplitBar uses
23334      * @param {Object} adapter A SplitBar adapter object
23335      */
23336     setAdapter : function(adapter){
23337         this.adapter = adapter;
23338         this.adapter.init(this);
23339     },
23340     
23341     /**
23342      * Gets the minimum size for the resizing element
23343      * @return {Number} The minimum size
23344      */
23345     getMinimumSize : function(){
23346         return this.minSize;
23347     },
23348     
23349     /**
23350      * Sets the minimum size for the resizing element
23351      * @param {Number} minSize The minimum size
23352      */
23353     setMinimumSize : function(minSize){
23354         this.minSize = minSize;
23355     },
23356     
23357     /**
23358      * Gets the maximum size for the resizing element
23359      * @return {Number} The maximum size
23360      */
23361     getMaximumSize : function(){
23362         return this.maxSize;
23363     },
23364     
23365     /**
23366      * Sets the maximum size for the resizing element
23367      * @param {Number} maxSize The maximum size
23368      */
23369     setMaximumSize : function(maxSize){
23370         this.maxSize = maxSize;
23371     },
23372     
23373     /**
23374      * Sets the initialize size for the resizing element
23375      * @param {Number} size The initial size
23376      */
23377     setCurrentSize : function(size){
23378         var oldAnimate = this.animate;
23379         this.animate = false;
23380         this.adapter.setElementSize(this, size);
23381         this.animate = oldAnimate;
23382     },
23383     
23384     /**
23385      * Destroy this splitbar. 
23386      * @param {Boolean} removeEl True to remove the element
23387      */
23388     destroy : function(removeEl){
23389         if(this.shim){
23390             this.shim.remove();
23391         }
23392         this.dd.unreg();
23393         this.proxy.parentNode.removeChild(this.proxy);
23394         if(removeEl){
23395             this.el.remove();
23396         }
23397     }
23398 });
23399
23400 /**
23401  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23402  */
23403 Roo.SplitBar.createProxy = function(dir){
23404     var proxy = new Roo.Element(document.createElement("div"));
23405     proxy.unselectable();
23406     var cls = 'x-splitbar-proxy';
23407     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23408     document.body.appendChild(proxy.dom);
23409     return proxy.dom;
23410 };
23411
23412 /** 
23413  * @class Roo.SplitBar.BasicLayoutAdapter
23414  * Default Adapter. It assumes the splitter and resizing element are not positioned
23415  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23416  */
23417 Roo.SplitBar.BasicLayoutAdapter = function(){
23418 };
23419
23420 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23421     // do nothing for now
23422     init : function(s){
23423     
23424     },
23425     /**
23426      * Called before drag operations to get the current size of the resizing element. 
23427      * @param {Roo.SplitBar} s The SplitBar using this adapter
23428      */
23429      getElementSize : function(s){
23430         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23431             return s.resizingEl.getWidth();
23432         }else{
23433             return s.resizingEl.getHeight();
23434         }
23435     },
23436     
23437     /**
23438      * Called after drag operations to set the size of the resizing element.
23439      * @param {Roo.SplitBar} s The SplitBar using this adapter
23440      * @param {Number} newSize The new size to set
23441      * @param {Function} onComplete A function to be invoked when resizing is complete
23442      */
23443     setElementSize : function(s, newSize, onComplete){
23444         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23445             if(!s.animate){
23446                 s.resizingEl.setWidth(newSize);
23447                 if(onComplete){
23448                     onComplete(s, newSize);
23449                 }
23450             }else{
23451                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23452             }
23453         }else{
23454             
23455             if(!s.animate){
23456                 s.resizingEl.setHeight(newSize);
23457                 if(onComplete){
23458                     onComplete(s, newSize);
23459                 }
23460             }else{
23461                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23462             }
23463         }
23464     }
23465 };
23466
23467 /** 
23468  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23469  * @extends Roo.SplitBar.BasicLayoutAdapter
23470  * Adapter that  moves the splitter element to align with the resized sizing element. 
23471  * Used with an absolute positioned SplitBar.
23472  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23473  * document.body, make sure you assign an id to the body element.
23474  */
23475 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23476     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23477     this.container = Roo.get(container);
23478 };
23479
23480 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23481     init : function(s){
23482         this.basic.init(s);
23483     },
23484     
23485     getElementSize : function(s){
23486         return this.basic.getElementSize(s);
23487     },
23488     
23489     setElementSize : function(s, newSize, onComplete){
23490         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23491     },
23492     
23493     moveSplitter : function(s){
23494         var yes = Roo.SplitBar;
23495         switch(s.placement){
23496             case yes.LEFT:
23497                 s.el.setX(s.resizingEl.getRight());
23498                 break;
23499             case yes.RIGHT:
23500                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23501                 break;
23502             case yes.TOP:
23503                 s.el.setY(s.resizingEl.getBottom());
23504                 break;
23505             case yes.BOTTOM:
23506                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23507                 break;
23508         }
23509     }
23510 };
23511
23512 /**
23513  * Orientation constant - Create a vertical SplitBar
23514  * @static
23515  * @type Number
23516  */
23517 Roo.SplitBar.VERTICAL = 1;
23518
23519 /**
23520  * Orientation constant - Create a horizontal SplitBar
23521  * @static
23522  * @type Number
23523  */
23524 Roo.SplitBar.HORIZONTAL = 2;
23525
23526 /**
23527  * Placement constant - The resizing element is to the left of the splitter element
23528  * @static
23529  * @type Number
23530  */
23531 Roo.SplitBar.LEFT = 1;
23532
23533 /**
23534  * Placement constant - The resizing element is to the right of the splitter element
23535  * @static
23536  * @type Number
23537  */
23538 Roo.SplitBar.RIGHT = 2;
23539
23540 /**
23541  * Placement constant - The resizing element is positioned above the splitter element
23542  * @static
23543  * @type Number
23544  */
23545 Roo.SplitBar.TOP = 3;
23546
23547 /**
23548  * Placement constant - The resizing element is positioned under splitter element
23549  * @static
23550  * @type Number
23551  */
23552 Roo.SplitBar.BOTTOM = 4;
23553 /*
23554  * Based on:
23555  * Ext JS Library 1.1.1
23556  * Copyright(c) 2006-2007, Ext JS, LLC.
23557  *
23558  * Originally Released Under LGPL - original licence link has changed is not relivant.
23559  *
23560  * Fork - LGPL
23561  * <script type="text/javascript">
23562  */
23563
23564 /**
23565  * @class Roo.View
23566  * @extends Roo.util.Observable
23567  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23568  * This class also supports single and multi selection modes. <br>
23569  * Create a data model bound view:
23570  <pre><code>
23571  var store = new Roo.data.Store(...);
23572
23573  var view = new Roo.View({
23574     el : "my-element",
23575     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23576  
23577     singleSelect: true,
23578     selectedClass: "ydataview-selected",
23579     store: store
23580  });
23581
23582  // listen for node click?
23583  view.on("click", function(vw, index, node, e){
23584  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23585  });
23586
23587  // load XML data
23588  dataModel.load("foobar.xml");
23589  </code></pre>
23590  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23591  * <br><br>
23592  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23593  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23594  * 
23595  * Note: old style constructor is still suported (container, template, config)
23596  * 
23597  * @constructor
23598  * Create a new View
23599  * @param {Object} config The config object
23600  * 
23601  */
23602 Roo.View = function(config, depreciated_tpl, depreciated_config){
23603     
23604     if (typeof(depreciated_tpl) == 'undefined') {
23605         // new way.. - universal constructor.
23606         Roo.apply(this, config);
23607         this.el  = Roo.get(this.el);
23608     } else {
23609         // old format..
23610         this.el  = Roo.get(config);
23611         this.tpl = depreciated_tpl;
23612         Roo.apply(this, depreciated_config);
23613     }
23614      
23615     
23616     if(typeof(this.tpl) == "string"){
23617         this.tpl = new Roo.Template(this.tpl);
23618     } else {
23619         // support xtype ctors..
23620         this.tpl = new Roo.factory(this.tpl, Roo);
23621     }
23622     
23623     
23624     this.tpl.compile();
23625    
23626
23627      
23628     /** @private */
23629     this.addEvents({
23630         /**
23631          * @event beforeclick
23632          * Fires before a click is processed. Returns false to cancel the default action.
23633          * @param {Roo.View} this
23634          * @param {Number} index The index of the target node
23635          * @param {HTMLElement} node The target node
23636          * @param {Roo.EventObject} e The raw event object
23637          */
23638             "beforeclick" : true,
23639         /**
23640          * @event click
23641          * Fires when a template node is clicked.
23642          * @param {Roo.View} this
23643          * @param {Number} index The index of the target node
23644          * @param {HTMLElement} node The target node
23645          * @param {Roo.EventObject} e The raw event object
23646          */
23647             "click" : true,
23648         /**
23649          * @event dblclick
23650          * Fires when a template node is double clicked.
23651          * @param {Roo.View} this
23652          * @param {Number} index The index of the target node
23653          * @param {HTMLElement} node The target node
23654          * @param {Roo.EventObject} e The raw event object
23655          */
23656             "dblclick" : true,
23657         /**
23658          * @event contextmenu
23659          * Fires when a template node is right clicked.
23660          * @param {Roo.View} this
23661          * @param {Number} index The index of the target node
23662          * @param {HTMLElement} node The target node
23663          * @param {Roo.EventObject} e The raw event object
23664          */
23665             "contextmenu" : true,
23666         /**
23667          * @event selectionchange
23668          * Fires when the selected nodes change.
23669          * @param {Roo.View} this
23670          * @param {Array} selections Array of the selected nodes
23671          */
23672             "selectionchange" : true,
23673     
23674         /**
23675          * @event beforeselect
23676          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23677          * @param {Roo.View} this
23678          * @param {HTMLElement} node The node to be selected
23679          * @param {Array} selections Array of currently selected nodes
23680          */
23681             "beforeselect" : true,
23682         /**
23683          * @event preparedata
23684          * Fires on every row to render, to allow you to change the data.
23685          * @param {Roo.View} this
23686          * @param {Object} data to be rendered (change this)
23687          */
23688           "preparedata" : true
23689         });
23690
23691     this.el.on({
23692         "click": this.onClick,
23693         "dblclick": this.onDblClick,
23694         "contextmenu": this.onContextMenu,
23695         scope:this
23696     });
23697
23698     this.selections = [];
23699     this.nodes = [];
23700     this.cmp = new Roo.CompositeElementLite([]);
23701     if(this.store){
23702         this.store = Roo.factory(this.store, Roo.data);
23703         this.setStore(this.store, true);
23704     }
23705     Roo.View.superclass.constructor.call(this);
23706 };
23707
23708 Roo.extend(Roo.View, Roo.util.Observable, {
23709     
23710      /**
23711      * @cfg {Roo.data.Store} store Data store to load data from.
23712      */
23713     store : false,
23714     
23715     /**
23716      * @cfg {String|Roo.Element} el The container element.
23717      */
23718     el : '',
23719     
23720     /**
23721      * @cfg {String|Roo.Template} tpl The template used by this View 
23722      */
23723     tpl : false,
23724     
23725     /**
23726      * @cfg {String} selectedClass The css class to add to selected nodes
23727      */
23728     selectedClass : "x-view-selected",
23729      /**
23730      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23731      */
23732     emptyText : "",
23733     /**
23734      * @cfg {Boolean} multiSelect Allow multiple selection
23735      */
23736     multiSelect : false,
23737     /**
23738      * @cfg {Boolean} singleSelect Allow single selection
23739      */
23740     singleSelect:  false,
23741     
23742     /**
23743      * @cfg {Boolean} toggleSelect - selecting 
23744      */
23745     toggleSelect : false,
23746     
23747     /**
23748      * Returns the element this view is bound to.
23749      * @return {Roo.Element}
23750      */
23751     getEl : function(){
23752         return this.el;
23753     },
23754
23755     /**
23756      * Refreshes the view.
23757      */
23758     refresh : function(){
23759         var t = this.tpl;
23760         this.clearSelections();
23761         this.el.update("");
23762         var html = [];
23763         var records = this.store.getRange();
23764         if(records.length < 1){
23765             this.el.update(this.emptyText);
23766             return;
23767         }
23768         for(var i = 0, len = records.length; i < len; i++){
23769             var data = this.prepareData(records[i].data, i, records[i]);
23770             this.fireEvent("preparedata", this, data, i, records[i]);
23771             html[html.length] = t.apply(data);
23772         }
23773         this.el.update(html.join(""));
23774         this.nodes = this.el.dom.childNodes;
23775         this.updateIndexes(0);
23776     },
23777
23778     /**
23779      * Function to override to reformat the data that is sent to
23780      * the template for each node.
23781      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23782      * a JSON object for an UpdateManager bound view).
23783      */
23784     prepareData : function(data){
23785         return data;
23786     },
23787
23788     onUpdate : function(ds, record){
23789         this.clearSelections();
23790         var index = this.store.indexOf(record);
23791         var n = this.nodes[index];
23792         this.tpl.insertBefore(n, this.prepareData(record.data));
23793         n.parentNode.removeChild(n);
23794         this.updateIndexes(index, index);
23795     },
23796
23797     onAdd : function(ds, records, index){
23798         this.clearSelections();
23799         if(this.nodes.length == 0){
23800             this.refresh();
23801             return;
23802         }
23803         var n = this.nodes[index];
23804         for(var i = 0, len = records.length; i < len; i++){
23805             var d = this.prepareData(records[i].data);
23806             if(n){
23807                 this.tpl.insertBefore(n, d);
23808             }else{
23809                 this.tpl.append(this.el, d);
23810             }
23811         }
23812         this.updateIndexes(index);
23813     },
23814
23815     onRemove : function(ds, record, index){
23816         this.clearSelections();
23817         this.el.dom.removeChild(this.nodes[index]);
23818         this.updateIndexes(index);
23819     },
23820
23821     /**
23822      * Refresh an individual node.
23823      * @param {Number} index
23824      */
23825     refreshNode : function(index){
23826         this.onUpdate(this.store, this.store.getAt(index));
23827     },
23828
23829     updateIndexes : function(startIndex, endIndex){
23830         var ns = this.nodes;
23831         startIndex = startIndex || 0;
23832         endIndex = endIndex || ns.length - 1;
23833         for(var i = startIndex; i <= endIndex; i++){
23834             ns[i].nodeIndex = i;
23835         }
23836     },
23837
23838     /**
23839      * Changes the data store this view uses and refresh the view.
23840      * @param {Store} store
23841      */
23842     setStore : function(store, initial){
23843         if(!initial && this.store){
23844             this.store.un("datachanged", this.refresh);
23845             this.store.un("add", this.onAdd);
23846             this.store.un("remove", this.onRemove);
23847             this.store.un("update", this.onUpdate);
23848             this.store.un("clear", this.refresh);
23849         }
23850         if(store){
23851           
23852             store.on("datachanged", this.refresh, this);
23853             store.on("add", this.onAdd, this);
23854             store.on("remove", this.onRemove, this);
23855             store.on("update", this.onUpdate, this);
23856             store.on("clear", this.refresh, this);
23857         }
23858         
23859         if(store){
23860             this.refresh();
23861         }
23862     },
23863
23864     /**
23865      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23866      * @param {HTMLElement} node
23867      * @return {HTMLElement} The template node
23868      */
23869     findItemFromChild : function(node){
23870         var el = this.el.dom;
23871         if(!node || node.parentNode == el){
23872                     return node;
23873             }
23874             var p = node.parentNode;
23875             while(p && p != el){
23876             if(p.parentNode == el){
23877                 return p;
23878             }
23879             p = p.parentNode;
23880         }
23881             return null;
23882     },
23883
23884     /** @ignore */
23885     onClick : function(e){
23886         var item = this.findItemFromChild(e.getTarget());
23887         if(item){
23888             var index = this.indexOf(item);
23889             if(this.onItemClick(item, index, e) !== false){
23890                 this.fireEvent("click", this, index, item, e);
23891             }
23892         }else{
23893             this.clearSelections();
23894         }
23895     },
23896
23897     /** @ignore */
23898     onContextMenu : function(e){
23899         var item = this.findItemFromChild(e.getTarget());
23900         if(item){
23901             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23902         }
23903     },
23904
23905     /** @ignore */
23906     onDblClick : function(e){
23907         var item = this.findItemFromChild(e.getTarget());
23908         if(item){
23909             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23910         }
23911     },
23912
23913     onItemClick : function(item, index, e)
23914     {
23915         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23916             return false;
23917         }
23918         if (this.toggleSelect) {
23919             var m = this.isSelected(item) ? 'unselect' : 'select';
23920             Roo.log(m);
23921             var _t = this;
23922             _t[m](item, true, false);
23923             return true;
23924         }
23925         if(this.multiSelect || this.singleSelect){
23926             if(this.multiSelect && e.shiftKey && this.lastSelection){
23927                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23928             }else{
23929                 this.select(item, this.multiSelect && e.ctrlKey);
23930                 this.lastSelection = item;
23931             }
23932             e.preventDefault();
23933         }
23934         return true;
23935     },
23936
23937     /**
23938      * Get the number of selected nodes.
23939      * @return {Number}
23940      */
23941     getSelectionCount : function(){
23942         return this.selections.length;
23943     },
23944
23945     /**
23946      * Get the currently selected nodes.
23947      * @return {Array} An array of HTMLElements
23948      */
23949     getSelectedNodes : function(){
23950         return this.selections;
23951     },
23952
23953     /**
23954      * Get the indexes of the selected nodes.
23955      * @return {Array}
23956      */
23957     getSelectedIndexes : function(){
23958         var indexes = [], s = this.selections;
23959         for(var i = 0, len = s.length; i < len; i++){
23960             indexes.push(s[i].nodeIndex);
23961         }
23962         return indexes;
23963     },
23964
23965     /**
23966      * Clear all selections
23967      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23968      */
23969     clearSelections : function(suppressEvent){
23970         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23971             this.cmp.elements = this.selections;
23972             this.cmp.removeClass(this.selectedClass);
23973             this.selections = [];
23974             if(!suppressEvent){
23975                 this.fireEvent("selectionchange", this, this.selections);
23976             }
23977         }
23978     },
23979
23980     /**
23981      * Returns true if the passed node is selected
23982      * @param {HTMLElement/Number} node The node or node index
23983      * @return {Boolean}
23984      */
23985     isSelected : function(node){
23986         var s = this.selections;
23987         if(s.length < 1){
23988             return false;
23989         }
23990         node = this.getNode(node);
23991         return s.indexOf(node) !== -1;
23992     },
23993
23994     /**
23995      * Selects nodes.
23996      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
23997      * @param {Boolean} keepExisting (optional) true to keep existing selections
23998      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23999      */
24000     select : function(nodeInfo, keepExisting, suppressEvent){
24001         if(nodeInfo instanceof Array){
24002             if(!keepExisting){
24003                 this.clearSelections(true);
24004             }
24005             for(var i = 0, len = nodeInfo.length; i < len; i++){
24006                 this.select(nodeInfo[i], true, true);
24007             }
24008             return;
24009         } 
24010         var node = this.getNode(nodeInfo);
24011         if(!node || this.isSelected(node)){
24012             return; // already selected.
24013         }
24014         if(!keepExisting){
24015             this.clearSelections(true);
24016         }
24017         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24018             Roo.fly(node).addClass(this.selectedClass);
24019             this.selections.push(node);
24020             if(!suppressEvent){
24021                 this.fireEvent("selectionchange", this, this.selections);
24022             }
24023         }
24024         
24025         
24026     },
24027       /**
24028      * Unselects nodes.
24029      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
24030      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24031      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24032      */
24033     unselect : function(nodeInfo, keepExisting, suppressEvent)
24034     {
24035         if(nodeInfo instanceof Array){
24036             Roo.each(this.selections, function(s) {
24037                 this.unselect(s, nodeInfo);
24038             }, this);
24039             return;
24040         }
24041         var node = this.getNode(nodeInfo);
24042         if(!node || !this.isSelected(node)){
24043             Roo.log("not selected");
24044             return; // not selected.
24045         }
24046         // fireevent???
24047         var ns = [];
24048         Roo.each(this.selections, function(s) {
24049             if (s == node ) {
24050                 Roo.fly(node).removeClass(this.selectedClass);
24051
24052                 return;
24053             }
24054             ns.push(s);
24055         },this);
24056         
24057         this.selections= ns;
24058         this.fireEvent("selectionchange", this, this.selections);
24059     },
24060
24061     /**
24062      * Gets a template node.
24063      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24064      * @return {HTMLElement} The node or null if it wasn't found
24065      */
24066     getNode : function(nodeInfo){
24067         if(typeof nodeInfo == "string"){
24068             return document.getElementById(nodeInfo);
24069         }else if(typeof nodeInfo == "number"){
24070             return this.nodes[nodeInfo];
24071         }
24072         return nodeInfo;
24073     },
24074
24075     /**
24076      * Gets a range template nodes.
24077      * @param {Number} startIndex
24078      * @param {Number} endIndex
24079      * @return {Array} An array of nodes
24080      */
24081     getNodes : function(start, end){
24082         var ns = this.nodes;
24083         start = start || 0;
24084         end = typeof end == "undefined" ? ns.length - 1 : end;
24085         var nodes = [];
24086         if(start <= end){
24087             for(var i = start; i <= end; i++){
24088                 nodes.push(ns[i]);
24089             }
24090         } else{
24091             for(var i = start; i >= end; i--){
24092                 nodes.push(ns[i]);
24093             }
24094         }
24095         return nodes;
24096     },
24097
24098     /**
24099      * Finds the index of the passed node
24100      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24101      * @return {Number} The index of the node or -1
24102      */
24103     indexOf : function(node){
24104         node = this.getNode(node);
24105         if(typeof node.nodeIndex == "number"){
24106             return node.nodeIndex;
24107         }
24108         var ns = this.nodes;
24109         for(var i = 0, len = ns.length; i < len; i++){
24110             if(ns[i] == node){
24111                 return i;
24112             }
24113         }
24114         return -1;
24115     }
24116 });
24117 /*
24118  * Based on:
24119  * Ext JS Library 1.1.1
24120  * Copyright(c) 2006-2007, Ext JS, LLC.
24121  *
24122  * Originally Released Under LGPL - original licence link has changed is not relivant.
24123  *
24124  * Fork - LGPL
24125  * <script type="text/javascript">
24126  */
24127
24128 /**
24129  * @class Roo.JsonView
24130  * @extends Roo.View
24131  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24132 <pre><code>
24133 var view = new Roo.JsonView({
24134     container: "my-element",
24135     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24136     multiSelect: true, 
24137     jsonRoot: "data" 
24138 });
24139
24140 // listen for node click?
24141 view.on("click", function(vw, index, node, e){
24142     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24143 });
24144
24145 // direct load of JSON data
24146 view.load("foobar.php");
24147
24148 // Example from my blog list
24149 var tpl = new Roo.Template(
24150     '&lt;div class="entry"&gt;' +
24151     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24152     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24153     "&lt;/div&gt;&lt;hr /&gt;"
24154 );
24155
24156 var moreView = new Roo.JsonView({
24157     container :  "entry-list", 
24158     template : tpl,
24159     jsonRoot: "posts"
24160 });
24161 moreView.on("beforerender", this.sortEntries, this);
24162 moreView.load({
24163     url: "/blog/get-posts.php",
24164     params: "allposts=true",
24165     text: "Loading Blog Entries..."
24166 });
24167 </code></pre>
24168
24169 * Note: old code is supported with arguments : (container, template, config)
24170
24171
24172  * @constructor
24173  * Create a new JsonView
24174  * 
24175  * @param {Object} config The config object
24176  * 
24177  */
24178 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24179     
24180     
24181     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24182
24183     var um = this.el.getUpdateManager();
24184     um.setRenderer(this);
24185     um.on("update", this.onLoad, this);
24186     um.on("failure", this.onLoadException, this);
24187
24188     /**
24189      * @event beforerender
24190      * Fires before rendering of the downloaded JSON data.
24191      * @param {Roo.JsonView} this
24192      * @param {Object} data The JSON data loaded
24193      */
24194     /**
24195      * @event load
24196      * Fires when data is loaded.
24197      * @param {Roo.JsonView} this
24198      * @param {Object} data The JSON data loaded
24199      * @param {Object} response The raw Connect response object
24200      */
24201     /**
24202      * @event loadexception
24203      * Fires when loading fails.
24204      * @param {Roo.JsonView} this
24205      * @param {Object} response The raw Connect response object
24206      */
24207     this.addEvents({
24208         'beforerender' : true,
24209         'load' : true,
24210         'loadexception' : true
24211     });
24212 };
24213 Roo.extend(Roo.JsonView, Roo.View, {
24214     /**
24215      * @type {String} The root property in the loaded JSON object that contains the data
24216      */
24217     jsonRoot : "",
24218
24219     /**
24220      * Refreshes the view.
24221      */
24222     refresh : function(){
24223         this.clearSelections();
24224         this.el.update("");
24225         var html = [];
24226         var o = this.jsonData;
24227         if(o && o.length > 0){
24228             for(var i = 0, len = o.length; i < len; i++){
24229                 var data = this.prepareData(o[i], i, o);
24230                 html[html.length] = this.tpl.apply(data);
24231             }
24232         }else{
24233             html.push(this.emptyText);
24234         }
24235         this.el.update(html.join(""));
24236         this.nodes = this.el.dom.childNodes;
24237         this.updateIndexes(0);
24238     },
24239
24240     /**
24241      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
24242      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
24243      <pre><code>
24244      view.load({
24245          url: "your-url.php",
24246          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24247          callback: yourFunction,
24248          scope: yourObject, //(optional scope)
24249          discardUrl: false,
24250          nocache: false,
24251          text: "Loading...",
24252          timeout: 30,
24253          scripts: false
24254      });
24255      </code></pre>
24256      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24257      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24258      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24259      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24260      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24261      */
24262     load : function(){
24263         var um = this.el.getUpdateManager();
24264         um.update.apply(um, arguments);
24265     },
24266
24267     render : function(el, response){
24268         this.clearSelections();
24269         this.el.update("");
24270         var o;
24271         try{
24272             o = Roo.util.JSON.decode(response.responseText);
24273             if(this.jsonRoot){
24274                 
24275                 o = o[this.jsonRoot];
24276             }
24277         } catch(e){
24278         }
24279         /**
24280          * The current JSON data or null
24281          */
24282         this.jsonData = o;
24283         this.beforeRender();
24284         this.refresh();
24285     },
24286
24287 /**
24288  * Get the number of records in the current JSON dataset
24289  * @return {Number}
24290  */
24291     getCount : function(){
24292         return this.jsonData ? this.jsonData.length : 0;
24293     },
24294
24295 /**
24296  * Returns the JSON object for the specified node(s)
24297  * @param {HTMLElement/Array} node The node or an array of nodes
24298  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24299  * you get the JSON object for the node
24300  */
24301     getNodeData : function(node){
24302         if(node instanceof Array){
24303             var data = [];
24304             for(var i = 0, len = node.length; i < len; i++){
24305                 data.push(this.getNodeData(node[i]));
24306             }
24307             return data;
24308         }
24309         return this.jsonData[this.indexOf(node)] || null;
24310     },
24311
24312     beforeRender : function(){
24313         this.snapshot = this.jsonData;
24314         if(this.sortInfo){
24315             this.sort.apply(this, this.sortInfo);
24316         }
24317         this.fireEvent("beforerender", this, this.jsonData);
24318     },
24319
24320     onLoad : function(el, o){
24321         this.fireEvent("load", this, this.jsonData, o);
24322     },
24323
24324     onLoadException : function(el, o){
24325         this.fireEvent("loadexception", this, o);
24326     },
24327
24328 /**
24329  * Filter the data by a specific property.
24330  * @param {String} property A property on your JSON objects
24331  * @param {String/RegExp} value Either string that the property values
24332  * should start with, or a RegExp to test against the property
24333  */
24334     filter : function(property, value){
24335         if(this.jsonData){
24336             var data = [];
24337             var ss = this.snapshot;
24338             if(typeof value == "string"){
24339                 var vlen = value.length;
24340                 if(vlen == 0){
24341                     this.clearFilter();
24342                     return;
24343                 }
24344                 value = value.toLowerCase();
24345                 for(var i = 0, len = ss.length; i < len; i++){
24346                     var o = ss[i];
24347                     if(o[property].substr(0, vlen).toLowerCase() == value){
24348                         data.push(o);
24349                     }
24350                 }
24351             } else if(value.exec){ // regex?
24352                 for(var i = 0, len = ss.length; i < len; i++){
24353                     var o = ss[i];
24354                     if(value.test(o[property])){
24355                         data.push(o);
24356                     }
24357                 }
24358             } else{
24359                 return;
24360             }
24361             this.jsonData = data;
24362             this.refresh();
24363         }
24364     },
24365
24366 /**
24367  * Filter by a function. The passed function will be called with each
24368  * object in the current dataset. If the function returns true the value is kept,
24369  * otherwise it is filtered.
24370  * @param {Function} fn
24371  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24372  */
24373     filterBy : function(fn, scope){
24374         if(this.jsonData){
24375             var data = [];
24376             var ss = this.snapshot;
24377             for(var i = 0, len = ss.length; i < len; i++){
24378                 var o = ss[i];
24379                 if(fn.call(scope || this, o)){
24380                     data.push(o);
24381                 }
24382             }
24383             this.jsonData = data;
24384             this.refresh();
24385         }
24386     },
24387
24388 /**
24389  * Clears the current filter.
24390  */
24391     clearFilter : function(){
24392         if(this.snapshot && this.jsonData != this.snapshot){
24393             this.jsonData = this.snapshot;
24394             this.refresh();
24395         }
24396     },
24397
24398
24399 /**
24400  * Sorts the data for this view and refreshes it.
24401  * @param {String} property A property on your JSON objects to sort on
24402  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24403  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24404  */
24405     sort : function(property, dir, sortType){
24406         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24407         if(this.jsonData){
24408             var p = property;
24409             var dsc = dir && dir.toLowerCase() == "desc";
24410             var f = function(o1, o2){
24411                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24412                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24413                 ;
24414                 if(v1 < v2){
24415                     return dsc ? +1 : -1;
24416                 } else if(v1 > v2){
24417                     return dsc ? -1 : +1;
24418                 } else{
24419                     return 0;
24420                 }
24421             };
24422             this.jsonData.sort(f);
24423             this.refresh();
24424             if(this.jsonData != this.snapshot){
24425                 this.snapshot.sort(f);
24426             }
24427         }
24428     }
24429 });/*
24430  * Based on:
24431  * Ext JS Library 1.1.1
24432  * Copyright(c) 2006-2007, Ext JS, LLC.
24433  *
24434  * Originally Released Under LGPL - original licence link has changed is not relivant.
24435  *
24436  * Fork - LGPL
24437  * <script type="text/javascript">
24438  */
24439  
24440
24441 /**
24442  * @class Roo.ColorPalette
24443  * @extends Roo.Component
24444  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24445  * Here's an example of typical usage:
24446  * <pre><code>
24447 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24448 cp.render('my-div');
24449
24450 cp.on('select', function(palette, selColor){
24451     // do something with selColor
24452 });
24453 </code></pre>
24454  * @constructor
24455  * Create a new ColorPalette
24456  * @param {Object} config The config object
24457  */
24458 Roo.ColorPalette = function(config){
24459     Roo.ColorPalette.superclass.constructor.call(this, config);
24460     this.addEvents({
24461         /**
24462              * @event select
24463              * Fires when a color is selected
24464              * @param {ColorPalette} this
24465              * @param {String} color The 6-digit color hex code (without the # symbol)
24466              */
24467         select: true
24468     });
24469
24470     if(this.handler){
24471         this.on("select", this.handler, this.scope, true);
24472     }
24473 };
24474 Roo.extend(Roo.ColorPalette, Roo.Component, {
24475     /**
24476      * @cfg {String} itemCls
24477      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24478      */
24479     itemCls : "x-color-palette",
24480     /**
24481      * @cfg {String} value
24482      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24483      * the hex codes are case-sensitive.
24484      */
24485     value : null,
24486     clickEvent:'click',
24487     // private
24488     ctype: "Roo.ColorPalette",
24489
24490     /**
24491      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24492      */
24493     allowReselect : false,
24494
24495     /**
24496      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24497      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24498      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24499      * of colors with the width setting until the box is symmetrical.</p>
24500      * <p>You can override individual colors if needed:</p>
24501      * <pre><code>
24502 var cp = new Roo.ColorPalette();
24503 cp.colors[0] = "FF0000";  // change the first box to red
24504 </code></pre>
24505
24506 Or you can provide a custom array of your own for complete control:
24507 <pre><code>
24508 var cp = new Roo.ColorPalette();
24509 cp.colors = ["000000", "993300", "333300"];
24510 </code></pre>
24511      * @type Array
24512      */
24513     colors : [
24514         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24515         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24516         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24517         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24518         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24519     ],
24520
24521     // private
24522     onRender : function(container, position){
24523         var t = new Roo.MasterTemplate(
24524             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24525         );
24526         var c = this.colors;
24527         for(var i = 0, len = c.length; i < len; i++){
24528             t.add([c[i]]);
24529         }
24530         var el = document.createElement("div");
24531         el.className = this.itemCls;
24532         t.overwrite(el);
24533         container.dom.insertBefore(el, position);
24534         this.el = Roo.get(el);
24535         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24536         if(this.clickEvent != 'click'){
24537             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24538         }
24539     },
24540
24541     // private
24542     afterRender : function(){
24543         Roo.ColorPalette.superclass.afterRender.call(this);
24544         if(this.value){
24545             var s = this.value;
24546             this.value = null;
24547             this.select(s);
24548         }
24549     },
24550
24551     // private
24552     handleClick : function(e, t){
24553         e.preventDefault();
24554         if(!this.disabled){
24555             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24556             this.select(c.toUpperCase());
24557         }
24558     },
24559
24560     /**
24561      * Selects the specified color in the palette (fires the select event)
24562      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24563      */
24564     select : function(color){
24565         color = color.replace("#", "");
24566         if(color != this.value || this.allowReselect){
24567             var el = this.el;
24568             if(this.value){
24569                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24570             }
24571             el.child("a.color-"+color).addClass("x-color-palette-sel");
24572             this.value = color;
24573             this.fireEvent("select", this, color);
24574         }
24575     }
24576 });/*
24577  * Based on:
24578  * Ext JS Library 1.1.1
24579  * Copyright(c) 2006-2007, Ext JS, LLC.
24580  *
24581  * Originally Released Under LGPL - original licence link has changed is not relivant.
24582  *
24583  * Fork - LGPL
24584  * <script type="text/javascript">
24585  */
24586  
24587 /**
24588  * @class Roo.DatePicker
24589  * @extends Roo.Component
24590  * Simple date picker class.
24591  * @constructor
24592  * Create a new DatePicker
24593  * @param {Object} config The config object
24594  */
24595 Roo.DatePicker = function(config){
24596     Roo.DatePicker.superclass.constructor.call(this, config);
24597
24598     this.value = config && config.value ?
24599                  config.value.clearTime() : new Date().clearTime();
24600
24601     this.addEvents({
24602         /**
24603              * @event select
24604              * Fires when a date is selected
24605              * @param {DatePicker} this
24606              * @param {Date} date The selected date
24607              */
24608         'select': true,
24609         /**
24610              * @event monthchange
24611              * Fires when the displayed month changes 
24612              * @param {DatePicker} this
24613              * @param {Date} date The selected month
24614              */
24615         'monthchange': true
24616     });
24617
24618     if(this.handler){
24619         this.on("select", this.handler,  this.scope || this);
24620     }
24621     // build the disabledDatesRE
24622     if(!this.disabledDatesRE && this.disabledDates){
24623         var dd = this.disabledDates;
24624         var re = "(?:";
24625         for(var i = 0; i < dd.length; i++){
24626             re += dd[i];
24627             if(i != dd.length-1) re += "|";
24628         }
24629         this.disabledDatesRE = new RegExp(re + ")");
24630     }
24631 };
24632
24633 Roo.extend(Roo.DatePicker, Roo.Component, {
24634     /**
24635      * @cfg {String} todayText
24636      * The text to display on the button that selects the current date (defaults to "Today")
24637      */
24638     todayText : "Today",
24639     /**
24640      * @cfg {String} okText
24641      * The text to display on the ok button
24642      */
24643     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24644     /**
24645      * @cfg {String} cancelText
24646      * The text to display on the cancel button
24647      */
24648     cancelText : "Cancel",
24649     /**
24650      * @cfg {String} todayTip
24651      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24652      */
24653     todayTip : "{0} (Spacebar)",
24654     /**
24655      * @cfg {Date} minDate
24656      * Minimum allowable date (JavaScript date object, defaults to null)
24657      */
24658     minDate : null,
24659     /**
24660      * @cfg {Date} maxDate
24661      * Maximum allowable date (JavaScript date object, defaults to null)
24662      */
24663     maxDate : null,
24664     /**
24665      * @cfg {String} minText
24666      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24667      */
24668     minText : "This date is before the minimum date",
24669     /**
24670      * @cfg {String} maxText
24671      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24672      */
24673     maxText : "This date is after the maximum date",
24674     /**
24675      * @cfg {String} format
24676      * The default date format string which can be overriden for localization support.  The format must be
24677      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24678      */
24679     format : "m/d/y",
24680     /**
24681      * @cfg {Array} disabledDays
24682      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24683      */
24684     disabledDays : null,
24685     /**
24686      * @cfg {String} disabledDaysText
24687      * The tooltip to display when the date falls on a disabled day (defaults to "")
24688      */
24689     disabledDaysText : "",
24690     /**
24691      * @cfg {RegExp} disabledDatesRE
24692      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24693      */
24694     disabledDatesRE : null,
24695     /**
24696      * @cfg {String} disabledDatesText
24697      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24698      */
24699     disabledDatesText : "",
24700     /**
24701      * @cfg {Boolean} constrainToViewport
24702      * True to constrain the date picker to the viewport (defaults to true)
24703      */
24704     constrainToViewport : true,
24705     /**
24706      * @cfg {Array} monthNames
24707      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24708      */
24709     monthNames : Date.monthNames,
24710     /**
24711      * @cfg {Array} dayNames
24712      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24713      */
24714     dayNames : Date.dayNames,
24715     /**
24716      * @cfg {String} nextText
24717      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24718      */
24719     nextText: 'Next Month (Control+Right)',
24720     /**
24721      * @cfg {String} prevText
24722      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24723      */
24724     prevText: 'Previous Month (Control+Left)',
24725     /**
24726      * @cfg {String} monthYearText
24727      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24728      */
24729     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24730     /**
24731      * @cfg {Number} startDay
24732      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24733      */
24734     startDay : 0,
24735     /**
24736      * @cfg {Bool} showClear
24737      * Show a clear button (usefull for date form elements that can be blank.)
24738      */
24739     
24740     showClear: false,
24741     
24742     /**
24743      * Sets the value of the date field
24744      * @param {Date} value The date to set
24745      */
24746     setValue : function(value){
24747         var old = this.value;
24748         this.value = value.clearTime(true);
24749         if(this.el){
24750             this.update(this.value);
24751         }
24752     },
24753
24754     /**
24755      * Gets the current selected value of the date field
24756      * @return {Date} The selected date
24757      */
24758     getValue : function(){
24759         return this.value;
24760     },
24761
24762     // private
24763     focus : function(){
24764         if(this.el){
24765             this.update(this.activeDate);
24766         }
24767     },
24768
24769     // private
24770     onRender : function(container, position){
24771         var m = [
24772              '<table cellspacing="0">',
24773                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24774                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24775         var dn = this.dayNames;
24776         for(var i = 0; i < 7; i++){
24777             var d = this.startDay+i;
24778             if(d > 6){
24779                 d = d-7;
24780             }
24781             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24782         }
24783         m[m.length] = "</tr></thead><tbody><tr>";
24784         for(var i = 0; i < 42; i++) {
24785             if(i % 7 == 0 && i != 0){
24786                 m[m.length] = "</tr><tr>";
24787             }
24788             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24789         }
24790         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24791             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24792
24793         var el = document.createElement("div");
24794         el.className = "x-date-picker";
24795         el.innerHTML = m.join("");
24796
24797         container.dom.insertBefore(el, position);
24798
24799         this.el = Roo.get(el);
24800         this.eventEl = Roo.get(el.firstChild);
24801
24802         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24803             handler: this.showPrevMonth,
24804             scope: this,
24805             preventDefault:true,
24806             stopDefault:true
24807         });
24808
24809         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24810             handler: this.showNextMonth,
24811             scope: this,
24812             preventDefault:true,
24813             stopDefault:true
24814         });
24815
24816         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24817
24818         this.monthPicker = this.el.down('div.x-date-mp');
24819         this.monthPicker.enableDisplayMode('block');
24820         
24821         var kn = new Roo.KeyNav(this.eventEl, {
24822             "left" : function(e){
24823                 e.ctrlKey ?
24824                     this.showPrevMonth() :
24825                     this.update(this.activeDate.add("d", -1));
24826             },
24827
24828             "right" : function(e){
24829                 e.ctrlKey ?
24830                     this.showNextMonth() :
24831                     this.update(this.activeDate.add("d", 1));
24832             },
24833
24834             "up" : function(e){
24835                 e.ctrlKey ?
24836                     this.showNextYear() :
24837                     this.update(this.activeDate.add("d", -7));
24838             },
24839
24840             "down" : function(e){
24841                 e.ctrlKey ?
24842                     this.showPrevYear() :
24843                     this.update(this.activeDate.add("d", 7));
24844             },
24845
24846             "pageUp" : function(e){
24847                 this.showNextMonth();
24848             },
24849
24850             "pageDown" : function(e){
24851                 this.showPrevMonth();
24852             },
24853
24854             "enter" : function(e){
24855                 e.stopPropagation();
24856                 return true;
24857             },
24858
24859             scope : this
24860         });
24861
24862         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24863
24864         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24865
24866         this.el.unselectable();
24867         
24868         this.cells = this.el.select("table.x-date-inner tbody td");
24869         this.textNodes = this.el.query("table.x-date-inner tbody span");
24870
24871         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24872             text: "&#160;",
24873             tooltip: this.monthYearText
24874         });
24875
24876         this.mbtn.on('click', this.showMonthPicker, this);
24877         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24878
24879
24880         var today = (new Date()).dateFormat(this.format);
24881         
24882         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24883         if (this.showClear) {
24884             baseTb.add( new Roo.Toolbar.Fill());
24885         }
24886         baseTb.add({
24887             text: String.format(this.todayText, today),
24888             tooltip: String.format(this.todayTip, today),
24889             handler: this.selectToday,
24890             scope: this
24891         });
24892         
24893         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24894             
24895         //});
24896         if (this.showClear) {
24897             
24898             baseTb.add( new Roo.Toolbar.Fill());
24899             baseTb.add({
24900                 text: '&#160;',
24901                 cls: 'x-btn-icon x-btn-clear',
24902                 handler: function() {
24903                     //this.value = '';
24904                     this.fireEvent("select", this, '');
24905                 },
24906                 scope: this
24907             });
24908         }
24909         
24910         
24911         if(Roo.isIE){
24912             this.el.repaint();
24913         }
24914         this.update(this.value);
24915     },
24916
24917     createMonthPicker : function(){
24918         if(!this.monthPicker.dom.firstChild){
24919             var buf = ['<table border="0" cellspacing="0">'];
24920             for(var i = 0; i < 6; i++){
24921                 buf.push(
24922                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24923                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24924                     i == 0 ?
24925                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24926                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24927                 );
24928             }
24929             buf.push(
24930                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24931                     this.okText,
24932                     '</button><button type="button" class="x-date-mp-cancel">',
24933                     this.cancelText,
24934                     '</button></td></tr>',
24935                 '</table>'
24936             );
24937             this.monthPicker.update(buf.join(''));
24938             this.monthPicker.on('click', this.onMonthClick, this);
24939             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24940
24941             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24942             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24943
24944             this.mpMonths.each(function(m, a, i){
24945                 i += 1;
24946                 if((i%2) == 0){
24947                     m.dom.xmonth = 5 + Math.round(i * .5);
24948                 }else{
24949                     m.dom.xmonth = Math.round((i-1) * .5);
24950                 }
24951             });
24952         }
24953     },
24954
24955     showMonthPicker : function(){
24956         this.createMonthPicker();
24957         var size = this.el.getSize();
24958         this.monthPicker.setSize(size);
24959         this.monthPicker.child('table').setSize(size);
24960
24961         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24962         this.updateMPMonth(this.mpSelMonth);
24963         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24964         this.updateMPYear(this.mpSelYear);
24965
24966         this.monthPicker.slideIn('t', {duration:.2});
24967     },
24968
24969     updateMPYear : function(y){
24970         this.mpyear = y;
24971         var ys = this.mpYears.elements;
24972         for(var i = 1; i <= 10; i++){
24973             var td = ys[i-1], y2;
24974             if((i%2) == 0){
24975                 y2 = y + Math.round(i * .5);
24976                 td.firstChild.innerHTML = y2;
24977                 td.xyear = y2;
24978             }else{
24979                 y2 = y - (5-Math.round(i * .5));
24980                 td.firstChild.innerHTML = y2;
24981                 td.xyear = y2;
24982             }
24983             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24984         }
24985     },
24986
24987     updateMPMonth : function(sm){
24988         this.mpMonths.each(function(m, a, i){
24989             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24990         });
24991     },
24992
24993     selectMPMonth: function(m){
24994         
24995     },
24996
24997     onMonthClick : function(e, t){
24998         e.stopEvent();
24999         var el = new Roo.Element(t), pn;
25000         if(el.is('button.x-date-mp-cancel')){
25001             this.hideMonthPicker();
25002         }
25003         else if(el.is('button.x-date-mp-ok')){
25004             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25005             this.hideMonthPicker();
25006         }
25007         else if(pn = el.up('td.x-date-mp-month', 2)){
25008             this.mpMonths.removeClass('x-date-mp-sel');
25009             pn.addClass('x-date-mp-sel');
25010             this.mpSelMonth = pn.dom.xmonth;
25011         }
25012         else if(pn = el.up('td.x-date-mp-year', 2)){
25013             this.mpYears.removeClass('x-date-mp-sel');
25014             pn.addClass('x-date-mp-sel');
25015             this.mpSelYear = pn.dom.xyear;
25016         }
25017         else if(el.is('a.x-date-mp-prev')){
25018             this.updateMPYear(this.mpyear-10);
25019         }
25020         else if(el.is('a.x-date-mp-next')){
25021             this.updateMPYear(this.mpyear+10);
25022         }
25023     },
25024
25025     onMonthDblClick : function(e, t){
25026         e.stopEvent();
25027         var el = new Roo.Element(t), pn;
25028         if(pn = el.up('td.x-date-mp-month', 2)){
25029             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25030             this.hideMonthPicker();
25031         }
25032         else if(pn = el.up('td.x-date-mp-year', 2)){
25033             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25034             this.hideMonthPicker();
25035         }
25036     },
25037
25038     hideMonthPicker : function(disableAnim){
25039         if(this.monthPicker){
25040             if(disableAnim === true){
25041                 this.monthPicker.hide();
25042             }else{
25043                 this.monthPicker.slideOut('t', {duration:.2});
25044             }
25045         }
25046     },
25047
25048     // private
25049     showPrevMonth : function(e){
25050         this.update(this.activeDate.add("mo", -1));
25051     },
25052
25053     // private
25054     showNextMonth : function(e){
25055         this.update(this.activeDate.add("mo", 1));
25056     },
25057
25058     // private
25059     showPrevYear : function(){
25060         this.update(this.activeDate.add("y", -1));
25061     },
25062
25063     // private
25064     showNextYear : function(){
25065         this.update(this.activeDate.add("y", 1));
25066     },
25067
25068     // private
25069     handleMouseWheel : function(e){
25070         var delta = e.getWheelDelta();
25071         if(delta > 0){
25072             this.showPrevMonth();
25073             e.stopEvent();
25074         } else if(delta < 0){
25075             this.showNextMonth();
25076             e.stopEvent();
25077         }
25078     },
25079
25080     // private
25081     handleDateClick : function(e, t){
25082         e.stopEvent();
25083         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25084             this.setValue(new Date(t.dateValue));
25085             this.fireEvent("select", this, this.value);
25086         }
25087     },
25088
25089     // private
25090     selectToday : function(){
25091         this.setValue(new Date().clearTime());
25092         this.fireEvent("select", this, this.value);
25093     },
25094
25095     // private
25096     update : function(date)
25097     {
25098         var vd = this.activeDate;
25099         this.activeDate = date;
25100         if(vd && this.el){
25101             var t = date.getTime();
25102             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25103                 this.cells.removeClass("x-date-selected");
25104                 this.cells.each(function(c){
25105                    if(c.dom.firstChild.dateValue == t){
25106                        c.addClass("x-date-selected");
25107                        setTimeout(function(){
25108                             try{c.dom.firstChild.focus();}catch(e){}
25109                        }, 50);
25110                        return false;
25111                    }
25112                 });
25113                 return;
25114             }
25115         }
25116         
25117         var days = date.getDaysInMonth();
25118         var firstOfMonth = date.getFirstDateOfMonth();
25119         var startingPos = firstOfMonth.getDay()-this.startDay;
25120
25121         if(startingPos <= this.startDay){
25122             startingPos += 7;
25123         }
25124
25125         var pm = date.add("mo", -1);
25126         var prevStart = pm.getDaysInMonth()-startingPos;
25127
25128         var cells = this.cells.elements;
25129         var textEls = this.textNodes;
25130         days += startingPos;
25131
25132         // convert everything to numbers so it's fast
25133         var day = 86400000;
25134         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25135         var today = new Date().clearTime().getTime();
25136         var sel = date.clearTime().getTime();
25137         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25138         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25139         var ddMatch = this.disabledDatesRE;
25140         var ddText = this.disabledDatesText;
25141         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25142         var ddaysText = this.disabledDaysText;
25143         var format = this.format;
25144
25145         var setCellClass = function(cal, cell){
25146             cell.title = "";
25147             var t = d.getTime();
25148             cell.firstChild.dateValue = t;
25149             if(t == today){
25150                 cell.className += " x-date-today";
25151                 cell.title = cal.todayText;
25152             }
25153             if(t == sel){
25154                 cell.className += " x-date-selected";
25155                 setTimeout(function(){
25156                     try{cell.firstChild.focus();}catch(e){}
25157                 }, 50);
25158             }
25159             // disabling
25160             if(t < min) {
25161                 cell.className = " x-date-disabled";
25162                 cell.title = cal.minText;
25163                 return;
25164             }
25165             if(t > max) {
25166                 cell.className = " x-date-disabled";
25167                 cell.title = cal.maxText;
25168                 return;
25169             }
25170             if(ddays){
25171                 if(ddays.indexOf(d.getDay()) != -1){
25172                     cell.title = ddaysText;
25173                     cell.className = " x-date-disabled";
25174                 }
25175             }
25176             if(ddMatch && format){
25177                 var fvalue = d.dateFormat(format);
25178                 if(ddMatch.test(fvalue)){
25179                     cell.title = ddText.replace("%0", fvalue);
25180                     cell.className = " x-date-disabled";
25181                 }
25182             }
25183         };
25184
25185         var i = 0;
25186         for(; i < startingPos; i++) {
25187             textEls[i].innerHTML = (++prevStart);
25188             d.setDate(d.getDate()+1);
25189             cells[i].className = "x-date-prevday";
25190             setCellClass(this, cells[i]);
25191         }
25192         for(; i < days; i++){
25193             intDay = i - startingPos + 1;
25194             textEls[i].innerHTML = (intDay);
25195             d.setDate(d.getDate()+1);
25196             cells[i].className = "x-date-active";
25197             setCellClass(this, cells[i]);
25198         }
25199         var extraDays = 0;
25200         for(; i < 42; i++) {
25201              textEls[i].innerHTML = (++extraDays);
25202              d.setDate(d.getDate()+1);
25203              cells[i].className = "x-date-nextday";
25204              setCellClass(this, cells[i]);
25205         }
25206
25207         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25208         this.fireEvent('monthchange', this, date);
25209         
25210         if(!this.internalRender){
25211             var main = this.el.dom.firstChild;
25212             var w = main.offsetWidth;
25213             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25214             Roo.fly(main).setWidth(w);
25215             this.internalRender = true;
25216             // opera does not respect the auto grow header center column
25217             // then, after it gets a width opera refuses to recalculate
25218             // without a second pass
25219             if(Roo.isOpera && !this.secondPass){
25220                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25221                 this.secondPass = true;
25222                 this.update.defer(10, this, [date]);
25223             }
25224         }
25225         
25226         
25227     }
25228 });        /*
25229  * Based on:
25230  * Ext JS Library 1.1.1
25231  * Copyright(c) 2006-2007, Ext JS, LLC.
25232  *
25233  * Originally Released Under LGPL - original licence link has changed is not relivant.
25234  *
25235  * Fork - LGPL
25236  * <script type="text/javascript">
25237  */
25238 /**
25239  * @class Roo.TabPanel
25240  * @extends Roo.util.Observable
25241  * A lightweight tab container.
25242  * <br><br>
25243  * Usage:
25244  * <pre><code>
25245 // basic tabs 1, built from existing content
25246 var tabs = new Roo.TabPanel("tabs1");
25247 tabs.addTab("script", "View Script");
25248 tabs.addTab("markup", "View Markup");
25249 tabs.activate("script");
25250
25251 // more advanced tabs, built from javascript
25252 var jtabs = new Roo.TabPanel("jtabs");
25253 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25254
25255 // set up the UpdateManager
25256 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25257 var updater = tab2.getUpdateManager();
25258 updater.setDefaultUrl("ajax1.htm");
25259 tab2.on('activate', updater.refresh, updater, true);
25260
25261 // Use setUrl for Ajax loading
25262 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25263 tab3.setUrl("ajax2.htm", null, true);
25264
25265 // Disabled tab
25266 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25267 tab4.disable();
25268
25269 jtabs.activate("jtabs-1");
25270  * </code></pre>
25271  * @constructor
25272  * Create a new TabPanel.
25273  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25274  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25275  */
25276 Roo.TabPanel = function(container, config){
25277     /**
25278     * The container element for this TabPanel.
25279     * @type Roo.Element
25280     */
25281     this.el = Roo.get(container, true);
25282     if(config){
25283         if(typeof config == "boolean"){
25284             this.tabPosition = config ? "bottom" : "top";
25285         }else{
25286             Roo.apply(this, config);
25287         }
25288     }
25289     if(this.tabPosition == "bottom"){
25290         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25291         this.el.addClass("x-tabs-bottom");
25292     }
25293     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25294     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25295     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25296     if(Roo.isIE){
25297         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25298     }
25299     if(this.tabPosition != "bottom"){
25300         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25301          * @type Roo.Element
25302          */
25303         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25304         this.el.addClass("x-tabs-top");
25305     }
25306     this.items = [];
25307
25308     this.bodyEl.setStyle("position", "relative");
25309
25310     this.active = null;
25311     this.activateDelegate = this.activate.createDelegate(this);
25312
25313     this.addEvents({
25314         /**
25315          * @event tabchange
25316          * Fires when the active tab changes
25317          * @param {Roo.TabPanel} this
25318          * @param {Roo.TabPanelItem} activePanel The new active tab
25319          */
25320         "tabchange": true,
25321         /**
25322          * @event beforetabchange
25323          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25324          * @param {Roo.TabPanel} this
25325          * @param {Object} e Set cancel to true on this object to cancel the tab change
25326          * @param {Roo.TabPanelItem} tab The tab being changed to
25327          */
25328         "beforetabchange" : true
25329     });
25330
25331     Roo.EventManager.onWindowResize(this.onResize, this);
25332     this.cpad = this.el.getPadding("lr");
25333     this.hiddenCount = 0;
25334
25335
25336     // toolbar on the tabbar support...
25337     if (this.toolbar) {
25338         var tcfg = this.toolbar;
25339         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25340         this.toolbar = new Roo.Toolbar(tcfg);
25341         if (Roo.isSafari) {
25342             var tbl = tcfg.container.child('table', true);
25343             tbl.setAttribute('width', '100%');
25344         }
25345         
25346     }
25347    
25348
25349
25350     Roo.TabPanel.superclass.constructor.call(this);
25351 };
25352
25353 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25354     /*
25355      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25356      */
25357     tabPosition : "top",
25358     /*
25359      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25360      */
25361     currentTabWidth : 0,
25362     /*
25363      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25364      */
25365     minTabWidth : 40,
25366     /*
25367      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25368      */
25369     maxTabWidth : 250,
25370     /*
25371      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25372      */
25373     preferredTabWidth : 175,
25374     /*
25375      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25376      */
25377     resizeTabs : false,
25378     /*
25379      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25380      */
25381     monitorResize : true,
25382     /*
25383      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25384      */
25385     toolbar : false,
25386
25387     /**
25388      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25389      * @param {String} id The id of the div to use <b>or create</b>
25390      * @param {String} text The text for the tab
25391      * @param {String} content (optional) Content to put in the TabPanelItem body
25392      * @param {Boolean} closable (optional) True to create a close icon on the tab
25393      * @return {Roo.TabPanelItem} The created TabPanelItem
25394      */
25395     addTab : function(id, text, content, closable){
25396         var item = new Roo.TabPanelItem(this, id, text, closable);
25397         this.addTabItem(item);
25398         if(content){
25399             item.setContent(content);
25400         }
25401         return item;
25402     },
25403
25404     /**
25405      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25406      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25407      * @return {Roo.TabPanelItem}
25408      */
25409     getTab : function(id){
25410         return this.items[id];
25411     },
25412
25413     /**
25414      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25415      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25416      */
25417     hideTab : function(id){
25418         var t = this.items[id];
25419         if(!t.isHidden()){
25420            t.setHidden(true);
25421            this.hiddenCount++;
25422            this.autoSizeTabs();
25423         }
25424     },
25425
25426     /**
25427      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25428      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25429      */
25430     unhideTab : function(id){
25431         var t = this.items[id];
25432         if(t.isHidden()){
25433            t.setHidden(false);
25434            this.hiddenCount--;
25435            this.autoSizeTabs();
25436         }
25437     },
25438
25439     /**
25440      * Adds an existing {@link Roo.TabPanelItem}.
25441      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25442      */
25443     addTabItem : function(item){
25444         this.items[item.id] = item;
25445         this.items.push(item);
25446         if(this.resizeTabs){
25447            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25448            this.autoSizeTabs();
25449         }else{
25450             item.autoSize();
25451         }
25452     },
25453
25454     /**
25455      * Removes a {@link Roo.TabPanelItem}.
25456      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25457      */
25458     removeTab : function(id){
25459         var items = this.items;
25460         var tab = items[id];
25461         if(!tab) { return; }
25462         var index = items.indexOf(tab);
25463         if(this.active == tab && items.length > 1){
25464             var newTab = this.getNextAvailable(index);
25465             if(newTab) {
25466                 newTab.activate();
25467             }
25468         }
25469         this.stripEl.dom.removeChild(tab.pnode.dom);
25470         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25471             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25472         }
25473         items.splice(index, 1);
25474         delete this.items[tab.id];
25475         tab.fireEvent("close", tab);
25476         tab.purgeListeners();
25477         this.autoSizeTabs();
25478     },
25479
25480     getNextAvailable : function(start){
25481         var items = this.items;
25482         var index = start;
25483         // look for a next tab that will slide over to
25484         // replace the one being removed
25485         while(index < items.length){
25486             var item = items[++index];
25487             if(item && !item.isHidden()){
25488                 return item;
25489             }
25490         }
25491         // if one isn't found select the previous tab (on the left)
25492         index = start;
25493         while(index >= 0){
25494             var item = items[--index];
25495             if(item && !item.isHidden()){
25496                 return item;
25497             }
25498         }
25499         return null;
25500     },
25501
25502     /**
25503      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25504      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25505      */
25506     disableTab : function(id){
25507         var tab = this.items[id];
25508         if(tab && this.active != tab){
25509             tab.disable();
25510         }
25511     },
25512
25513     /**
25514      * Enables a {@link Roo.TabPanelItem} that is disabled.
25515      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25516      */
25517     enableTab : function(id){
25518         var tab = this.items[id];
25519         tab.enable();
25520     },
25521
25522     /**
25523      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25524      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25525      * @return {Roo.TabPanelItem} The TabPanelItem.
25526      */
25527     activate : function(id){
25528         var tab = this.items[id];
25529         if(!tab){
25530             return null;
25531         }
25532         if(tab == this.active || tab.disabled){
25533             return tab;
25534         }
25535         var e = {};
25536         this.fireEvent("beforetabchange", this, e, tab);
25537         if(e.cancel !== true && !tab.disabled){
25538             if(this.active){
25539                 this.active.hide();
25540             }
25541             this.active = this.items[id];
25542             this.active.show();
25543             this.fireEvent("tabchange", this, this.active);
25544         }
25545         return tab;
25546     },
25547
25548     /**
25549      * Gets the active {@link Roo.TabPanelItem}.
25550      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25551      */
25552     getActiveTab : function(){
25553         return this.active;
25554     },
25555
25556     /**
25557      * Updates the tab body element to fit the height of the container element
25558      * for overflow scrolling
25559      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25560      */
25561     syncHeight : function(targetHeight){
25562         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25563         var bm = this.bodyEl.getMargins();
25564         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25565         this.bodyEl.setHeight(newHeight);
25566         return newHeight;
25567     },
25568
25569     onResize : function(){
25570         if(this.monitorResize){
25571             this.autoSizeTabs();
25572         }
25573     },
25574
25575     /**
25576      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25577      */
25578     beginUpdate : function(){
25579         this.updating = true;
25580     },
25581
25582     /**
25583      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25584      */
25585     endUpdate : function(){
25586         this.updating = false;
25587         this.autoSizeTabs();
25588     },
25589
25590     /**
25591      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25592      */
25593     autoSizeTabs : function(){
25594         var count = this.items.length;
25595         var vcount = count - this.hiddenCount;
25596         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25597         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25598         var availWidth = Math.floor(w / vcount);
25599         var b = this.stripBody;
25600         if(b.getWidth() > w){
25601             var tabs = this.items;
25602             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25603             if(availWidth < this.minTabWidth){
25604                 /*if(!this.sleft){    // incomplete scrolling code
25605                     this.createScrollButtons();
25606                 }
25607                 this.showScroll();
25608                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25609             }
25610         }else{
25611             if(this.currentTabWidth < this.preferredTabWidth){
25612                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25613             }
25614         }
25615     },
25616
25617     /**
25618      * Returns the number of tabs in this TabPanel.
25619      * @return {Number}
25620      */
25621      getCount : function(){
25622          return this.items.length;
25623      },
25624
25625     /**
25626      * Resizes all the tabs to the passed width
25627      * @param {Number} The new width
25628      */
25629     setTabWidth : function(width){
25630         this.currentTabWidth = width;
25631         for(var i = 0, len = this.items.length; i < len; i++) {
25632                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25633         }
25634     },
25635
25636     /**
25637      * Destroys this TabPanel
25638      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25639      */
25640     destroy : function(removeEl){
25641         Roo.EventManager.removeResizeListener(this.onResize, this);
25642         for(var i = 0, len = this.items.length; i < len; i++){
25643             this.items[i].purgeListeners();
25644         }
25645         if(removeEl === true){
25646             this.el.update("");
25647             this.el.remove();
25648         }
25649     }
25650 });
25651
25652 /**
25653  * @class Roo.TabPanelItem
25654  * @extends Roo.util.Observable
25655  * Represents an individual item (tab plus body) in a TabPanel.
25656  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25657  * @param {String} id The id of this TabPanelItem
25658  * @param {String} text The text for the tab of this TabPanelItem
25659  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25660  */
25661 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25662     /**
25663      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25664      * @type Roo.TabPanel
25665      */
25666     this.tabPanel = tabPanel;
25667     /**
25668      * The id for this TabPanelItem
25669      * @type String
25670      */
25671     this.id = id;
25672     /** @private */
25673     this.disabled = false;
25674     /** @private */
25675     this.text = text;
25676     /** @private */
25677     this.loaded = false;
25678     this.closable = closable;
25679
25680     /**
25681      * The body element for this TabPanelItem.
25682      * @type Roo.Element
25683      */
25684     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25685     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25686     this.bodyEl.setStyle("display", "block");
25687     this.bodyEl.setStyle("zoom", "1");
25688     this.hideAction();
25689
25690     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25691     /** @private */
25692     this.el = Roo.get(els.el, true);
25693     this.inner = Roo.get(els.inner, true);
25694     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25695     this.pnode = Roo.get(els.el.parentNode, true);
25696     this.el.on("mousedown", this.onTabMouseDown, this);
25697     this.el.on("click", this.onTabClick, this);
25698     /** @private */
25699     if(closable){
25700         var c = Roo.get(els.close, true);
25701         c.dom.title = this.closeText;
25702         c.addClassOnOver("close-over");
25703         c.on("click", this.closeClick, this);
25704      }
25705
25706     this.addEvents({
25707          /**
25708          * @event activate
25709          * Fires when this tab becomes the active tab.
25710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25711          * @param {Roo.TabPanelItem} this
25712          */
25713         "activate": true,
25714         /**
25715          * @event beforeclose
25716          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25717          * @param {Roo.TabPanelItem} this
25718          * @param {Object} e Set cancel to true on this object to cancel the close.
25719          */
25720         "beforeclose": true,
25721         /**
25722          * @event close
25723          * Fires when this tab is closed.
25724          * @param {Roo.TabPanelItem} this
25725          */
25726          "close": true,
25727         /**
25728          * @event deactivate
25729          * Fires when this tab is no longer the active tab.
25730          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25731          * @param {Roo.TabPanelItem} this
25732          */
25733          "deactivate" : true
25734     });
25735     this.hidden = false;
25736
25737     Roo.TabPanelItem.superclass.constructor.call(this);
25738 };
25739
25740 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25741     purgeListeners : function(){
25742        Roo.util.Observable.prototype.purgeListeners.call(this);
25743        this.el.removeAllListeners();
25744     },
25745     /**
25746      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25747      */
25748     show : function(){
25749         this.pnode.addClass("on");
25750         this.showAction();
25751         if(Roo.isOpera){
25752             this.tabPanel.stripWrap.repaint();
25753         }
25754         this.fireEvent("activate", this.tabPanel, this);
25755     },
25756
25757     /**
25758      * Returns true if this tab is the active tab.
25759      * @return {Boolean}
25760      */
25761     isActive : function(){
25762         return this.tabPanel.getActiveTab() == this;
25763     },
25764
25765     /**
25766      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25767      */
25768     hide : function(){
25769         this.pnode.removeClass("on");
25770         this.hideAction();
25771         this.fireEvent("deactivate", this.tabPanel, this);
25772     },
25773
25774     hideAction : function(){
25775         this.bodyEl.hide();
25776         this.bodyEl.setStyle("position", "absolute");
25777         this.bodyEl.setLeft("-20000px");
25778         this.bodyEl.setTop("-20000px");
25779     },
25780
25781     showAction : function(){
25782         this.bodyEl.setStyle("position", "relative");
25783         this.bodyEl.setTop("");
25784         this.bodyEl.setLeft("");
25785         this.bodyEl.show();
25786     },
25787
25788     /**
25789      * Set the tooltip for the tab.
25790      * @param {String} tooltip The tab's tooltip
25791      */
25792     setTooltip : function(text){
25793         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25794             this.textEl.dom.qtip = text;
25795             this.textEl.dom.removeAttribute('title');
25796         }else{
25797             this.textEl.dom.title = text;
25798         }
25799     },
25800
25801     onTabClick : function(e){
25802         e.preventDefault();
25803         this.tabPanel.activate(this.id);
25804     },
25805
25806     onTabMouseDown : function(e){
25807         e.preventDefault();
25808         this.tabPanel.activate(this.id);
25809     },
25810
25811     getWidth : function(){
25812         return this.inner.getWidth();
25813     },
25814
25815     setWidth : function(width){
25816         var iwidth = width - this.pnode.getPadding("lr");
25817         this.inner.setWidth(iwidth);
25818         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25819         this.pnode.setWidth(width);
25820     },
25821
25822     /**
25823      * Show or hide the tab
25824      * @param {Boolean} hidden True to hide or false to show.
25825      */
25826     setHidden : function(hidden){
25827         this.hidden = hidden;
25828         this.pnode.setStyle("display", hidden ? "none" : "");
25829     },
25830
25831     /**
25832      * Returns true if this tab is "hidden"
25833      * @return {Boolean}
25834      */
25835     isHidden : function(){
25836         return this.hidden;
25837     },
25838
25839     /**
25840      * Returns the text for this tab
25841      * @return {String}
25842      */
25843     getText : function(){
25844         return this.text;
25845     },
25846
25847     autoSize : function(){
25848         //this.el.beginMeasure();
25849         this.textEl.setWidth(1);
25850         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25851         //this.el.endMeasure();
25852     },
25853
25854     /**
25855      * Sets the text for the tab (Note: this also sets the tooltip text)
25856      * @param {String} text The tab's text and tooltip
25857      */
25858     setText : function(text){
25859         this.text = text;
25860         this.textEl.update(text);
25861         this.setTooltip(text);
25862         if(!this.tabPanel.resizeTabs){
25863             this.autoSize();
25864         }
25865     },
25866     /**
25867      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25868      */
25869     activate : function(){
25870         this.tabPanel.activate(this.id);
25871     },
25872
25873     /**
25874      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25875      */
25876     disable : function(){
25877         if(this.tabPanel.active != this){
25878             this.disabled = true;
25879             this.pnode.addClass("disabled");
25880         }
25881     },
25882
25883     /**
25884      * Enables this TabPanelItem if it was previously disabled.
25885      */
25886     enable : function(){
25887         this.disabled = false;
25888         this.pnode.removeClass("disabled");
25889     },
25890
25891     /**
25892      * Sets the content for this TabPanelItem.
25893      * @param {String} content The content
25894      * @param {Boolean} loadScripts true to look for and load scripts
25895      */
25896     setContent : function(content, loadScripts){
25897         this.bodyEl.update(content, loadScripts);
25898     },
25899
25900     /**
25901      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25902      * @return {Roo.UpdateManager} The UpdateManager
25903      */
25904     getUpdateManager : function(){
25905         return this.bodyEl.getUpdateManager();
25906     },
25907
25908     /**
25909      * Set a URL to be used to load the content for this TabPanelItem.
25910      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25911      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25912      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25913      * @return {Roo.UpdateManager} The UpdateManager
25914      */
25915     setUrl : function(url, params, loadOnce){
25916         if(this.refreshDelegate){
25917             this.un('activate', this.refreshDelegate);
25918         }
25919         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25920         this.on("activate", this.refreshDelegate);
25921         return this.bodyEl.getUpdateManager();
25922     },
25923
25924     /** @private */
25925     _handleRefresh : function(url, params, loadOnce){
25926         if(!loadOnce || !this.loaded){
25927             var updater = this.bodyEl.getUpdateManager();
25928             updater.update(url, params, this._setLoaded.createDelegate(this));
25929         }
25930     },
25931
25932     /**
25933      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25934      *   Will fail silently if the setUrl method has not been called.
25935      *   This does not activate the panel, just updates its content.
25936      */
25937     refresh : function(){
25938         if(this.refreshDelegate){
25939            this.loaded = false;
25940            this.refreshDelegate();
25941         }
25942     },
25943
25944     /** @private */
25945     _setLoaded : function(){
25946         this.loaded = true;
25947     },
25948
25949     /** @private */
25950     closeClick : function(e){
25951         var o = {};
25952         e.stopEvent();
25953         this.fireEvent("beforeclose", this, o);
25954         if(o.cancel !== true){
25955             this.tabPanel.removeTab(this.id);
25956         }
25957     },
25958     /**
25959      * The text displayed in the tooltip for the close icon.
25960      * @type String
25961      */
25962     closeText : "Close this tab"
25963 });
25964
25965 /** @private */
25966 Roo.TabPanel.prototype.createStrip = function(container){
25967     var strip = document.createElement("div");
25968     strip.className = "x-tabs-wrap";
25969     container.appendChild(strip);
25970     return strip;
25971 };
25972 /** @private */
25973 Roo.TabPanel.prototype.createStripList = function(strip){
25974     // div wrapper for retard IE
25975     // returns the "tr" element.
25976     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25977         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25978         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25979     return strip.firstChild.firstChild.firstChild.firstChild;
25980 };
25981 /** @private */
25982 Roo.TabPanel.prototype.createBody = function(container){
25983     var body = document.createElement("div");
25984     Roo.id(body, "tab-body");
25985     Roo.fly(body).addClass("x-tabs-body");
25986     container.appendChild(body);
25987     return body;
25988 };
25989 /** @private */
25990 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25991     var body = Roo.getDom(id);
25992     if(!body){
25993         body = document.createElement("div");
25994         body.id = id;
25995     }
25996     Roo.fly(body).addClass("x-tabs-item-body");
25997     bodyEl.insertBefore(body, bodyEl.firstChild);
25998     return body;
25999 };
26000 /** @private */
26001 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26002     var td = document.createElement("td");
26003     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26004     //stripEl.appendChild(td);
26005     if(closable){
26006         td.className = "x-tabs-closable";
26007         if(!this.closeTpl){
26008             this.closeTpl = new Roo.Template(
26009                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26010                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26011                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26012             );
26013         }
26014         var el = this.closeTpl.overwrite(td, {"text": text});
26015         var close = el.getElementsByTagName("div")[0];
26016         var inner = el.getElementsByTagName("em")[0];
26017         return {"el": el, "close": close, "inner": inner};
26018     } else {
26019         if(!this.tabTpl){
26020             this.tabTpl = new Roo.Template(
26021                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26022                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26023             );
26024         }
26025         var el = this.tabTpl.overwrite(td, {"text": text});
26026         var inner = el.getElementsByTagName("em")[0];
26027         return {"el": el, "inner": inner};
26028     }
26029 };/*
26030  * Based on:
26031  * Ext JS Library 1.1.1
26032  * Copyright(c) 2006-2007, Ext JS, LLC.
26033  *
26034  * Originally Released Under LGPL - original licence link has changed is not relivant.
26035  *
26036  * Fork - LGPL
26037  * <script type="text/javascript">
26038  */
26039
26040 /**
26041  * @class Roo.Button
26042  * @extends Roo.util.Observable
26043  * Simple Button class
26044  * @cfg {String} text The button text
26045  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26046  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26047  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26048  * @cfg {Object} scope The scope of the handler
26049  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26050  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26051  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26052  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26053  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26054  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26055    applies if enableToggle = true)
26056  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26057  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26058   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26059  * @constructor
26060  * Create a new button
26061  * @param {Object} config The config object
26062  */
26063 Roo.Button = function(renderTo, config)
26064 {
26065     if (!config) {
26066         config = renderTo;
26067         renderTo = config.renderTo || false;
26068     }
26069     
26070     Roo.apply(this, config);
26071     this.addEvents({
26072         /**
26073              * @event click
26074              * Fires when this button is clicked
26075              * @param {Button} this
26076              * @param {EventObject} e The click event
26077              */
26078             "click" : true,
26079         /**
26080              * @event toggle
26081              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26082              * @param {Button} this
26083              * @param {Boolean} pressed
26084              */
26085             "toggle" : true,
26086         /**
26087              * @event mouseover
26088              * Fires when the mouse hovers over the button
26089              * @param {Button} this
26090              * @param {Event} e The event object
26091              */
26092         'mouseover' : true,
26093         /**
26094              * @event mouseout
26095              * Fires when the mouse exits the button
26096              * @param {Button} this
26097              * @param {Event} e The event object
26098              */
26099         'mouseout': true,
26100          /**
26101              * @event render
26102              * Fires when the button is rendered
26103              * @param {Button} this
26104              */
26105         'render': true
26106     });
26107     if(this.menu){
26108         this.menu = Roo.menu.MenuMgr.get(this.menu);
26109     }
26110     // register listeners first!!  - so render can be captured..
26111     Roo.util.Observable.call(this);
26112     if(renderTo){
26113         this.render(renderTo);
26114     }
26115     
26116   
26117 };
26118
26119 Roo.extend(Roo.Button, Roo.util.Observable, {
26120     /**
26121      * 
26122      */
26123     
26124     /**
26125      * Read-only. True if this button is hidden
26126      * @type Boolean
26127      */
26128     hidden : false,
26129     /**
26130      * Read-only. True if this button is disabled
26131      * @type Boolean
26132      */
26133     disabled : false,
26134     /**
26135      * Read-only. True if this button is pressed (only if enableToggle = true)
26136      * @type Boolean
26137      */
26138     pressed : false,
26139
26140     /**
26141      * @cfg {Number} tabIndex 
26142      * The DOM tabIndex for this button (defaults to undefined)
26143      */
26144     tabIndex : undefined,
26145
26146     /**
26147      * @cfg {Boolean} enableToggle
26148      * True to enable pressed/not pressed toggling (defaults to false)
26149      */
26150     enableToggle: false,
26151     /**
26152      * @cfg {Mixed} menu
26153      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26154      */
26155     menu : undefined,
26156     /**
26157      * @cfg {String} menuAlign
26158      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26159      */
26160     menuAlign : "tl-bl?",
26161
26162     /**
26163      * @cfg {String} iconCls
26164      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26165      */
26166     iconCls : undefined,
26167     /**
26168      * @cfg {String} type
26169      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26170      */
26171     type : 'button',
26172
26173     // private
26174     menuClassTarget: 'tr',
26175
26176     /**
26177      * @cfg {String} clickEvent
26178      * The type of event to map to the button's event handler (defaults to 'click')
26179      */
26180     clickEvent : 'click',
26181
26182     /**
26183      * @cfg {Boolean} handleMouseEvents
26184      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26185      */
26186     handleMouseEvents : true,
26187
26188     /**
26189      * @cfg {String} tooltipType
26190      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26191      */
26192     tooltipType : 'qtip',
26193
26194     /**
26195      * @cfg {String} cls
26196      * A CSS class to apply to the button's main element.
26197      */
26198     
26199     /**
26200      * @cfg {Roo.Template} template (Optional)
26201      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26202      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26203      * require code modifications if required elements (e.g. a button) aren't present.
26204      */
26205
26206     // private
26207     render : function(renderTo){
26208         var btn;
26209         if(this.hideParent){
26210             this.parentEl = Roo.get(renderTo);
26211         }
26212         if(!this.dhconfig){
26213             if(!this.template){
26214                 if(!Roo.Button.buttonTemplate){
26215                     // hideous table template
26216                     Roo.Button.buttonTemplate = new Roo.Template(
26217                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26218                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
26219                         "</tr></tbody></table>");
26220                 }
26221                 this.template = Roo.Button.buttonTemplate;
26222             }
26223             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26224             var btnEl = btn.child("button:first");
26225             btnEl.on('focus', this.onFocus, this);
26226             btnEl.on('blur', this.onBlur, this);
26227             if(this.cls){
26228                 btn.addClass(this.cls);
26229             }
26230             if(this.icon){
26231                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26232             }
26233             if(this.iconCls){
26234                 btnEl.addClass(this.iconCls);
26235                 if(!this.cls){
26236                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26237                 }
26238             }
26239             if(this.tabIndex !== undefined){
26240                 btnEl.dom.tabIndex = this.tabIndex;
26241             }
26242             if(this.tooltip){
26243                 if(typeof this.tooltip == 'object'){
26244                     Roo.QuickTips.tips(Roo.apply({
26245                           target: btnEl.id
26246                     }, this.tooltip));
26247                 } else {
26248                     btnEl.dom[this.tooltipType] = this.tooltip;
26249                 }
26250             }
26251         }else{
26252             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26253         }
26254         this.el = btn;
26255         if(this.id){
26256             this.el.dom.id = this.el.id = this.id;
26257         }
26258         if(this.menu){
26259             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26260             this.menu.on("show", this.onMenuShow, this);
26261             this.menu.on("hide", this.onMenuHide, this);
26262         }
26263         btn.addClass("x-btn");
26264         if(Roo.isIE && !Roo.isIE7){
26265             this.autoWidth.defer(1, this);
26266         }else{
26267             this.autoWidth();
26268         }
26269         if(this.handleMouseEvents){
26270             btn.on("mouseover", this.onMouseOver, this);
26271             btn.on("mouseout", this.onMouseOut, this);
26272             btn.on("mousedown", this.onMouseDown, this);
26273         }
26274         btn.on(this.clickEvent, this.onClick, this);
26275         //btn.on("mouseup", this.onMouseUp, this);
26276         if(this.hidden){
26277             this.hide();
26278         }
26279         if(this.disabled){
26280             this.disable();
26281         }
26282         Roo.ButtonToggleMgr.register(this);
26283         if(this.pressed){
26284             this.el.addClass("x-btn-pressed");
26285         }
26286         if(this.repeat){
26287             var repeater = new Roo.util.ClickRepeater(btn,
26288                 typeof this.repeat == "object" ? this.repeat : {}
26289             );
26290             repeater.on("click", this.onClick,  this);
26291         }
26292         
26293         this.fireEvent('render', this);
26294         
26295     },
26296     /**
26297      * Returns the button's underlying element
26298      * @return {Roo.Element} The element
26299      */
26300     getEl : function(){
26301         return this.el;  
26302     },
26303     
26304     /**
26305      * Destroys this Button and removes any listeners.
26306      */
26307     destroy : function(){
26308         Roo.ButtonToggleMgr.unregister(this);
26309         this.el.removeAllListeners();
26310         this.purgeListeners();
26311         this.el.remove();
26312     },
26313
26314     // private
26315     autoWidth : function(){
26316         if(this.el){
26317             this.el.setWidth("auto");
26318             if(Roo.isIE7 && Roo.isStrict){
26319                 var ib = this.el.child('button');
26320                 if(ib && ib.getWidth() > 20){
26321                     ib.clip();
26322                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26323                 }
26324             }
26325             if(this.minWidth){
26326                 if(this.hidden){
26327                     this.el.beginMeasure();
26328                 }
26329                 if(this.el.getWidth() < this.minWidth){
26330                     this.el.setWidth(this.minWidth);
26331                 }
26332                 if(this.hidden){
26333                     this.el.endMeasure();
26334                 }
26335             }
26336         }
26337     },
26338
26339     /**
26340      * Assigns this button's click handler
26341      * @param {Function} handler The function to call when the button is clicked
26342      * @param {Object} scope (optional) Scope for the function passed in
26343      */
26344     setHandler : function(handler, scope){
26345         this.handler = handler;
26346         this.scope = scope;  
26347     },
26348     
26349     /**
26350      * Sets this button's text
26351      * @param {String} text The button text
26352      */
26353     setText : function(text){
26354         this.text = text;
26355         if(this.el){
26356             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26357         }
26358         this.autoWidth();
26359     },
26360     
26361     /**
26362      * Gets the text for this button
26363      * @return {String} The button text
26364      */
26365     getText : function(){
26366         return this.text;  
26367     },
26368     
26369     /**
26370      * Show this button
26371      */
26372     show: function(){
26373         this.hidden = false;
26374         if(this.el){
26375             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26376         }
26377     },
26378     
26379     /**
26380      * Hide this button
26381      */
26382     hide: function(){
26383         this.hidden = true;
26384         if(this.el){
26385             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26386         }
26387     },
26388     
26389     /**
26390      * Convenience function for boolean show/hide
26391      * @param {Boolean} visible True to show, false to hide
26392      */
26393     setVisible: function(visible){
26394         if(visible) {
26395             this.show();
26396         }else{
26397             this.hide();
26398         }
26399     },
26400     
26401     /**
26402      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26403      * @param {Boolean} state (optional) Force a particular state
26404      */
26405     toggle : function(state){
26406         state = state === undefined ? !this.pressed : state;
26407         if(state != this.pressed){
26408             if(state){
26409                 this.el.addClass("x-btn-pressed");
26410                 this.pressed = true;
26411                 this.fireEvent("toggle", this, true);
26412             }else{
26413                 this.el.removeClass("x-btn-pressed");
26414                 this.pressed = false;
26415                 this.fireEvent("toggle", this, false);
26416             }
26417             if(this.toggleHandler){
26418                 this.toggleHandler.call(this.scope || this, this, state);
26419             }
26420         }
26421     },
26422     
26423     /**
26424      * Focus the button
26425      */
26426     focus : function(){
26427         this.el.child('button:first').focus();
26428     },
26429     
26430     /**
26431      * Disable this button
26432      */
26433     disable : function(){
26434         if(this.el){
26435             this.el.addClass("x-btn-disabled");
26436         }
26437         this.disabled = true;
26438     },
26439     
26440     /**
26441      * Enable this button
26442      */
26443     enable : function(){
26444         if(this.el){
26445             this.el.removeClass("x-btn-disabled");
26446         }
26447         this.disabled = false;
26448     },
26449
26450     /**
26451      * Convenience function for boolean enable/disable
26452      * @param {Boolean} enabled True to enable, false to disable
26453      */
26454     setDisabled : function(v){
26455         this[v !== true ? "enable" : "disable"]();
26456     },
26457
26458     // private
26459     onClick : function(e){
26460         if(e){
26461             e.preventDefault();
26462         }
26463         if(e.button != 0){
26464             return;
26465         }
26466         if(!this.disabled){
26467             if(this.enableToggle){
26468                 this.toggle();
26469             }
26470             if(this.menu && !this.menu.isVisible()){
26471                 this.menu.show(this.el, this.menuAlign);
26472             }
26473             this.fireEvent("click", this, e);
26474             if(this.handler){
26475                 this.el.removeClass("x-btn-over");
26476                 this.handler.call(this.scope || this, this, e);
26477             }
26478         }
26479     },
26480     // private
26481     onMouseOver : function(e){
26482         if(!this.disabled){
26483             this.el.addClass("x-btn-over");
26484             this.fireEvent('mouseover', this, e);
26485         }
26486     },
26487     // private
26488     onMouseOut : function(e){
26489         if(!e.within(this.el,  true)){
26490             this.el.removeClass("x-btn-over");
26491             this.fireEvent('mouseout', this, e);
26492         }
26493     },
26494     // private
26495     onFocus : function(e){
26496         if(!this.disabled){
26497             this.el.addClass("x-btn-focus");
26498         }
26499     },
26500     // private
26501     onBlur : function(e){
26502         this.el.removeClass("x-btn-focus");
26503     },
26504     // private
26505     onMouseDown : function(e){
26506         if(!this.disabled && e.button == 0){
26507             this.el.addClass("x-btn-click");
26508             Roo.get(document).on('mouseup', this.onMouseUp, this);
26509         }
26510     },
26511     // private
26512     onMouseUp : function(e){
26513         if(e.button == 0){
26514             this.el.removeClass("x-btn-click");
26515             Roo.get(document).un('mouseup', this.onMouseUp, this);
26516         }
26517     },
26518     // private
26519     onMenuShow : function(e){
26520         this.el.addClass("x-btn-menu-active");
26521     },
26522     // private
26523     onMenuHide : function(e){
26524         this.el.removeClass("x-btn-menu-active");
26525     }   
26526 });
26527
26528 // Private utility class used by Button
26529 Roo.ButtonToggleMgr = function(){
26530    var groups = {};
26531    
26532    function toggleGroup(btn, state){
26533        if(state){
26534            var g = groups[btn.toggleGroup];
26535            for(var i = 0, l = g.length; i < l; i++){
26536                if(g[i] != btn){
26537                    g[i].toggle(false);
26538                }
26539            }
26540        }
26541    }
26542    
26543    return {
26544        register : function(btn){
26545            if(!btn.toggleGroup){
26546                return;
26547            }
26548            var g = groups[btn.toggleGroup];
26549            if(!g){
26550                g = groups[btn.toggleGroup] = [];
26551            }
26552            g.push(btn);
26553            btn.on("toggle", toggleGroup);
26554        },
26555        
26556        unregister : function(btn){
26557            if(!btn.toggleGroup){
26558                return;
26559            }
26560            var g = groups[btn.toggleGroup];
26561            if(g){
26562                g.remove(btn);
26563                btn.un("toggle", toggleGroup);
26564            }
26565        }
26566    };
26567 }();/*
26568  * Based on:
26569  * Ext JS Library 1.1.1
26570  * Copyright(c) 2006-2007, Ext JS, LLC.
26571  *
26572  * Originally Released Under LGPL - original licence link has changed is not relivant.
26573  *
26574  * Fork - LGPL
26575  * <script type="text/javascript">
26576  */
26577  
26578 /**
26579  * @class Roo.SplitButton
26580  * @extends Roo.Button
26581  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26582  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26583  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26584  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26585  * @cfg {String} arrowTooltip The title attribute of the arrow
26586  * @constructor
26587  * Create a new menu button
26588  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26589  * @param {Object} config The config object
26590  */
26591 Roo.SplitButton = function(renderTo, config){
26592     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26593     /**
26594      * @event arrowclick
26595      * Fires when this button's arrow is clicked
26596      * @param {SplitButton} this
26597      * @param {EventObject} e The click event
26598      */
26599     this.addEvents({"arrowclick":true});
26600 };
26601
26602 Roo.extend(Roo.SplitButton, Roo.Button, {
26603     render : function(renderTo){
26604         // this is one sweet looking template!
26605         var tpl = new Roo.Template(
26606             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26607             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26608             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26609             "</tbody></table></td><td>",
26610             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26611             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26612             "</tbody></table></td></tr></table>"
26613         );
26614         var btn = tpl.append(renderTo, [this.text, this.type], true);
26615         var btnEl = btn.child("button");
26616         if(this.cls){
26617             btn.addClass(this.cls);
26618         }
26619         if(this.icon){
26620             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26621         }
26622         if(this.iconCls){
26623             btnEl.addClass(this.iconCls);
26624             if(!this.cls){
26625                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26626             }
26627         }
26628         this.el = btn;
26629         if(this.handleMouseEvents){
26630             btn.on("mouseover", this.onMouseOver, this);
26631             btn.on("mouseout", this.onMouseOut, this);
26632             btn.on("mousedown", this.onMouseDown, this);
26633             btn.on("mouseup", this.onMouseUp, this);
26634         }
26635         btn.on(this.clickEvent, this.onClick, this);
26636         if(this.tooltip){
26637             if(typeof this.tooltip == 'object'){
26638                 Roo.QuickTips.tips(Roo.apply({
26639                       target: btnEl.id
26640                 }, this.tooltip));
26641             } else {
26642                 btnEl.dom[this.tooltipType] = this.tooltip;
26643             }
26644         }
26645         if(this.arrowTooltip){
26646             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26647         }
26648         if(this.hidden){
26649             this.hide();
26650         }
26651         if(this.disabled){
26652             this.disable();
26653         }
26654         if(this.pressed){
26655             this.el.addClass("x-btn-pressed");
26656         }
26657         if(Roo.isIE && !Roo.isIE7){
26658             this.autoWidth.defer(1, this);
26659         }else{
26660             this.autoWidth();
26661         }
26662         if(this.menu){
26663             this.menu.on("show", this.onMenuShow, this);
26664             this.menu.on("hide", this.onMenuHide, this);
26665         }
26666         this.fireEvent('render', this);
26667     },
26668
26669     // private
26670     autoWidth : function(){
26671         if(this.el){
26672             var tbl = this.el.child("table:first");
26673             var tbl2 = this.el.child("table:last");
26674             this.el.setWidth("auto");
26675             tbl.setWidth("auto");
26676             if(Roo.isIE7 && Roo.isStrict){
26677                 var ib = this.el.child('button:first');
26678                 if(ib && ib.getWidth() > 20){
26679                     ib.clip();
26680                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26681                 }
26682             }
26683             if(this.minWidth){
26684                 if(this.hidden){
26685                     this.el.beginMeasure();
26686                 }
26687                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26688                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26689                 }
26690                 if(this.hidden){
26691                     this.el.endMeasure();
26692                 }
26693             }
26694             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26695         } 
26696     },
26697     /**
26698      * Sets this button's click handler
26699      * @param {Function} handler The function to call when the button is clicked
26700      * @param {Object} scope (optional) Scope for the function passed above
26701      */
26702     setHandler : function(handler, scope){
26703         this.handler = handler;
26704         this.scope = scope;  
26705     },
26706     
26707     /**
26708      * Sets this button's arrow click handler
26709      * @param {Function} handler The function to call when the arrow is clicked
26710      * @param {Object} scope (optional) Scope for the function passed above
26711      */
26712     setArrowHandler : function(handler, scope){
26713         this.arrowHandler = handler;
26714         this.scope = scope;  
26715     },
26716     
26717     /**
26718      * Focus the button
26719      */
26720     focus : function(){
26721         if(this.el){
26722             this.el.child("button:first").focus();
26723         }
26724     },
26725
26726     // private
26727     onClick : function(e){
26728         e.preventDefault();
26729         if(!this.disabled){
26730             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26731                 if(this.menu && !this.menu.isVisible()){
26732                     this.menu.show(this.el, this.menuAlign);
26733                 }
26734                 this.fireEvent("arrowclick", this, e);
26735                 if(this.arrowHandler){
26736                     this.arrowHandler.call(this.scope || this, this, e);
26737                 }
26738             }else{
26739                 this.fireEvent("click", this, e);
26740                 if(this.handler){
26741                     this.handler.call(this.scope || this, this, e);
26742                 }
26743             }
26744         }
26745     },
26746     // private
26747     onMouseDown : function(e){
26748         if(!this.disabled){
26749             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26750         }
26751     },
26752     // private
26753     onMouseUp : function(e){
26754         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26755     }   
26756 });
26757
26758
26759 // backwards compat
26760 Roo.MenuButton = Roo.SplitButton;/*
26761  * Based on:
26762  * Ext JS Library 1.1.1
26763  * Copyright(c) 2006-2007, Ext JS, LLC.
26764  *
26765  * Originally Released Under LGPL - original licence link has changed is not relivant.
26766  *
26767  * Fork - LGPL
26768  * <script type="text/javascript">
26769  */
26770
26771 /**
26772  * @class Roo.Toolbar
26773  * Basic Toolbar class.
26774  * @constructor
26775  * Creates a new Toolbar
26776  * @param {Object} container The config object
26777  */ 
26778 Roo.Toolbar = function(container, buttons, config)
26779 {
26780     /// old consturctor format still supported..
26781     if(container instanceof Array){ // omit the container for later rendering
26782         buttons = container;
26783         config = buttons;
26784         container = null;
26785     }
26786     if (typeof(container) == 'object' && container.xtype) {
26787         config = container;
26788         container = config.container;
26789         buttons = config.buttons || []; // not really - use items!!
26790     }
26791     var xitems = [];
26792     if (config && config.items) {
26793         xitems = config.items;
26794         delete config.items;
26795     }
26796     Roo.apply(this, config);
26797     this.buttons = buttons;
26798     
26799     if(container){
26800         this.render(container);
26801     }
26802     this.xitems = xitems;
26803     Roo.each(xitems, function(b) {
26804         this.add(b);
26805     }, this);
26806     
26807 };
26808
26809 Roo.Toolbar.prototype = {
26810     /**
26811      * @cfg {Array} items
26812      * array of button configs or elements to add (will be converted to a MixedCollection)
26813      */
26814     
26815     /**
26816      * @cfg {String/HTMLElement/Element} container
26817      * The id or element that will contain the toolbar
26818      */
26819     // private
26820     render : function(ct){
26821         this.el = Roo.get(ct);
26822         if(this.cls){
26823             this.el.addClass(this.cls);
26824         }
26825         // using a table allows for vertical alignment
26826         // 100% width is needed by Safari...
26827         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26828         this.tr = this.el.child("tr", true);
26829         var autoId = 0;
26830         this.items = new Roo.util.MixedCollection(false, function(o){
26831             return o.id || ("item" + (++autoId));
26832         });
26833         if(this.buttons){
26834             this.add.apply(this, this.buttons);
26835             delete this.buttons;
26836         }
26837     },
26838
26839     /**
26840      * Adds element(s) to the toolbar -- this function takes a variable number of 
26841      * arguments of mixed type and adds them to the toolbar.
26842      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26843      * <ul>
26844      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26845      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26846      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26847      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26848      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26849      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26850      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26851      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26852      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26853      * </ul>
26854      * @param {Mixed} arg2
26855      * @param {Mixed} etc.
26856      */
26857     add : function(){
26858         var a = arguments, l = a.length;
26859         for(var i = 0; i < l; i++){
26860             this._add(a[i]);
26861         }
26862     },
26863     // private..
26864     _add : function(el) {
26865         
26866         if (el.xtype) {
26867             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26868         }
26869         
26870         if (el.applyTo){ // some kind of form field
26871             return this.addField(el);
26872         } 
26873         if (el.render){ // some kind of Toolbar.Item
26874             return this.addItem(el);
26875         }
26876         if (typeof el == "string"){ // string
26877             if(el == "separator" || el == "-"){
26878                 return this.addSeparator();
26879             }
26880             if (el == " "){
26881                 return this.addSpacer();
26882             }
26883             if(el == "->"){
26884                 return this.addFill();
26885             }
26886             return this.addText(el);
26887             
26888         }
26889         if(el.tagName){ // element
26890             return this.addElement(el);
26891         }
26892         if(typeof el == "object"){ // must be button config?
26893             return this.addButton(el);
26894         }
26895         // and now what?!?!
26896         return false;
26897         
26898     },
26899     
26900     /**
26901      * Add an Xtype element
26902      * @param {Object} xtype Xtype Object
26903      * @return {Object} created Object
26904      */
26905     addxtype : function(e){
26906         return this.add(e);  
26907     },
26908     
26909     /**
26910      * Returns the Element for this toolbar.
26911      * @return {Roo.Element}
26912      */
26913     getEl : function(){
26914         return this.el;  
26915     },
26916     
26917     /**
26918      * Adds a separator
26919      * @return {Roo.Toolbar.Item} The separator item
26920      */
26921     addSeparator : function(){
26922         return this.addItem(new Roo.Toolbar.Separator());
26923     },
26924
26925     /**
26926      * Adds a spacer element
26927      * @return {Roo.Toolbar.Spacer} The spacer item
26928      */
26929     addSpacer : function(){
26930         return this.addItem(new Roo.Toolbar.Spacer());
26931     },
26932
26933     /**
26934      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26935      * @return {Roo.Toolbar.Fill} The fill item
26936      */
26937     addFill : function(){
26938         return this.addItem(new Roo.Toolbar.Fill());
26939     },
26940
26941     /**
26942      * Adds any standard HTML element to the toolbar
26943      * @param {String/HTMLElement/Element} el The element or id of the element to add
26944      * @return {Roo.Toolbar.Item} The element's item
26945      */
26946     addElement : function(el){
26947         return this.addItem(new Roo.Toolbar.Item(el));
26948     },
26949     /**
26950      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26951      * @type Roo.util.MixedCollection  
26952      */
26953     items : false,
26954      
26955     /**
26956      * Adds any Toolbar.Item or subclass
26957      * @param {Roo.Toolbar.Item} item
26958      * @return {Roo.Toolbar.Item} The item
26959      */
26960     addItem : function(item){
26961         var td = this.nextBlock();
26962         item.render(td);
26963         this.items.add(item);
26964         return item;
26965     },
26966     
26967     /**
26968      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26969      * @param {Object/Array} config A button config or array of configs
26970      * @return {Roo.Toolbar.Button/Array}
26971      */
26972     addButton : function(config){
26973         if(config instanceof Array){
26974             var buttons = [];
26975             for(var i = 0, len = config.length; i < len; i++) {
26976                 buttons.push(this.addButton(config[i]));
26977             }
26978             return buttons;
26979         }
26980         var b = config;
26981         if(!(config instanceof Roo.Toolbar.Button)){
26982             b = config.split ?
26983                 new Roo.Toolbar.SplitButton(config) :
26984                 new Roo.Toolbar.Button(config);
26985         }
26986         var td = this.nextBlock();
26987         b.render(td);
26988         this.items.add(b);
26989         return b;
26990     },
26991     
26992     /**
26993      * Adds text to the toolbar
26994      * @param {String} text The text to add
26995      * @return {Roo.Toolbar.Item} The element's item
26996      */
26997     addText : function(text){
26998         return this.addItem(new Roo.Toolbar.TextItem(text));
26999     },
27000     
27001     /**
27002      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27003      * @param {Number} index The index where the item is to be inserted
27004      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27005      * @return {Roo.Toolbar.Button/Item}
27006      */
27007     insertButton : function(index, item){
27008         if(item instanceof Array){
27009             var buttons = [];
27010             for(var i = 0, len = item.length; i < len; i++) {
27011                buttons.push(this.insertButton(index + i, item[i]));
27012             }
27013             return buttons;
27014         }
27015         if (!(item instanceof Roo.Toolbar.Button)){
27016            item = new Roo.Toolbar.Button(item);
27017         }
27018         var td = document.createElement("td");
27019         this.tr.insertBefore(td, this.tr.childNodes[index]);
27020         item.render(td);
27021         this.items.insert(index, item);
27022         return item;
27023     },
27024     
27025     /**
27026      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27027      * @param {Object} config
27028      * @return {Roo.Toolbar.Item} The element's item
27029      */
27030     addDom : function(config, returnEl){
27031         var td = this.nextBlock();
27032         Roo.DomHelper.overwrite(td, config);
27033         var ti = new Roo.Toolbar.Item(td.firstChild);
27034         ti.render(td);
27035         this.items.add(ti);
27036         return ti;
27037     },
27038
27039     /**
27040      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27041      * @type Roo.util.MixedCollection  
27042      */
27043     fields : false,
27044     
27045     /**
27046      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27047      * Note: the field should not have been rendered yet. For a field that has already been
27048      * rendered, use {@link #addElement}.
27049      * @param {Roo.form.Field} field
27050      * @return {Roo.ToolbarItem}
27051      */
27052      
27053       
27054     addField : function(field) {
27055         if (!this.fields) {
27056             var autoId = 0;
27057             this.fields = new Roo.util.MixedCollection(false, function(o){
27058                 return o.id || ("item" + (++autoId));
27059             });
27060
27061         }
27062         
27063         var td = this.nextBlock();
27064         field.render(td);
27065         var ti = new Roo.Toolbar.Item(td.firstChild);
27066         ti.render(td);
27067         this.items.add(ti);
27068         this.fields.add(field);
27069         return ti;
27070     },
27071     /**
27072      * Hide the toolbar
27073      * @method hide
27074      */
27075      
27076       
27077     hide : function()
27078     {
27079         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27080         this.el.child('div').hide();
27081     },
27082     /**
27083      * Show the toolbar
27084      * @method show
27085      */
27086     show : function()
27087     {
27088         this.el.child('div').show();
27089     },
27090       
27091     // private
27092     nextBlock : function(){
27093         var td = document.createElement("td");
27094         this.tr.appendChild(td);
27095         return td;
27096     },
27097
27098     // private
27099     destroy : function(){
27100         if(this.items){ // rendered?
27101             Roo.destroy.apply(Roo, this.items.items);
27102         }
27103         if(this.fields){ // rendered?
27104             Roo.destroy.apply(Roo, this.fields.items);
27105         }
27106         Roo.Element.uncache(this.el, this.tr);
27107     }
27108 };
27109
27110 /**
27111  * @class Roo.Toolbar.Item
27112  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27113  * @constructor
27114  * Creates a new Item
27115  * @param {HTMLElement} el 
27116  */
27117 Roo.Toolbar.Item = function(el){
27118     this.el = Roo.getDom(el);
27119     this.id = Roo.id(this.el);
27120     this.hidden = false;
27121 };
27122
27123 Roo.Toolbar.Item.prototype = {
27124     
27125     /**
27126      * Get this item's HTML Element
27127      * @return {HTMLElement}
27128      */
27129     getEl : function(){
27130        return this.el;  
27131     },
27132
27133     // private
27134     render : function(td){
27135         this.td = td;
27136         td.appendChild(this.el);
27137     },
27138     
27139     /**
27140      * Removes and destroys this item.
27141      */
27142     destroy : function(){
27143         this.td.parentNode.removeChild(this.td);
27144     },
27145     
27146     /**
27147      * Shows this item.
27148      */
27149     show: function(){
27150         this.hidden = false;
27151         this.td.style.display = "";
27152     },
27153     
27154     /**
27155      * Hides this item.
27156      */
27157     hide: function(){
27158         this.hidden = true;
27159         this.td.style.display = "none";
27160     },
27161     
27162     /**
27163      * Convenience function for boolean show/hide.
27164      * @param {Boolean} visible true to show/false to hide
27165      */
27166     setVisible: function(visible){
27167         if(visible) {
27168             this.show();
27169         }else{
27170             this.hide();
27171         }
27172     },
27173     
27174     /**
27175      * Try to focus this item.
27176      */
27177     focus : function(){
27178         Roo.fly(this.el).focus();
27179     },
27180     
27181     /**
27182      * Disables this item.
27183      */
27184     disable : function(){
27185         Roo.fly(this.td).addClass("x-item-disabled");
27186         this.disabled = true;
27187         this.el.disabled = true;
27188     },
27189     
27190     /**
27191      * Enables this item.
27192      */
27193     enable : function(){
27194         Roo.fly(this.td).removeClass("x-item-disabled");
27195         this.disabled = false;
27196         this.el.disabled = false;
27197     }
27198 };
27199
27200
27201 /**
27202  * @class Roo.Toolbar.Separator
27203  * @extends Roo.Toolbar.Item
27204  * A simple toolbar separator class
27205  * @constructor
27206  * Creates a new Separator
27207  */
27208 Roo.Toolbar.Separator = function(){
27209     var s = document.createElement("span");
27210     s.className = "ytb-sep";
27211     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27212 };
27213 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27214     enable:Roo.emptyFn,
27215     disable:Roo.emptyFn,
27216     focus:Roo.emptyFn
27217 });
27218
27219 /**
27220  * @class Roo.Toolbar.Spacer
27221  * @extends Roo.Toolbar.Item
27222  * A simple element that adds extra horizontal space to a toolbar.
27223  * @constructor
27224  * Creates a new Spacer
27225  */
27226 Roo.Toolbar.Spacer = function(){
27227     var s = document.createElement("div");
27228     s.className = "ytb-spacer";
27229     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27230 };
27231 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27232     enable:Roo.emptyFn,
27233     disable:Roo.emptyFn,
27234     focus:Roo.emptyFn
27235 });
27236
27237 /**
27238  * @class Roo.Toolbar.Fill
27239  * @extends Roo.Toolbar.Spacer
27240  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27241  * @constructor
27242  * Creates a new Spacer
27243  */
27244 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27245     // private
27246     render : function(td){
27247         td.style.width = '100%';
27248         Roo.Toolbar.Fill.superclass.render.call(this, td);
27249     }
27250 });
27251
27252 /**
27253  * @class Roo.Toolbar.TextItem
27254  * @extends Roo.Toolbar.Item
27255  * A simple class that renders text directly into a toolbar.
27256  * @constructor
27257  * Creates a new TextItem
27258  * @param {String} text
27259  */
27260 Roo.Toolbar.TextItem = function(text){
27261     if (typeof(text) == 'object') {
27262         text = text.text;
27263     }
27264     var s = document.createElement("span");
27265     s.className = "ytb-text";
27266     s.innerHTML = text;
27267     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27268 };
27269 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27270     enable:Roo.emptyFn,
27271     disable:Roo.emptyFn,
27272     focus:Roo.emptyFn
27273 });
27274
27275 /**
27276  * @class Roo.Toolbar.Button
27277  * @extends Roo.Button
27278  * A button that renders into a toolbar.
27279  * @constructor
27280  * Creates a new Button
27281  * @param {Object} config A standard {@link Roo.Button} config object
27282  */
27283 Roo.Toolbar.Button = function(config){
27284     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27285 };
27286 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27287     render : function(td){
27288         this.td = td;
27289         Roo.Toolbar.Button.superclass.render.call(this, td);
27290     },
27291     
27292     /**
27293      * Removes and destroys this button
27294      */
27295     destroy : function(){
27296         Roo.Toolbar.Button.superclass.destroy.call(this);
27297         this.td.parentNode.removeChild(this.td);
27298     },
27299     
27300     /**
27301      * Shows this button
27302      */
27303     show: function(){
27304         this.hidden = false;
27305         this.td.style.display = "";
27306     },
27307     
27308     /**
27309      * Hides this button
27310      */
27311     hide: function(){
27312         this.hidden = true;
27313         this.td.style.display = "none";
27314     },
27315
27316     /**
27317      * Disables this item
27318      */
27319     disable : function(){
27320         Roo.fly(this.td).addClass("x-item-disabled");
27321         this.disabled = true;
27322     },
27323
27324     /**
27325      * Enables this item
27326      */
27327     enable : function(){
27328         Roo.fly(this.td).removeClass("x-item-disabled");
27329         this.disabled = false;
27330     }
27331 });
27332 // backwards compat
27333 Roo.ToolbarButton = Roo.Toolbar.Button;
27334
27335 /**
27336  * @class Roo.Toolbar.SplitButton
27337  * @extends Roo.SplitButton
27338  * A menu button that renders into a toolbar.
27339  * @constructor
27340  * Creates a new SplitButton
27341  * @param {Object} config A standard {@link Roo.SplitButton} config object
27342  */
27343 Roo.Toolbar.SplitButton = function(config){
27344     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27345 };
27346 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27347     render : function(td){
27348         this.td = td;
27349         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27350     },
27351     
27352     /**
27353      * Removes and destroys this button
27354      */
27355     destroy : function(){
27356         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27357         this.td.parentNode.removeChild(this.td);
27358     },
27359     
27360     /**
27361      * Shows this button
27362      */
27363     show: function(){
27364         this.hidden = false;
27365         this.td.style.display = "";
27366     },
27367     
27368     /**
27369      * Hides this button
27370      */
27371     hide: function(){
27372         this.hidden = true;
27373         this.td.style.display = "none";
27374     }
27375 });
27376
27377 // backwards compat
27378 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27379  * Based on:
27380  * Ext JS Library 1.1.1
27381  * Copyright(c) 2006-2007, Ext JS, LLC.
27382  *
27383  * Originally Released Under LGPL - original licence link has changed is not relivant.
27384  *
27385  * Fork - LGPL
27386  * <script type="text/javascript">
27387  */
27388  
27389 /**
27390  * @class Roo.PagingToolbar
27391  * @extends Roo.Toolbar
27392  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27393  * @constructor
27394  * Create a new PagingToolbar
27395  * @param {Object} config The config object
27396  */
27397 Roo.PagingToolbar = function(el, ds, config)
27398 {
27399     // old args format still supported... - xtype is prefered..
27400     if (typeof(el) == 'object' && el.xtype) {
27401         // created from xtype...
27402         config = el;
27403         ds = el.dataSource;
27404         el = config.container;
27405     }
27406     var items = [];
27407     if (config.items) {
27408         items = config.items;
27409         config.items = [];
27410     }
27411     
27412     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27413     this.ds = ds;
27414     this.cursor = 0;
27415     this.renderButtons(this.el);
27416     this.bind(ds);
27417     
27418     // supprot items array.
27419    
27420     Roo.each(items, function(e) {
27421         this.add(Roo.factory(e));
27422     },this);
27423     
27424 };
27425
27426 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27427     /**
27428      * @cfg {Roo.data.Store} dataSource
27429      * The underlying data store providing the paged data
27430      */
27431     /**
27432      * @cfg {String/HTMLElement/Element} container
27433      * container The id or element that will contain the toolbar
27434      */
27435     /**
27436      * @cfg {Boolean} displayInfo
27437      * True to display the displayMsg (defaults to false)
27438      */
27439     /**
27440      * @cfg {Number} pageSize
27441      * The number of records to display per page (defaults to 20)
27442      */
27443     pageSize: 20,
27444     /**
27445      * @cfg {String} displayMsg
27446      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27447      */
27448     displayMsg : 'Displaying {0} - {1} of {2}',
27449     /**
27450      * @cfg {String} emptyMsg
27451      * The message to display when no records are found (defaults to "No data to display")
27452      */
27453     emptyMsg : 'No data to display',
27454     /**
27455      * Customizable piece of the default paging text (defaults to "Page")
27456      * @type String
27457      */
27458     beforePageText : "Page",
27459     /**
27460      * Customizable piece of the default paging text (defaults to "of %0")
27461      * @type String
27462      */
27463     afterPageText : "of {0}",
27464     /**
27465      * Customizable piece of the default paging text (defaults to "First Page")
27466      * @type String
27467      */
27468     firstText : "First Page",
27469     /**
27470      * Customizable piece of the default paging text (defaults to "Previous Page")
27471      * @type String
27472      */
27473     prevText : "Previous Page",
27474     /**
27475      * Customizable piece of the default paging text (defaults to "Next Page")
27476      * @type String
27477      */
27478     nextText : "Next Page",
27479     /**
27480      * Customizable piece of the default paging text (defaults to "Last Page")
27481      * @type String
27482      */
27483     lastText : "Last Page",
27484     /**
27485      * Customizable piece of the default paging text (defaults to "Refresh")
27486      * @type String
27487      */
27488     refreshText : "Refresh",
27489
27490     // private
27491     renderButtons : function(el){
27492         Roo.PagingToolbar.superclass.render.call(this, el);
27493         this.first = this.addButton({
27494             tooltip: this.firstText,
27495             cls: "x-btn-icon x-grid-page-first",
27496             disabled: true,
27497             handler: this.onClick.createDelegate(this, ["first"])
27498         });
27499         this.prev = this.addButton({
27500             tooltip: this.prevText,
27501             cls: "x-btn-icon x-grid-page-prev",
27502             disabled: true,
27503             handler: this.onClick.createDelegate(this, ["prev"])
27504         });
27505         //this.addSeparator();
27506         this.add(this.beforePageText);
27507         this.field = Roo.get(this.addDom({
27508            tag: "input",
27509            type: "text",
27510            size: "3",
27511            value: "1",
27512            cls: "x-grid-page-number"
27513         }).el);
27514         this.field.on("keydown", this.onPagingKeydown, this);
27515         this.field.on("focus", function(){this.dom.select();});
27516         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27517         this.field.setHeight(18);
27518         //this.addSeparator();
27519         this.next = this.addButton({
27520             tooltip: this.nextText,
27521             cls: "x-btn-icon x-grid-page-next",
27522             disabled: true,
27523             handler: this.onClick.createDelegate(this, ["next"])
27524         });
27525         this.last = this.addButton({
27526             tooltip: this.lastText,
27527             cls: "x-btn-icon x-grid-page-last",
27528             disabled: true,
27529             handler: this.onClick.createDelegate(this, ["last"])
27530         });
27531         //this.addSeparator();
27532         this.loading = this.addButton({
27533             tooltip: this.refreshText,
27534             cls: "x-btn-icon x-grid-loading",
27535             handler: this.onClick.createDelegate(this, ["refresh"])
27536         });
27537
27538         if(this.displayInfo){
27539             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27540         }
27541     },
27542
27543     // private
27544     updateInfo : function(){
27545         if(this.displayEl){
27546             var count = this.ds.getCount();
27547             var msg = count == 0 ?
27548                 this.emptyMsg :
27549                 String.format(
27550                     this.displayMsg,
27551                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27552                 );
27553             this.displayEl.update(msg);
27554         }
27555     },
27556
27557     // private
27558     onLoad : function(ds, r, o){
27559        this.cursor = o.params ? o.params.start : 0;
27560        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27561
27562        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27563        this.field.dom.value = ap;
27564        this.first.setDisabled(ap == 1);
27565        this.prev.setDisabled(ap == 1);
27566        this.next.setDisabled(ap == ps);
27567        this.last.setDisabled(ap == ps);
27568        this.loading.enable();
27569        this.updateInfo();
27570     },
27571
27572     // private
27573     getPageData : function(){
27574         var total = this.ds.getTotalCount();
27575         return {
27576             total : total,
27577             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27578             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27579         };
27580     },
27581
27582     // private
27583     onLoadError : function(){
27584         this.loading.enable();
27585     },
27586
27587     // private
27588     onPagingKeydown : function(e){
27589         var k = e.getKey();
27590         var d = this.getPageData();
27591         if(k == e.RETURN){
27592             var v = this.field.dom.value, pageNum;
27593             if(!v || isNaN(pageNum = parseInt(v, 10))){
27594                 this.field.dom.value = d.activePage;
27595                 return;
27596             }
27597             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27598             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27599             e.stopEvent();
27600         }
27601         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27602         {
27603           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27604           this.field.dom.value = pageNum;
27605           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27606           e.stopEvent();
27607         }
27608         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27609         {
27610           var v = this.field.dom.value, pageNum; 
27611           var increment = (e.shiftKey) ? 10 : 1;
27612           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27613             increment *= -1;
27614           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27615             this.field.dom.value = d.activePage;
27616             return;
27617           }
27618           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27619           {
27620             this.field.dom.value = parseInt(v, 10) + increment;
27621             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623           }
27624           e.stopEvent();
27625         }
27626     },
27627
27628     // private
27629     beforeLoad : function(){
27630         if(this.loading){
27631             this.loading.disable();
27632         }
27633     },
27634
27635     // private
27636     onClick : function(which){
27637         var ds = this.ds;
27638         switch(which){
27639             case "first":
27640                 ds.load({params:{start: 0, limit: this.pageSize}});
27641             break;
27642             case "prev":
27643                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27644             break;
27645             case "next":
27646                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27647             break;
27648             case "last":
27649                 var total = ds.getTotalCount();
27650                 var extra = total % this.pageSize;
27651                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27652                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27653             break;
27654             case "refresh":
27655                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27656             break;
27657         }
27658     },
27659
27660     /**
27661      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27662      * @param {Roo.data.Store} store The data store to unbind
27663      */
27664     unbind : function(ds){
27665         ds.un("beforeload", this.beforeLoad, this);
27666         ds.un("load", this.onLoad, this);
27667         ds.un("loadexception", this.onLoadError, this);
27668         ds.un("remove", this.updateInfo, this);
27669         ds.un("add", this.updateInfo, this);
27670         this.ds = undefined;
27671     },
27672
27673     /**
27674      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27675      * @param {Roo.data.Store} store The data store to bind
27676      */
27677     bind : function(ds){
27678         ds.on("beforeload", this.beforeLoad, this);
27679         ds.on("load", this.onLoad, this);
27680         ds.on("loadexception", this.onLoadError, this);
27681         ds.on("remove", this.updateInfo, this);
27682         ds.on("add", this.updateInfo, this);
27683         this.ds = ds;
27684     }
27685 });/*
27686  * Based on:
27687  * Ext JS Library 1.1.1
27688  * Copyright(c) 2006-2007, Ext JS, LLC.
27689  *
27690  * Originally Released Under LGPL - original licence link has changed is not relivant.
27691  *
27692  * Fork - LGPL
27693  * <script type="text/javascript">
27694  */
27695
27696 /**
27697  * @class Roo.Resizable
27698  * @extends Roo.util.Observable
27699  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27700  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27701  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27702  * the element will be wrapped for you automatically.</p>
27703  * <p>Here is the list of valid resize handles:</p>
27704  * <pre>
27705 Value   Description
27706 ------  -------------------
27707  'n'     north
27708  's'     south
27709  'e'     east
27710  'w'     west
27711  'nw'    northwest
27712  'sw'    southwest
27713  'se'    southeast
27714  'ne'    northeast
27715  'hd'    horizontal drag
27716  'all'   all
27717 </pre>
27718  * <p>Here's an example showing the creation of a typical Resizable:</p>
27719  * <pre><code>
27720 var resizer = new Roo.Resizable("element-id", {
27721     handles: 'all',
27722     minWidth: 200,
27723     minHeight: 100,
27724     maxWidth: 500,
27725     maxHeight: 400,
27726     pinned: true
27727 });
27728 resizer.on("resize", myHandler);
27729 </code></pre>
27730  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27731  * resizer.east.setDisplayed(false);</p>
27732  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27733  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27734  * resize operation's new size (defaults to [0, 0])
27735  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27736  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27737  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27738  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27739  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27740  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27741  * @cfg {Number} width The width of the element in pixels (defaults to null)
27742  * @cfg {Number} height The height of the element in pixels (defaults to null)
27743  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27744  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27745  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27746  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27747  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27748  * in favor of the handles config option (defaults to false)
27749  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27750  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27751  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27752  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27753  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27754  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27755  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27756  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27757  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27758  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27759  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27760  * @constructor
27761  * Create a new resizable component
27762  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27763  * @param {Object} config configuration options
27764   */
27765 Roo.Resizable = function(el, config)
27766 {
27767     this.el = Roo.get(el);
27768
27769     if(config && config.wrap){
27770         config.resizeChild = this.el;
27771         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27772         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27773         this.el.setStyle("overflow", "hidden");
27774         this.el.setPositioning(config.resizeChild.getPositioning());
27775         config.resizeChild.clearPositioning();
27776         if(!config.width || !config.height){
27777             var csize = config.resizeChild.getSize();
27778             this.el.setSize(csize.width, csize.height);
27779         }
27780         if(config.pinned && !config.adjustments){
27781             config.adjustments = "auto";
27782         }
27783     }
27784
27785     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27786     this.proxy.unselectable();
27787     this.proxy.enableDisplayMode('block');
27788
27789     Roo.apply(this, config);
27790
27791     if(this.pinned){
27792         this.disableTrackOver = true;
27793         this.el.addClass("x-resizable-pinned");
27794     }
27795     // if the element isn't positioned, make it relative
27796     var position = this.el.getStyle("position");
27797     if(position != "absolute" && position != "fixed"){
27798         this.el.setStyle("position", "relative");
27799     }
27800     if(!this.handles){ // no handles passed, must be legacy style
27801         this.handles = 's,e,se';
27802         if(this.multiDirectional){
27803             this.handles += ',n,w';
27804         }
27805     }
27806     if(this.handles == "all"){
27807         this.handles = "n s e w ne nw se sw";
27808     }
27809     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27810     var ps = Roo.Resizable.positions;
27811     for(var i = 0, len = hs.length; i < len; i++){
27812         if(hs[i] && ps[hs[i]]){
27813             var pos = ps[hs[i]];
27814             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27815         }
27816     }
27817     // legacy
27818     this.corner = this.southeast;
27819     
27820     // updateBox = the box can move..
27821     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27822         this.updateBox = true;
27823     }
27824
27825     this.activeHandle = null;
27826
27827     if(this.resizeChild){
27828         if(typeof this.resizeChild == "boolean"){
27829             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27830         }else{
27831             this.resizeChild = Roo.get(this.resizeChild, true);
27832         }
27833     }
27834     
27835     if(this.adjustments == "auto"){
27836         var rc = this.resizeChild;
27837         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27838         if(rc && (hw || hn)){
27839             rc.position("relative");
27840             rc.setLeft(hw ? hw.el.getWidth() : 0);
27841             rc.setTop(hn ? hn.el.getHeight() : 0);
27842         }
27843         this.adjustments = [
27844             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27845             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27846         ];
27847     }
27848
27849     if(this.draggable){
27850         this.dd = this.dynamic ?
27851             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27852         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27853     }
27854
27855     // public events
27856     this.addEvents({
27857         /**
27858          * @event beforeresize
27859          * Fired before resize is allowed. Set enabled to false to cancel resize.
27860          * @param {Roo.Resizable} this
27861          * @param {Roo.EventObject} e The mousedown event
27862          */
27863         "beforeresize" : true,
27864         /**
27865          * @event resize
27866          * Fired after a resize.
27867          * @param {Roo.Resizable} this
27868          * @param {Number} width The new width
27869          * @param {Number} height The new height
27870          * @param {Roo.EventObject} e The mouseup event
27871          */
27872         "resize" : true
27873     });
27874
27875     if(this.width !== null && this.height !== null){
27876         this.resizeTo(this.width, this.height);
27877     }else{
27878         this.updateChildSize();
27879     }
27880     if(Roo.isIE){
27881         this.el.dom.style.zoom = 1;
27882     }
27883     Roo.Resizable.superclass.constructor.call(this);
27884 };
27885
27886 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27887         resizeChild : false,
27888         adjustments : [0, 0],
27889         minWidth : 5,
27890         minHeight : 5,
27891         maxWidth : 10000,
27892         maxHeight : 10000,
27893         enabled : true,
27894         animate : false,
27895         duration : .35,
27896         dynamic : false,
27897         handles : false,
27898         multiDirectional : false,
27899         disableTrackOver : false,
27900         easing : 'easeOutStrong',
27901         widthIncrement : 0,
27902         heightIncrement : 0,
27903         pinned : false,
27904         width : null,
27905         height : null,
27906         preserveRatio : false,
27907         transparent: false,
27908         minX: 0,
27909         minY: 0,
27910         draggable: false,
27911
27912         /**
27913          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27914          */
27915         constrainTo: undefined,
27916         /**
27917          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27918          */
27919         resizeRegion: undefined,
27920
27921
27922     /**
27923      * Perform a manual resize
27924      * @param {Number} width
27925      * @param {Number} height
27926      */
27927     resizeTo : function(width, height){
27928         this.el.setSize(width, height);
27929         this.updateChildSize();
27930         this.fireEvent("resize", this, width, height, null);
27931     },
27932
27933     // private
27934     startSizing : function(e, handle){
27935         this.fireEvent("beforeresize", this, e);
27936         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27937
27938             if(!this.overlay){
27939                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27940                 this.overlay.unselectable();
27941                 this.overlay.enableDisplayMode("block");
27942                 this.overlay.on("mousemove", this.onMouseMove, this);
27943                 this.overlay.on("mouseup", this.onMouseUp, this);
27944             }
27945             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27946
27947             this.resizing = true;
27948             this.startBox = this.el.getBox();
27949             this.startPoint = e.getXY();
27950             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27951                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27952
27953             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27954             this.overlay.show();
27955
27956             if(this.constrainTo) {
27957                 var ct = Roo.get(this.constrainTo);
27958                 this.resizeRegion = ct.getRegion().adjust(
27959                     ct.getFrameWidth('t'),
27960                     ct.getFrameWidth('l'),
27961                     -ct.getFrameWidth('b'),
27962                     -ct.getFrameWidth('r')
27963                 );
27964             }
27965
27966             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27967             this.proxy.show();
27968             this.proxy.setBox(this.startBox);
27969             if(!this.dynamic){
27970                 this.proxy.setStyle('visibility', 'visible');
27971             }
27972         }
27973     },
27974
27975     // private
27976     onMouseDown : function(handle, e){
27977         if(this.enabled){
27978             e.stopEvent();
27979             this.activeHandle = handle;
27980             this.startSizing(e, handle);
27981         }
27982     },
27983
27984     // private
27985     onMouseUp : function(e){
27986         var size = this.resizeElement();
27987         this.resizing = false;
27988         this.handleOut();
27989         this.overlay.hide();
27990         this.proxy.hide();
27991         this.fireEvent("resize", this, size.width, size.height, e);
27992     },
27993
27994     // private
27995     updateChildSize : function(){
27996         if(this.resizeChild){
27997             var el = this.el;
27998             var child = this.resizeChild;
27999             var adj = this.adjustments;
28000             if(el.dom.offsetWidth){
28001                 var b = el.getSize(true);
28002                 child.setSize(b.width+adj[0], b.height+adj[1]);
28003             }
28004             // Second call here for IE
28005             // The first call enables instant resizing and
28006             // the second call corrects scroll bars if they
28007             // exist
28008             if(Roo.isIE){
28009                 setTimeout(function(){
28010                     if(el.dom.offsetWidth){
28011                         var b = el.getSize(true);
28012                         child.setSize(b.width+adj[0], b.height+adj[1]);
28013                     }
28014                 }, 10);
28015             }
28016         }
28017     },
28018
28019     // private
28020     snap : function(value, inc, min){
28021         if(!inc || !value) return value;
28022         var newValue = value;
28023         var m = value % inc;
28024         if(m > 0){
28025             if(m > (inc/2)){
28026                 newValue = value + (inc-m);
28027             }else{
28028                 newValue = value - m;
28029             }
28030         }
28031         return Math.max(min, newValue);
28032     },
28033
28034     // private
28035     resizeElement : function(){
28036         var box = this.proxy.getBox();
28037         if(this.updateBox){
28038             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28039         }else{
28040             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28041         }
28042         this.updateChildSize();
28043         if(!this.dynamic){
28044             this.proxy.hide();
28045         }
28046         return box;
28047     },
28048
28049     // private
28050     constrain : function(v, diff, m, mx){
28051         if(v - diff < m){
28052             diff = v - m;
28053         }else if(v - diff > mx){
28054             diff = mx - v;
28055         }
28056         return diff;
28057     },
28058
28059     // private
28060     onMouseMove : function(e){
28061         if(this.enabled){
28062             try{// try catch so if something goes wrong the user doesn't get hung
28063
28064             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28065                 return;
28066             }
28067
28068             //var curXY = this.startPoint;
28069             var curSize = this.curSize || this.startBox;
28070             var x = this.startBox.x, y = this.startBox.y;
28071             var ox = x, oy = y;
28072             var w = curSize.width, h = curSize.height;
28073             var ow = w, oh = h;
28074             var mw = this.minWidth, mh = this.minHeight;
28075             var mxw = this.maxWidth, mxh = this.maxHeight;
28076             var wi = this.widthIncrement;
28077             var hi = this.heightIncrement;
28078
28079             var eventXY = e.getXY();
28080             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28081             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28082
28083             var pos = this.activeHandle.position;
28084
28085             switch(pos){
28086                 case "east":
28087                     w += diffX;
28088                     w = Math.min(Math.max(mw, w), mxw);
28089                     break;
28090              
28091                 case "south":
28092                     h += diffY;
28093                     h = Math.min(Math.max(mh, h), mxh);
28094                     break;
28095                 case "southeast":
28096                     w += diffX;
28097                     h += diffY;
28098                     w = Math.min(Math.max(mw, w), mxw);
28099                     h = Math.min(Math.max(mh, h), mxh);
28100                     break;
28101                 case "north":
28102                     diffY = this.constrain(h, diffY, mh, mxh);
28103                     y += diffY;
28104                     h -= diffY;
28105                     break;
28106                 case "hdrag":
28107                     
28108                     if (wi) {
28109                         var adiffX = Math.abs(diffX);
28110                         var sub = (adiffX % wi); // how much 
28111                         if (sub > (wi/2)) { // far enough to snap
28112                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28113                         } else {
28114                             // remove difference.. 
28115                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28116                         }
28117                     }
28118                     x += diffX;
28119                     x = Math.max(this.minX, x);
28120                     break;
28121                 case "west":
28122                     diffX = this.constrain(w, diffX, mw, mxw);
28123                     x += diffX;
28124                     w -= diffX;
28125                     break;
28126                 case "northeast":
28127                     w += diffX;
28128                     w = Math.min(Math.max(mw, w), mxw);
28129                     diffY = this.constrain(h, diffY, mh, mxh);
28130                     y += diffY;
28131                     h -= diffY;
28132                     break;
28133                 case "northwest":
28134                     diffX = this.constrain(w, diffX, mw, mxw);
28135                     diffY = this.constrain(h, diffY, mh, mxh);
28136                     y += diffY;
28137                     h -= diffY;
28138                     x += diffX;
28139                     w -= diffX;
28140                     break;
28141                case "southwest":
28142                     diffX = this.constrain(w, diffX, mw, mxw);
28143                     h += diffY;
28144                     h = Math.min(Math.max(mh, h), mxh);
28145                     x += diffX;
28146                     w -= diffX;
28147                     break;
28148             }
28149
28150             var sw = this.snap(w, wi, mw);
28151             var sh = this.snap(h, hi, mh);
28152             if(sw != w || sh != h){
28153                 switch(pos){
28154                     case "northeast":
28155                         y -= sh - h;
28156                     break;
28157                     case "north":
28158                         y -= sh - h;
28159                         break;
28160                     case "southwest":
28161                         x -= sw - w;
28162                     break;
28163                     case "west":
28164                         x -= sw - w;
28165                         break;
28166                     case "northwest":
28167                         x -= sw - w;
28168                         y -= sh - h;
28169                     break;
28170                 }
28171                 w = sw;
28172                 h = sh;
28173             }
28174
28175             if(this.preserveRatio){
28176                 switch(pos){
28177                     case "southeast":
28178                     case "east":
28179                         h = oh * (w/ow);
28180                         h = Math.min(Math.max(mh, h), mxh);
28181                         w = ow * (h/oh);
28182                        break;
28183                     case "south":
28184                         w = ow * (h/oh);
28185                         w = Math.min(Math.max(mw, w), mxw);
28186                         h = oh * (w/ow);
28187                         break;
28188                     case "northeast":
28189                         w = ow * (h/oh);
28190                         w = Math.min(Math.max(mw, w), mxw);
28191                         h = oh * (w/ow);
28192                     break;
28193                     case "north":
28194                         var tw = w;
28195                         w = ow * (h/oh);
28196                         w = Math.min(Math.max(mw, w), mxw);
28197                         h = oh * (w/ow);
28198                         x += (tw - w) / 2;
28199                         break;
28200                     case "southwest":
28201                         h = oh * (w/ow);
28202                         h = Math.min(Math.max(mh, h), mxh);
28203                         var tw = w;
28204                         w = ow * (h/oh);
28205                         x += tw - w;
28206                         break;
28207                     case "west":
28208                         var th = h;
28209                         h = oh * (w/ow);
28210                         h = Math.min(Math.max(mh, h), mxh);
28211                         y += (th - h) / 2;
28212                         var tw = w;
28213                         w = ow * (h/oh);
28214                         x += tw - w;
28215                        break;
28216                     case "northwest":
28217                         var tw = w;
28218                         var th = h;
28219                         h = oh * (w/ow);
28220                         h = Math.min(Math.max(mh, h), mxh);
28221                         w = ow * (h/oh);
28222                         y += th - h;
28223                         x += tw - w;
28224                        break;
28225
28226                 }
28227             }
28228             if (pos == 'hdrag') {
28229                 w = ow;
28230             }
28231             this.proxy.setBounds(x, y, w, h);
28232             if(this.dynamic){
28233                 this.resizeElement();
28234             }
28235             }catch(e){}
28236         }
28237     },
28238
28239     // private
28240     handleOver : function(){
28241         if(this.enabled){
28242             this.el.addClass("x-resizable-over");
28243         }
28244     },
28245
28246     // private
28247     handleOut : function(){
28248         if(!this.resizing){
28249             this.el.removeClass("x-resizable-over");
28250         }
28251     },
28252
28253     /**
28254      * Returns the element this component is bound to.
28255      * @return {Roo.Element}
28256      */
28257     getEl : function(){
28258         return this.el;
28259     },
28260
28261     /**
28262      * Returns the resizeChild element (or null).
28263      * @return {Roo.Element}
28264      */
28265     getResizeChild : function(){
28266         return this.resizeChild;
28267     },
28268
28269     /**
28270      * Destroys this resizable. If the element was wrapped and
28271      * removeEl is not true then the element remains.
28272      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28273      */
28274     destroy : function(removeEl){
28275         this.proxy.remove();
28276         if(this.overlay){
28277             this.overlay.removeAllListeners();
28278             this.overlay.remove();
28279         }
28280         var ps = Roo.Resizable.positions;
28281         for(var k in ps){
28282             if(typeof ps[k] != "function" && this[ps[k]]){
28283                 var h = this[ps[k]];
28284                 h.el.removeAllListeners();
28285                 h.el.remove();
28286             }
28287         }
28288         if(removeEl){
28289             this.el.update("");
28290             this.el.remove();
28291         }
28292     }
28293 });
28294
28295 // private
28296 // hash to map config positions to true positions
28297 Roo.Resizable.positions = {
28298     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28299     hd: "hdrag"
28300 };
28301
28302 // private
28303 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28304     if(!this.tpl){
28305         // only initialize the template if resizable is used
28306         var tpl = Roo.DomHelper.createTemplate(
28307             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28308         );
28309         tpl.compile();
28310         Roo.Resizable.Handle.prototype.tpl = tpl;
28311     }
28312     this.position = pos;
28313     this.rz = rz;
28314     // show north drag fro topdra
28315     var handlepos = pos == 'hdrag' ? 'north' : pos;
28316     
28317     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28318     if (pos == 'hdrag') {
28319         this.el.setStyle('cursor', 'pointer');
28320     }
28321     this.el.unselectable();
28322     if(transparent){
28323         this.el.setOpacity(0);
28324     }
28325     this.el.on("mousedown", this.onMouseDown, this);
28326     if(!disableTrackOver){
28327         this.el.on("mouseover", this.onMouseOver, this);
28328         this.el.on("mouseout", this.onMouseOut, this);
28329     }
28330 };
28331
28332 // private
28333 Roo.Resizable.Handle.prototype = {
28334     afterResize : function(rz){
28335         // do nothing
28336     },
28337     // private
28338     onMouseDown : function(e){
28339         this.rz.onMouseDown(this, e);
28340     },
28341     // private
28342     onMouseOver : function(e){
28343         this.rz.handleOver(this, e);
28344     },
28345     // private
28346     onMouseOut : function(e){
28347         this.rz.handleOut(this, e);
28348     }
28349 };/*
28350  * Based on:
28351  * Ext JS Library 1.1.1
28352  * Copyright(c) 2006-2007, Ext JS, LLC.
28353  *
28354  * Originally Released Under LGPL - original licence link has changed is not relivant.
28355  *
28356  * Fork - LGPL
28357  * <script type="text/javascript">
28358  */
28359
28360 /**
28361  * @class Roo.Editor
28362  * @extends Roo.Component
28363  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28364  * @constructor
28365  * Create a new Editor
28366  * @param {Roo.form.Field} field The Field object (or descendant)
28367  * @param {Object} config The config object
28368  */
28369 Roo.Editor = function(field, config){
28370     Roo.Editor.superclass.constructor.call(this, config);
28371     this.field = field;
28372     this.addEvents({
28373         /**
28374              * @event beforestartedit
28375              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28376              * false from the handler of this event.
28377              * @param {Editor} this
28378              * @param {Roo.Element} boundEl The underlying element bound to this editor
28379              * @param {Mixed} value The field value being set
28380              */
28381         "beforestartedit" : true,
28382         /**
28383              * @event startedit
28384              * Fires when this editor is displayed
28385              * @param {Roo.Element} boundEl The underlying element bound to this editor
28386              * @param {Mixed} value The starting field value
28387              */
28388         "startedit" : true,
28389         /**
28390              * @event beforecomplete
28391              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28392              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28393              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28394              * event will not fire since no edit actually occurred.
28395              * @param {Editor} this
28396              * @param {Mixed} value The current field value
28397              * @param {Mixed} startValue The original field value
28398              */
28399         "beforecomplete" : true,
28400         /**
28401              * @event complete
28402              * Fires after editing is complete and any changed value has been written to the underlying field.
28403              * @param {Editor} this
28404              * @param {Mixed} value The current field value
28405              * @param {Mixed} startValue The original field value
28406              */
28407         "complete" : true,
28408         /**
28409          * @event specialkey
28410          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28411          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28412          * @param {Roo.form.Field} this
28413          * @param {Roo.EventObject} e The event object
28414          */
28415         "specialkey" : true
28416     });
28417 };
28418
28419 Roo.extend(Roo.Editor, Roo.Component, {
28420     /**
28421      * @cfg {Boolean/String} autosize
28422      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28423      * or "height" to adopt the height only (defaults to false)
28424      */
28425     /**
28426      * @cfg {Boolean} revertInvalid
28427      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28428      * validation fails (defaults to true)
28429      */
28430     /**
28431      * @cfg {Boolean} ignoreNoChange
28432      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28433      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28434      * will never be ignored.
28435      */
28436     /**
28437      * @cfg {Boolean} hideEl
28438      * False to keep the bound element visible while the editor is displayed (defaults to true)
28439      */
28440     /**
28441      * @cfg {Mixed} value
28442      * The data value of the underlying field (defaults to "")
28443      */
28444     value : "",
28445     /**
28446      * @cfg {String} alignment
28447      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28448      */
28449     alignment: "c-c?",
28450     /**
28451      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28452      * for bottom-right shadow (defaults to "frame")
28453      */
28454     shadow : "frame",
28455     /**
28456      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28457      */
28458     constrain : false,
28459     /**
28460      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28461      */
28462     completeOnEnter : false,
28463     /**
28464      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28465      */
28466     cancelOnEsc : false,
28467     /**
28468      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28469      */
28470     updateEl : false,
28471
28472     // private
28473     onRender : function(ct, position){
28474         this.el = new Roo.Layer({
28475             shadow: this.shadow,
28476             cls: "x-editor",
28477             parentEl : ct,
28478             shim : this.shim,
28479             shadowOffset:4,
28480             id: this.id,
28481             constrain: this.constrain
28482         });
28483         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28484         if(this.field.msgTarget != 'title'){
28485             this.field.msgTarget = 'qtip';
28486         }
28487         this.field.render(this.el);
28488         if(Roo.isGecko){
28489             this.field.el.dom.setAttribute('autocomplete', 'off');
28490         }
28491         this.field.on("specialkey", this.onSpecialKey, this);
28492         if(this.swallowKeys){
28493             this.field.el.swallowEvent(['keydown','keypress']);
28494         }
28495         this.field.show();
28496         this.field.on("blur", this.onBlur, this);
28497         if(this.field.grow){
28498             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28499         }
28500     },
28501
28502     onSpecialKey : function(field, e)
28503     {
28504         //Roo.log('editor onSpecialKey');
28505         if(this.completeOnEnter && e.getKey() == e.ENTER){
28506             e.stopEvent();
28507             this.completeEdit();
28508             return;
28509         }
28510         // do not fire special key otherwise it might hide close the editor...
28511         if(e.getKey() == e.ENTER){    
28512             return;
28513         }
28514         if(this.cancelOnEsc && e.getKey() == e.ESC){
28515             this.cancelEdit();
28516             return;
28517         } 
28518         this.fireEvent('specialkey', field, e);
28519     
28520     },
28521
28522     /**
28523      * Starts the editing process and shows the editor.
28524      * @param {String/HTMLElement/Element} el The element to edit
28525      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28526       * to the innerHTML of el.
28527      */
28528     startEdit : function(el, value){
28529         if(this.editing){
28530             this.completeEdit();
28531         }
28532         this.boundEl = Roo.get(el);
28533         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28534         if(!this.rendered){
28535             this.render(this.parentEl || document.body);
28536         }
28537         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28538             return;
28539         }
28540         this.startValue = v;
28541         this.field.setValue(v);
28542         if(this.autoSize){
28543             var sz = this.boundEl.getSize();
28544             switch(this.autoSize){
28545                 case "width":
28546                 this.setSize(sz.width,  "");
28547                 break;
28548                 case "height":
28549                 this.setSize("",  sz.height);
28550                 break;
28551                 default:
28552                 this.setSize(sz.width,  sz.height);
28553             }
28554         }
28555         this.el.alignTo(this.boundEl, this.alignment);
28556         this.editing = true;
28557         if(Roo.QuickTips){
28558             Roo.QuickTips.disable();
28559         }
28560         this.show();
28561     },
28562
28563     /**
28564      * Sets the height and width of this editor.
28565      * @param {Number} width The new width
28566      * @param {Number} height The new height
28567      */
28568     setSize : function(w, h){
28569         this.field.setSize(w, h);
28570         if(this.el){
28571             this.el.sync();
28572         }
28573     },
28574
28575     /**
28576      * Realigns the editor to the bound field based on the current alignment config value.
28577      */
28578     realign : function(){
28579         this.el.alignTo(this.boundEl, this.alignment);
28580     },
28581
28582     /**
28583      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28584      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28585      */
28586     completeEdit : function(remainVisible){
28587         if(!this.editing){
28588             return;
28589         }
28590         var v = this.getValue();
28591         if(this.revertInvalid !== false && !this.field.isValid()){
28592             v = this.startValue;
28593             this.cancelEdit(true);
28594         }
28595         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28596             this.editing = false;
28597             this.hide();
28598             return;
28599         }
28600         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28601             this.editing = false;
28602             if(this.updateEl && this.boundEl){
28603                 this.boundEl.update(v);
28604             }
28605             if(remainVisible !== true){
28606                 this.hide();
28607             }
28608             this.fireEvent("complete", this, v, this.startValue);
28609         }
28610     },
28611
28612     // private
28613     onShow : function(){
28614         this.el.show();
28615         if(this.hideEl !== false){
28616             this.boundEl.hide();
28617         }
28618         this.field.show();
28619         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28620             this.fixIEFocus = true;
28621             this.deferredFocus.defer(50, this);
28622         }else{
28623             this.field.focus();
28624         }
28625         this.fireEvent("startedit", this.boundEl, this.startValue);
28626     },
28627
28628     deferredFocus : function(){
28629         if(this.editing){
28630             this.field.focus();
28631         }
28632     },
28633
28634     /**
28635      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28636      * reverted to the original starting value.
28637      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28638      * cancel (defaults to false)
28639      */
28640     cancelEdit : function(remainVisible){
28641         if(this.editing){
28642             this.setValue(this.startValue);
28643             if(remainVisible !== true){
28644                 this.hide();
28645             }
28646         }
28647     },
28648
28649     // private
28650     onBlur : function(){
28651         if(this.allowBlur !== true && this.editing){
28652             this.completeEdit();
28653         }
28654     },
28655
28656     // private
28657     onHide : function(){
28658         if(this.editing){
28659             this.completeEdit();
28660             return;
28661         }
28662         this.field.blur();
28663         if(this.field.collapse){
28664             this.field.collapse();
28665         }
28666         this.el.hide();
28667         if(this.hideEl !== false){
28668             this.boundEl.show();
28669         }
28670         if(Roo.QuickTips){
28671             Roo.QuickTips.enable();
28672         }
28673     },
28674
28675     /**
28676      * Sets the data value of the editor
28677      * @param {Mixed} value Any valid value supported by the underlying field
28678      */
28679     setValue : function(v){
28680         this.field.setValue(v);
28681     },
28682
28683     /**
28684      * Gets the data value of the editor
28685      * @return {Mixed} The data value
28686      */
28687     getValue : function(){
28688         return this.field.getValue();
28689     }
28690 });/*
28691  * Based on:
28692  * Ext JS Library 1.1.1
28693  * Copyright(c) 2006-2007, Ext JS, LLC.
28694  *
28695  * Originally Released Under LGPL - original licence link has changed is not relivant.
28696  *
28697  * Fork - LGPL
28698  * <script type="text/javascript">
28699  */
28700  
28701 /**
28702  * @class Roo.BasicDialog
28703  * @extends Roo.util.Observable
28704  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28705  * <pre><code>
28706 var dlg = new Roo.BasicDialog("my-dlg", {
28707     height: 200,
28708     width: 300,
28709     minHeight: 100,
28710     minWidth: 150,
28711     modal: true,
28712     proxyDrag: true,
28713     shadow: true
28714 });
28715 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28716 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28717 dlg.addButton('Cancel', dlg.hide, dlg);
28718 dlg.show();
28719 </code></pre>
28720   <b>A Dialog should always be a direct child of the body element.</b>
28721  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28722  * @cfg {String} title Default text to display in the title bar (defaults to null)
28723  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28724  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28725  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28726  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28727  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28728  * (defaults to null with no animation)
28729  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28730  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28731  * property for valid values (defaults to 'all')
28732  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28733  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28734  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28735  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28736  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28737  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28738  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28739  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28740  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28741  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28742  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28743  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28744  * draggable = true (defaults to false)
28745  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28746  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28747  * shadow (defaults to false)
28748  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28749  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28750  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28751  * @cfg {Array} buttons Array of buttons
28752  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28753  * @constructor
28754  * Create a new BasicDialog.
28755  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28756  * @param {Object} config Configuration options
28757  */
28758 Roo.BasicDialog = function(el, config){
28759     this.el = Roo.get(el);
28760     var dh = Roo.DomHelper;
28761     if(!this.el && config && config.autoCreate){
28762         if(typeof config.autoCreate == "object"){
28763             if(!config.autoCreate.id){
28764                 config.autoCreate.id = el;
28765             }
28766             this.el = dh.append(document.body,
28767                         config.autoCreate, true);
28768         }else{
28769             this.el = dh.append(document.body,
28770                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28771         }
28772     }
28773     el = this.el;
28774     el.setDisplayed(true);
28775     el.hide = this.hideAction;
28776     this.id = el.id;
28777     el.addClass("x-dlg");
28778
28779     Roo.apply(this, config);
28780
28781     this.proxy = el.createProxy("x-dlg-proxy");
28782     this.proxy.hide = this.hideAction;
28783     this.proxy.setOpacity(.5);
28784     this.proxy.hide();
28785
28786     if(config.width){
28787         el.setWidth(config.width);
28788     }
28789     if(config.height){
28790         el.setHeight(config.height);
28791     }
28792     this.size = el.getSize();
28793     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28794         this.xy = [config.x,config.y];
28795     }else{
28796         this.xy = el.getCenterXY(true);
28797     }
28798     /** The header element @type Roo.Element */
28799     this.header = el.child("> .x-dlg-hd");
28800     /** The body element @type Roo.Element */
28801     this.body = el.child("> .x-dlg-bd");
28802     /** The footer element @type Roo.Element */
28803     this.footer = el.child("> .x-dlg-ft");
28804
28805     if(!this.header){
28806         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28807     }
28808     if(!this.body){
28809         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28810     }
28811
28812     this.header.unselectable();
28813     if(this.title){
28814         this.header.update(this.title);
28815     }
28816     // this element allows the dialog to be focused for keyboard event
28817     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28818     this.focusEl.swallowEvent("click", true);
28819
28820     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28821
28822     // wrap the body and footer for special rendering
28823     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28824     if(this.footer){
28825         this.bwrap.dom.appendChild(this.footer.dom);
28826     }
28827
28828     this.bg = this.el.createChild({
28829         tag: "div", cls:"x-dlg-bg",
28830         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28831     });
28832     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28833
28834
28835     if(this.autoScroll !== false && !this.autoTabs){
28836         this.body.setStyle("overflow", "auto");
28837     }
28838
28839     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28840
28841     if(this.closable !== false){
28842         this.el.addClass("x-dlg-closable");
28843         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28844         this.close.on("click", this.closeClick, this);
28845         this.close.addClassOnOver("x-dlg-close-over");
28846     }
28847     if(this.collapsible !== false){
28848         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28849         this.collapseBtn.on("click", this.collapseClick, this);
28850         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28851         this.header.on("dblclick", this.collapseClick, this);
28852     }
28853     if(this.resizable !== false){
28854         this.el.addClass("x-dlg-resizable");
28855         this.resizer = new Roo.Resizable(el, {
28856             minWidth: this.minWidth || 80,
28857             minHeight:this.minHeight || 80,
28858             handles: this.resizeHandles || "all",
28859             pinned: true
28860         });
28861         this.resizer.on("beforeresize", this.beforeResize, this);
28862         this.resizer.on("resize", this.onResize, this);
28863     }
28864     if(this.draggable !== false){
28865         el.addClass("x-dlg-draggable");
28866         if (!this.proxyDrag) {
28867             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28868         }
28869         else {
28870             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28871         }
28872         dd.setHandleElId(this.header.id);
28873         dd.endDrag = this.endMove.createDelegate(this);
28874         dd.startDrag = this.startMove.createDelegate(this);
28875         dd.onDrag = this.onDrag.createDelegate(this);
28876         dd.scroll = false;
28877         this.dd = dd;
28878     }
28879     if(this.modal){
28880         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28881         this.mask.enableDisplayMode("block");
28882         this.mask.hide();
28883         this.el.addClass("x-dlg-modal");
28884     }
28885     if(this.shadow){
28886         this.shadow = new Roo.Shadow({
28887             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28888             offset : this.shadowOffset
28889         });
28890     }else{
28891         this.shadowOffset = 0;
28892     }
28893     if(Roo.useShims && this.shim !== false){
28894         this.shim = this.el.createShim();
28895         this.shim.hide = this.hideAction;
28896         this.shim.hide();
28897     }else{
28898         this.shim = false;
28899     }
28900     if(this.autoTabs){
28901         this.initTabs();
28902     }
28903     if (this.buttons) { 
28904         var bts= this.buttons;
28905         this.buttons = [];
28906         Roo.each(bts, function(b) {
28907             this.addButton(b);
28908         }, this);
28909     }
28910     
28911     
28912     this.addEvents({
28913         /**
28914          * @event keydown
28915          * Fires when a key is pressed
28916          * @param {Roo.BasicDialog} this
28917          * @param {Roo.EventObject} e
28918          */
28919         "keydown" : true,
28920         /**
28921          * @event move
28922          * Fires when this dialog is moved by the user.
28923          * @param {Roo.BasicDialog} this
28924          * @param {Number} x The new page X
28925          * @param {Number} y The new page Y
28926          */
28927         "move" : true,
28928         /**
28929          * @event resize
28930          * Fires when this dialog is resized by the user.
28931          * @param {Roo.BasicDialog} this
28932          * @param {Number} width The new width
28933          * @param {Number} height The new height
28934          */
28935         "resize" : true,
28936         /**
28937          * @event beforehide
28938          * Fires before this dialog is hidden.
28939          * @param {Roo.BasicDialog} this
28940          */
28941         "beforehide" : true,
28942         /**
28943          * @event hide
28944          * Fires when this dialog is hidden.
28945          * @param {Roo.BasicDialog} this
28946          */
28947         "hide" : true,
28948         /**
28949          * @event beforeshow
28950          * Fires before this dialog is shown.
28951          * @param {Roo.BasicDialog} this
28952          */
28953         "beforeshow" : true,
28954         /**
28955          * @event show
28956          * Fires when this dialog is shown.
28957          * @param {Roo.BasicDialog} this
28958          */
28959         "show" : true
28960     });
28961     el.on("keydown", this.onKeyDown, this);
28962     el.on("mousedown", this.toFront, this);
28963     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28964     this.el.hide();
28965     Roo.DialogManager.register(this);
28966     Roo.BasicDialog.superclass.constructor.call(this);
28967 };
28968
28969 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28970     shadowOffset: Roo.isIE ? 6 : 5,
28971     minHeight: 80,
28972     minWidth: 200,
28973     minButtonWidth: 75,
28974     defaultButton: null,
28975     buttonAlign: "right",
28976     tabTag: 'div',
28977     firstShow: true,
28978
28979     /**
28980      * Sets the dialog title text
28981      * @param {String} text The title text to display
28982      * @return {Roo.BasicDialog} this
28983      */
28984     setTitle : function(text){
28985         this.header.update(text);
28986         return this;
28987     },
28988
28989     // private
28990     closeClick : function(){
28991         this.hide();
28992     },
28993
28994     // private
28995     collapseClick : function(){
28996         this[this.collapsed ? "expand" : "collapse"]();
28997     },
28998
28999     /**
29000      * Collapses the dialog to its minimized state (only the title bar is visible).
29001      * Equivalent to the user clicking the collapse dialog button.
29002      */
29003     collapse : function(){
29004         if(!this.collapsed){
29005             this.collapsed = true;
29006             this.el.addClass("x-dlg-collapsed");
29007             this.restoreHeight = this.el.getHeight();
29008             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29009         }
29010     },
29011
29012     /**
29013      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29014      * clicking the expand dialog button.
29015      */
29016     expand : function(){
29017         if(this.collapsed){
29018             this.collapsed = false;
29019             this.el.removeClass("x-dlg-collapsed");
29020             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29021         }
29022     },
29023
29024     /**
29025      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29026      * @return {Roo.TabPanel} The tabs component
29027      */
29028     initTabs : function(){
29029         var tabs = this.getTabs();
29030         while(tabs.getTab(0)){
29031             tabs.removeTab(0);
29032         }
29033         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29034             var dom = el.dom;
29035             tabs.addTab(Roo.id(dom), dom.title);
29036             dom.title = "";
29037         });
29038         tabs.activate(0);
29039         return tabs;
29040     },
29041
29042     // private
29043     beforeResize : function(){
29044         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29045     },
29046
29047     // private
29048     onResize : function(){
29049         this.refreshSize();
29050         this.syncBodyHeight();
29051         this.adjustAssets();
29052         this.focus();
29053         this.fireEvent("resize", this, this.size.width, this.size.height);
29054     },
29055
29056     // private
29057     onKeyDown : function(e){
29058         if(this.isVisible()){
29059             this.fireEvent("keydown", this, e);
29060         }
29061     },
29062
29063     /**
29064      * Resizes the dialog.
29065      * @param {Number} width
29066      * @param {Number} height
29067      * @return {Roo.BasicDialog} this
29068      */
29069     resizeTo : function(width, height){
29070         this.el.setSize(width, height);
29071         this.size = {width: width, height: height};
29072         this.syncBodyHeight();
29073         if(this.fixedcenter){
29074             this.center();
29075         }
29076         if(this.isVisible()){
29077             this.constrainXY();
29078             this.adjustAssets();
29079         }
29080         this.fireEvent("resize", this, width, height);
29081         return this;
29082     },
29083
29084
29085     /**
29086      * Resizes the dialog to fit the specified content size.
29087      * @param {Number} width
29088      * @param {Number} height
29089      * @return {Roo.BasicDialog} this
29090      */
29091     setContentSize : function(w, h){
29092         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29093         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29094         //if(!this.el.isBorderBox()){
29095             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29096             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29097         //}
29098         if(this.tabs){
29099             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29100             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29101         }
29102         this.resizeTo(w, h);
29103         return this;
29104     },
29105
29106     /**
29107      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29108      * executed in response to a particular key being pressed while the dialog is active.
29109      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29110      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29111      * @param {Function} fn The function to call
29112      * @param {Object} scope (optional) The scope of the function
29113      * @return {Roo.BasicDialog} this
29114      */
29115     addKeyListener : function(key, fn, scope){
29116         var keyCode, shift, ctrl, alt;
29117         if(typeof key == "object" && !(key instanceof Array)){
29118             keyCode = key["key"];
29119             shift = key["shift"];
29120             ctrl = key["ctrl"];
29121             alt = key["alt"];
29122         }else{
29123             keyCode = key;
29124         }
29125         var handler = function(dlg, e){
29126             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29127                 var k = e.getKey();
29128                 if(keyCode instanceof Array){
29129                     for(var i = 0, len = keyCode.length; i < len; i++){
29130                         if(keyCode[i] == k){
29131                           fn.call(scope || window, dlg, k, e);
29132                           return;
29133                         }
29134                     }
29135                 }else{
29136                     if(k == keyCode){
29137                         fn.call(scope || window, dlg, k, e);
29138                     }
29139                 }
29140             }
29141         };
29142         this.on("keydown", handler);
29143         return this;
29144     },
29145
29146     /**
29147      * Returns the TabPanel component (creates it if it doesn't exist).
29148      * Note: If you wish to simply check for the existence of tabs without creating them,
29149      * check for a null 'tabs' property.
29150      * @return {Roo.TabPanel} The tabs component
29151      */
29152     getTabs : function(){
29153         if(!this.tabs){
29154             this.el.addClass("x-dlg-auto-tabs");
29155             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29156             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29157         }
29158         return this.tabs;
29159     },
29160
29161     /**
29162      * Adds a button to the footer section of the dialog.
29163      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29164      * object or a valid Roo.DomHelper element config
29165      * @param {Function} handler The function called when the button is clicked
29166      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29167      * @return {Roo.Button} The new button
29168      */
29169     addButton : function(config, handler, scope){
29170         var dh = Roo.DomHelper;
29171         if(!this.footer){
29172             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29173         }
29174         if(!this.btnContainer){
29175             var tb = this.footer.createChild({
29176
29177                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29178                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29179             }, null, true);
29180             this.btnContainer = tb.firstChild.firstChild.firstChild;
29181         }
29182         var bconfig = {
29183             handler: handler,
29184             scope: scope,
29185             minWidth: this.minButtonWidth,
29186             hideParent:true
29187         };
29188         if(typeof config == "string"){
29189             bconfig.text = config;
29190         }else{
29191             if(config.tag){
29192                 bconfig.dhconfig = config;
29193             }else{
29194                 Roo.apply(bconfig, config);
29195             }
29196         }
29197         var fc = false;
29198         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29199             bconfig.position = Math.max(0, bconfig.position);
29200             fc = this.btnContainer.childNodes[bconfig.position];
29201         }
29202          
29203         var btn = new Roo.Button(
29204             fc ? 
29205                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29206                 : this.btnContainer.appendChild(document.createElement("td")),
29207             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29208             bconfig
29209         );
29210         this.syncBodyHeight();
29211         if(!this.buttons){
29212             /**
29213              * Array of all the buttons that have been added to this dialog via addButton
29214              * @type Array
29215              */
29216             this.buttons = [];
29217         }
29218         this.buttons.push(btn);
29219         return btn;
29220     },
29221
29222     /**
29223      * Sets the default button to be focused when the dialog is displayed.
29224      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29225      * @return {Roo.BasicDialog} this
29226      */
29227     setDefaultButton : function(btn){
29228         this.defaultButton = btn;
29229         return this;
29230     },
29231
29232     // private
29233     getHeaderFooterHeight : function(safe){
29234         var height = 0;
29235         if(this.header){
29236            height += this.header.getHeight();
29237         }
29238         if(this.footer){
29239            var fm = this.footer.getMargins();
29240             height += (this.footer.getHeight()+fm.top+fm.bottom);
29241         }
29242         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29243         height += this.centerBg.getPadding("tb");
29244         return height;
29245     },
29246
29247     // private
29248     syncBodyHeight : function(){
29249         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29250         var height = this.size.height - this.getHeaderFooterHeight(false);
29251         bd.setHeight(height-bd.getMargins("tb"));
29252         var hh = this.header.getHeight();
29253         var h = this.size.height-hh;
29254         cb.setHeight(h);
29255         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29256         bw.setHeight(h-cb.getPadding("tb"));
29257         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29258         bd.setWidth(bw.getWidth(true));
29259         if(this.tabs){
29260             this.tabs.syncHeight();
29261             if(Roo.isIE){
29262                 this.tabs.el.repaint();
29263             }
29264         }
29265     },
29266
29267     /**
29268      * Restores the previous state of the dialog if Roo.state is configured.
29269      * @return {Roo.BasicDialog} this
29270      */
29271     restoreState : function(){
29272         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29273         if(box && box.width){
29274             this.xy = [box.x, box.y];
29275             this.resizeTo(box.width, box.height);
29276         }
29277         return this;
29278     },
29279
29280     // private
29281     beforeShow : function(){
29282         this.expand();
29283         if(this.fixedcenter){
29284             this.xy = this.el.getCenterXY(true);
29285         }
29286         if(this.modal){
29287             Roo.get(document.body).addClass("x-body-masked");
29288             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29289             this.mask.show();
29290         }
29291         this.constrainXY();
29292     },
29293
29294     // private
29295     animShow : function(){
29296         var b = Roo.get(this.animateTarget).getBox();
29297         this.proxy.setSize(b.width, b.height);
29298         this.proxy.setLocation(b.x, b.y);
29299         this.proxy.show();
29300         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29301                     true, .35, this.showEl.createDelegate(this));
29302     },
29303
29304     /**
29305      * Shows the dialog.
29306      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29307      * @return {Roo.BasicDialog} this
29308      */
29309     show : function(animateTarget){
29310         if (this.fireEvent("beforeshow", this) === false){
29311             return;
29312         }
29313         if(this.syncHeightBeforeShow){
29314             this.syncBodyHeight();
29315         }else if(this.firstShow){
29316             this.firstShow = false;
29317             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29318         }
29319         this.animateTarget = animateTarget || this.animateTarget;
29320         if(!this.el.isVisible()){
29321             this.beforeShow();
29322             if(this.animateTarget && Roo.get(this.animateTarget)){
29323                 this.animShow();
29324             }else{
29325                 this.showEl();
29326             }
29327         }
29328         return this;
29329     },
29330
29331     // private
29332     showEl : function(){
29333         this.proxy.hide();
29334         this.el.setXY(this.xy);
29335         this.el.show();
29336         this.adjustAssets(true);
29337         this.toFront();
29338         this.focus();
29339         // IE peekaboo bug - fix found by Dave Fenwick
29340         if(Roo.isIE){
29341             this.el.repaint();
29342         }
29343         this.fireEvent("show", this);
29344     },
29345
29346     /**
29347      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29348      * dialog itself will receive focus.
29349      */
29350     focus : function(){
29351         if(this.defaultButton){
29352             this.defaultButton.focus();
29353         }else{
29354             this.focusEl.focus();
29355         }
29356     },
29357
29358     // private
29359     constrainXY : function(){
29360         if(this.constraintoviewport !== false){
29361             if(!this.viewSize){
29362                 if(this.container){
29363                     var s = this.container.getSize();
29364                     this.viewSize = [s.width, s.height];
29365                 }else{
29366                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29367                 }
29368             }
29369             var s = Roo.get(this.container||document).getScroll();
29370
29371             var x = this.xy[0], y = this.xy[1];
29372             var w = this.size.width, h = this.size.height;
29373             var vw = this.viewSize[0], vh = this.viewSize[1];
29374             // only move it if it needs it
29375             var moved = false;
29376             // first validate right/bottom
29377             if(x + w > vw+s.left){
29378                 x = vw - w;
29379                 moved = true;
29380             }
29381             if(y + h > vh+s.top){
29382                 y = vh - h;
29383                 moved = true;
29384             }
29385             // then make sure top/left isn't negative
29386             if(x < s.left){
29387                 x = s.left;
29388                 moved = true;
29389             }
29390             if(y < s.top){
29391                 y = s.top;
29392                 moved = true;
29393             }
29394             if(moved){
29395                 // cache xy
29396                 this.xy = [x, y];
29397                 if(this.isVisible()){
29398                     this.el.setLocation(x, y);
29399                     this.adjustAssets();
29400                 }
29401             }
29402         }
29403     },
29404
29405     // private
29406     onDrag : function(){
29407         if(!this.proxyDrag){
29408             this.xy = this.el.getXY();
29409             this.adjustAssets();
29410         }
29411     },
29412
29413     // private
29414     adjustAssets : function(doShow){
29415         var x = this.xy[0], y = this.xy[1];
29416         var w = this.size.width, h = this.size.height;
29417         if(doShow === true){
29418             if(this.shadow){
29419                 this.shadow.show(this.el);
29420             }
29421             if(this.shim){
29422                 this.shim.show();
29423             }
29424         }
29425         if(this.shadow && this.shadow.isVisible()){
29426             this.shadow.show(this.el);
29427         }
29428         if(this.shim && this.shim.isVisible()){
29429             this.shim.setBounds(x, y, w, h);
29430         }
29431     },
29432
29433     // private
29434     adjustViewport : function(w, h){
29435         if(!w || !h){
29436             w = Roo.lib.Dom.getViewWidth();
29437             h = Roo.lib.Dom.getViewHeight();
29438         }
29439         // cache the size
29440         this.viewSize = [w, h];
29441         if(this.modal && this.mask.isVisible()){
29442             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29443             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29444         }
29445         if(this.isVisible()){
29446             this.constrainXY();
29447         }
29448     },
29449
29450     /**
29451      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29452      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29453      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29454      */
29455     destroy : function(removeEl){
29456         if(this.isVisible()){
29457             this.animateTarget = null;
29458             this.hide();
29459         }
29460         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29461         if(this.tabs){
29462             this.tabs.destroy(removeEl);
29463         }
29464         Roo.destroy(
29465              this.shim,
29466              this.proxy,
29467              this.resizer,
29468              this.close,
29469              this.mask
29470         );
29471         if(this.dd){
29472             this.dd.unreg();
29473         }
29474         if(this.buttons){
29475            for(var i = 0, len = this.buttons.length; i < len; i++){
29476                this.buttons[i].destroy();
29477            }
29478         }
29479         this.el.removeAllListeners();
29480         if(removeEl === true){
29481             this.el.update("");
29482             this.el.remove();
29483         }
29484         Roo.DialogManager.unregister(this);
29485     },
29486
29487     // private
29488     startMove : function(){
29489         if(this.proxyDrag){
29490             this.proxy.show();
29491         }
29492         if(this.constraintoviewport !== false){
29493             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29494         }
29495     },
29496
29497     // private
29498     endMove : function(){
29499         if(!this.proxyDrag){
29500             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29501         }else{
29502             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29503             this.proxy.hide();
29504         }
29505         this.refreshSize();
29506         this.adjustAssets();
29507         this.focus();
29508         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29509     },
29510
29511     /**
29512      * Brings this dialog to the front of any other visible dialogs
29513      * @return {Roo.BasicDialog} this
29514      */
29515     toFront : function(){
29516         Roo.DialogManager.bringToFront(this);
29517         return this;
29518     },
29519
29520     /**
29521      * Sends this dialog to the back (under) of any other visible dialogs
29522      * @return {Roo.BasicDialog} this
29523      */
29524     toBack : function(){
29525         Roo.DialogManager.sendToBack(this);
29526         return this;
29527     },
29528
29529     /**
29530      * Centers this dialog in the viewport
29531      * @return {Roo.BasicDialog} this
29532      */
29533     center : function(){
29534         var xy = this.el.getCenterXY(true);
29535         this.moveTo(xy[0], xy[1]);
29536         return this;
29537     },
29538
29539     /**
29540      * Moves the dialog's top-left corner to the specified point
29541      * @param {Number} x
29542      * @param {Number} y
29543      * @return {Roo.BasicDialog} this
29544      */
29545     moveTo : function(x, y){
29546         this.xy = [x,y];
29547         if(this.isVisible()){
29548             this.el.setXY(this.xy);
29549             this.adjustAssets();
29550         }
29551         return this;
29552     },
29553
29554     /**
29555      * Aligns the dialog to the specified element
29556      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29557      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29558      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29559      * @return {Roo.BasicDialog} this
29560      */
29561     alignTo : function(element, position, offsets){
29562         this.xy = this.el.getAlignToXY(element, position, offsets);
29563         if(this.isVisible()){
29564             this.el.setXY(this.xy);
29565             this.adjustAssets();
29566         }
29567         return this;
29568     },
29569
29570     /**
29571      * Anchors an element to another element and realigns it when the window is resized.
29572      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29573      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29574      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29575      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29576      * is a number, it is used as the buffer delay (defaults to 50ms).
29577      * @return {Roo.BasicDialog} this
29578      */
29579     anchorTo : function(el, alignment, offsets, monitorScroll){
29580         var action = function(){
29581             this.alignTo(el, alignment, offsets);
29582         };
29583         Roo.EventManager.onWindowResize(action, this);
29584         var tm = typeof monitorScroll;
29585         if(tm != 'undefined'){
29586             Roo.EventManager.on(window, 'scroll', action, this,
29587                 {buffer: tm == 'number' ? monitorScroll : 50});
29588         }
29589         action.call(this);
29590         return this;
29591     },
29592
29593     /**
29594      * Returns true if the dialog is visible
29595      * @return {Boolean}
29596      */
29597     isVisible : function(){
29598         return this.el.isVisible();
29599     },
29600
29601     // private
29602     animHide : function(callback){
29603         var b = Roo.get(this.animateTarget).getBox();
29604         this.proxy.show();
29605         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29606         this.el.hide();
29607         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29608                     this.hideEl.createDelegate(this, [callback]));
29609     },
29610
29611     /**
29612      * Hides the dialog.
29613      * @param {Function} callback (optional) Function to call when the dialog is hidden
29614      * @return {Roo.BasicDialog} this
29615      */
29616     hide : function(callback){
29617         if (this.fireEvent("beforehide", this) === false){
29618             return;
29619         }
29620         if(this.shadow){
29621             this.shadow.hide();
29622         }
29623         if(this.shim) {
29624           this.shim.hide();
29625         }
29626         // sometimes animateTarget seems to get set.. causing problems...
29627         // this just double checks..
29628         if(this.animateTarget && Roo.get(this.animateTarget)) {
29629            this.animHide(callback);
29630         }else{
29631             this.el.hide();
29632             this.hideEl(callback);
29633         }
29634         return this;
29635     },
29636
29637     // private
29638     hideEl : function(callback){
29639         this.proxy.hide();
29640         if(this.modal){
29641             this.mask.hide();
29642             Roo.get(document.body).removeClass("x-body-masked");
29643         }
29644         this.fireEvent("hide", this);
29645         if(typeof callback == "function"){
29646             callback();
29647         }
29648     },
29649
29650     // private
29651     hideAction : function(){
29652         this.setLeft("-10000px");
29653         this.setTop("-10000px");
29654         this.setStyle("visibility", "hidden");
29655     },
29656
29657     // private
29658     refreshSize : function(){
29659         this.size = this.el.getSize();
29660         this.xy = this.el.getXY();
29661         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29662     },
29663
29664     // private
29665     // z-index is managed by the DialogManager and may be overwritten at any time
29666     setZIndex : function(index){
29667         if(this.modal){
29668             this.mask.setStyle("z-index", index);
29669         }
29670         if(this.shim){
29671             this.shim.setStyle("z-index", ++index);
29672         }
29673         if(this.shadow){
29674             this.shadow.setZIndex(++index);
29675         }
29676         this.el.setStyle("z-index", ++index);
29677         if(this.proxy){
29678             this.proxy.setStyle("z-index", ++index);
29679         }
29680         if(this.resizer){
29681             this.resizer.proxy.setStyle("z-index", ++index);
29682         }
29683
29684         this.lastZIndex = index;
29685     },
29686
29687     /**
29688      * Returns the element for this dialog
29689      * @return {Roo.Element} The underlying dialog Element
29690      */
29691     getEl : function(){
29692         return this.el;
29693     }
29694 });
29695
29696 /**
29697  * @class Roo.DialogManager
29698  * Provides global access to BasicDialogs that have been created and
29699  * support for z-indexing (layering) multiple open dialogs.
29700  */
29701 Roo.DialogManager = function(){
29702     var list = {};
29703     var accessList = [];
29704     var front = null;
29705
29706     // private
29707     var sortDialogs = function(d1, d2){
29708         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29709     };
29710
29711     // private
29712     var orderDialogs = function(){
29713         accessList.sort(sortDialogs);
29714         var seed = Roo.DialogManager.zseed;
29715         for(var i = 0, len = accessList.length; i < len; i++){
29716             var dlg = accessList[i];
29717             if(dlg){
29718                 dlg.setZIndex(seed + (i*10));
29719             }
29720         }
29721     };
29722
29723     return {
29724         /**
29725          * The starting z-index for BasicDialogs (defaults to 9000)
29726          * @type Number The z-index value
29727          */
29728         zseed : 9000,
29729
29730         // private
29731         register : function(dlg){
29732             list[dlg.id] = dlg;
29733             accessList.push(dlg);
29734         },
29735
29736         // private
29737         unregister : function(dlg){
29738             delete list[dlg.id];
29739             var i=0;
29740             var len=0;
29741             if(!accessList.indexOf){
29742                 for(  i = 0, len = accessList.length; i < len; i++){
29743                     if(accessList[i] == dlg){
29744                         accessList.splice(i, 1);
29745                         return;
29746                     }
29747                 }
29748             }else{
29749                  i = accessList.indexOf(dlg);
29750                 if(i != -1){
29751                     accessList.splice(i, 1);
29752                 }
29753             }
29754         },
29755
29756         /**
29757          * Gets a registered dialog by id
29758          * @param {String/Object} id The id of the dialog or a dialog
29759          * @return {Roo.BasicDialog} this
29760          */
29761         get : function(id){
29762             return typeof id == "object" ? id : list[id];
29763         },
29764
29765         /**
29766          * Brings the specified dialog to the front
29767          * @param {String/Object} dlg The id of the dialog or a dialog
29768          * @return {Roo.BasicDialog} this
29769          */
29770         bringToFront : function(dlg){
29771             dlg = this.get(dlg);
29772             if(dlg != front){
29773                 front = dlg;
29774                 dlg._lastAccess = new Date().getTime();
29775                 orderDialogs();
29776             }
29777             return dlg;
29778         },
29779
29780         /**
29781          * Sends the specified dialog to the back
29782          * @param {String/Object} dlg The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         sendToBack : function(dlg){
29786             dlg = this.get(dlg);
29787             dlg._lastAccess = -(new Date().getTime());
29788             orderDialogs();
29789             return dlg;
29790         },
29791
29792         /**
29793          * Hides all dialogs
29794          */
29795         hideAll : function(){
29796             for(var id in list){
29797                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29798                     list[id].hide();
29799                 }
29800             }
29801         }
29802     };
29803 }();
29804
29805 /**
29806  * @class Roo.LayoutDialog
29807  * @extends Roo.BasicDialog
29808  * Dialog which provides adjustments for working with a layout in a Dialog.
29809  * Add your necessary layout config options to the dialog's config.<br>
29810  * Example usage (including a nested layout):
29811  * <pre><code>
29812 if(!dialog){
29813     dialog = new Roo.LayoutDialog("download-dlg", {
29814         modal: true,
29815         width:600,
29816         height:450,
29817         shadow:true,
29818         minWidth:500,
29819         minHeight:350,
29820         autoTabs:true,
29821         proxyDrag:true,
29822         // layout config merges with the dialog config
29823         center:{
29824             tabPosition: "top",
29825             alwaysShowTabs: true
29826         }
29827     });
29828     dialog.addKeyListener(27, dialog.hide, dialog);
29829     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29830     dialog.addButton("Build It!", this.getDownload, this);
29831
29832     // we can even add nested layouts
29833     var innerLayout = new Roo.BorderLayout("dl-inner", {
29834         east: {
29835             initialSize: 200,
29836             autoScroll:true,
29837             split:true
29838         },
29839         center: {
29840             autoScroll:true
29841         }
29842     });
29843     innerLayout.beginUpdate();
29844     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29845     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29846     innerLayout.endUpdate(true);
29847
29848     var layout = dialog.getLayout();
29849     layout.beginUpdate();
29850     layout.add("center", new Roo.ContentPanel("standard-panel",
29851                         {title: "Download the Source", fitToFrame:true}));
29852     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29853                {title: "Build your own roo.js"}));
29854     layout.getRegion("center").showPanel(sp);
29855     layout.endUpdate();
29856 }
29857 </code></pre>
29858     * @constructor
29859     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29860     * @param {Object} config configuration options
29861   */
29862 Roo.LayoutDialog = function(el, cfg){
29863     
29864     var config=  cfg;
29865     if (typeof(cfg) == 'undefined') {
29866         config = Roo.apply({}, el);
29867         // not sure why we use documentElement here.. - it should always be body.
29868         // IE7 borks horribly if we use documentElement.
29869         // webkit also does not like documentElement - it creates a body element...
29870         el = Roo.get( document.body || document.documentElement ).createChild();
29871         //config.autoCreate = true;
29872     }
29873     
29874     
29875     config.autoTabs = false;
29876     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29877     this.body.setStyle({overflow:"hidden", position:"relative"});
29878     this.layout = new Roo.BorderLayout(this.body.dom, config);
29879     this.layout.monitorWindowResize = false;
29880     this.el.addClass("x-dlg-auto-layout");
29881     // fix case when center region overwrites center function
29882     this.center = Roo.BasicDialog.prototype.center;
29883     this.on("show", this.layout.layout, this.layout, true);
29884     if (config.items) {
29885         var xitems = config.items;
29886         delete config.items;
29887         Roo.each(xitems, this.addxtype, this);
29888     }
29889     
29890     
29891 };
29892 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29893     /**
29894      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29895      * @deprecated
29896      */
29897     endUpdate : function(){
29898         this.layout.endUpdate();
29899     },
29900
29901     /**
29902      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29903      *  @deprecated
29904      */
29905     beginUpdate : function(){
29906         this.layout.beginUpdate();
29907     },
29908
29909     /**
29910      * Get the BorderLayout for this dialog
29911      * @return {Roo.BorderLayout}
29912      */
29913     getLayout : function(){
29914         return this.layout;
29915     },
29916
29917     showEl : function(){
29918         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29919         if(Roo.isIE7){
29920             this.layout.layout();
29921         }
29922     },
29923
29924     // private
29925     // Use the syncHeightBeforeShow config option to control this automatically
29926     syncBodyHeight : function(){
29927         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29928         if(this.layout){this.layout.layout();}
29929     },
29930     
29931       /**
29932      * Add an xtype element (actually adds to the layout.)
29933      * @return {Object} xdata xtype object data.
29934      */
29935     
29936     addxtype : function(c) {
29937         return this.layout.addxtype(c);
29938     }
29939 });/*
29940  * Based on:
29941  * Ext JS Library 1.1.1
29942  * Copyright(c) 2006-2007, Ext JS, LLC.
29943  *
29944  * Originally Released Under LGPL - original licence link has changed is not relivant.
29945  *
29946  * Fork - LGPL
29947  * <script type="text/javascript">
29948  */
29949  
29950 /**
29951  * @class Roo.MessageBox
29952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29953  * Example usage:
29954  *<pre><code>
29955 // Basic alert:
29956 Roo.Msg.alert('Status', 'Changes saved successfully.');
29957
29958 // Prompt for user data:
29959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29960     if (btn == 'ok'){
29961         // process text value...
29962     }
29963 });
29964
29965 // Show a dialog using config options:
29966 Roo.Msg.show({
29967    title:'Save Changes?',
29968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29969    buttons: Roo.Msg.YESNOCANCEL,
29970    fn: processResult,
29971    animEl: 'elId'
29972 });
29973 </code></pre>
29974  * @singleton
29975  */
29976 Roo.MessageBox = function(){
29977     var dlg, opt, mask, waitTimer;
29978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29979     var buttons, activeTextEl, bwidth;
29980
29981     // private
29982     var handleButton = function(button){
29983         dlg.hide();
29984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29985     };
29986
29987     // private
29988     var handleHide = function(){
29989         if(opt && opt.cls){
29990             dlg.el.removeClass(opt.cls);
29991         }
29992         if(waitTimer){
29993             Roo.TaskMgr.stop(waitTimer);
29994             waitTimer = null;
29995         }
29996     };
29997
29998     // private
29999     var updateButtons = function(b){
30000         var width = 0;
30001         if(!b){
30002             buttons["ok"].hide();
30003             buttons["cancel"].hide();
30004             buttons["yes"].hide();
30005             buttons["no"].hide();
30006             dlg.footer.dom.style.display = 'none';
30007             return width;
30008         }
30009         dlg.footer.dom.style.display = '';
30010         for(var k in buttons){
30011             if(typeof buttons[k] != "function"){
30012                 if(b[k]){
30013                     buttons[k].show();
30014                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30015                     width += buttons[k].el.getWidth()+15;
30016                 }else{
30017                     buttons[k].hide();
30018                 }
30019             }
30020         }
30021         return width;
30022     };
30023
30024     // private
30025     var handleEsc = function(d, k, e){
30026         if(opt && opt.closable !== false){
30027             dlg.hide();
30028         }
30029         if(e){
30030             e.stopEvent();
30031         }
30032     };
30033
30034     return {
30035         /**
30036          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30037          * @return {Roo.BasicDialog} The BasicDialog element
30038          */
30039         getDialog : function(){
30040            if(!dlg){
30041                 dlg = new Roo.BasicDialog("x-msg-box", {
30042                     autoCreate : true,
30043                     shadow: true,
30044                     draggable: true,
30045                     resizable:false,
30046                     constraintoviewport:false,
30047                     fixedcenter:true,
30048                     collapsible : false,
30049                     shim:true,
30050                     modal: true,
30051                     width:400, height:100,
30052                     buttonAlign:"center",
30053                     closeClick : function(){
30054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30055                             handleButton("no");
30056                         }else{
30057                             handleButton("cancel");
30058                         }
30059                     }
30060                 });
30061                 dlg.on("hide", handleHide);
30062                 mask = dlg.mask;
30063                 dlg.addKeyListener(27, handleEsc);
30064                 buttons = {};
30065                 var bt = this.buttonText;
30066                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30067                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30068                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30069                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30070                 bodyEl = dlg.body.createChild({
30071
30072                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
30073                 });
30074                 msgEl = bodyEl.dom.firstChild;
30075                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30076                 textboxEl.enableDisplayMode();
30077                 textboxEl.addKeyListener([10,13], function(){
30078                     if(dlg.isVisible() && opt && opt.buttons){
30079                         if(opt.buttons.ok){
30080                             handleButton("ok");
30081                         }else if(opt.buttons.yes){
30082                             handleButton("yes");
30083                         }
30084                     }
30085                 });
30086                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30087                 textareaEl.enableDisplayMode();
30088                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30089                 progressEl.enableDisplayMode();
30090                 var pf = progressEl.dom.firstChild;
30091                 if (pf) {
30092                     pp = Roo.get(pf.firstChild);
30093                     pp.setHeight(pf.offsetHeight);
30094                 }
30095                 
30096             }
30097             return dlg;
30098         },
30099
30100         /**
30101          * Updates the message box body text
30102          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30103          * the XHTML-compliant non-breaking space character '&amp;#160;')
30104          * @return {Roo.MessageBox} This message box
30105          */
30106         updateText : function(text){
30107             if(!dlg.isVisible() && !opt.width){
30108                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30109             }
30110             msgEl.innerHTML = text || '&#160;';
30111       
30112             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30113             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30114             var w = Math.max(
30115                     Math.min(opt.width || cw , this.maxWidth), 
30116                     Math.max(opt.minWidth || this.minWidth, bwidth)
30117             );
30118             if(opt.prompt){
30119                 activeTextEl.setWidth(w);
30120             }
30121             if(dlg.isVisible()){
30122                 dlg.fixedcenter = false;
30123             }
30124             // to big, make it scroll. = But as usual stupid IE does not support
30125             // !important..
30126             
30127             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30128                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30129                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30130             } else {
30131                 bodyEl.dom.style.height = '';
30132                 bodyEl.dom.style.overflowY = '';
30133             }
30134             if (cw > w) {
30135                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30136             } else {
30137                 bodyEl.dom.style.overflowX = '';
30138             }
30139             
30140             dlg.setContentSize(w, bodyEl.getHeight());
30141             if(dlg.isVisible()){
30142                 dlg.fixedcenter = true;
30143             }
30144             return this;
30145         },
30146
30147         /**
30148          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30149          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30150          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30151          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30152          * @return {Roo.MessageBox} This message box
30153          */
30154         updateProgress : function(value, text){
30155             if(text){
30156                 this.updateText(text);
30157             }
30158             if (pp) { // weird bug on my firefox - for some reason this is not defined
30159                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30160             }
30161             return this;
30162         },        
30163
30164         /**
30165          * Returns true if the message box is currently displayed
30166          * @return {Boolean} True if the message box is visible, else false
30167          */
30168         isVisible : function(){
30169             return dlg && dlg.isVisible();  
30170         },
30171
30172         /**
30173          * Hides the message box if it is displayed
30174          */
30175         hide : function(){
30176             if(this.isVisible()){
30177                 dlg.hide();
30178             }  
30179         },
30180
30181         /**
30182          * Displays a new message box, or reinitializes an existing message box, based on the config options
30183          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30184          * The following config object properties are supported:
30185          * <pre>
30186 Property    Type             Description
30187 ----------  ---------------  ------------------------------------------------------------------------------------
30188 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30189                                    closes (defaults to undefined)
30190 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30191                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30192 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30193                                    progress and wait dialogs will ignore this property and always hide the
30194                                    close button as they can only be closed programmatically.
30195 cls               String           A custom CSS class to apply to the message box element
30196 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30197                                    displayed (defaults to 75)
30198 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30199                                    function will be btn (the name of the button that was clicked, if applicable,
30200                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30201                                    Progress and wait dialogs will ignore this option since they do not respond to
30202                                    user actions and can only be closed programmatically, so any required function
30203                                    should be called by the same code after it closes the dialog.
30204 icon              String           A CSS class that provides a background image to be used as an icon for
30205                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30206 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30207 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30208 modal             Boolean          False to allow user interaction with the page while the message box is
30209                                    displayed (defaults to true)
30210 msg               String           A string that will replace the existing message box body text (defaults
30211                                    to the XHTML-compliant non-breaking space character '&#160;')
30212 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30213 progress          Boolean          True to display a progress bar (defaults to false)
30214 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30215 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30216 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30217 title             String           The title text
30218 value             String           The string value to set into the active textbox element if displayed
30219 wait              Boolean          True to display a progress bar (defaults to false)
30220 width             Number           The width of the dialog in pixels
30221 </pre>
30222          *
30223          * Example usage:
30224          * <pre><code>
30225 Roo.Msg.show({
30226    title: 'Address',
30227    msg: 'Please enter your address:',
30228    width: 300,
30229    buttons: Roo.MessageBox.OKCANCEL,
30230    multiline: true,
30231    fn: saveAddress,
30232    animEl: 'addAddressBtn'
30233 });
30234 </code></pre>
30235          * @param {Object} config Configuration options
30236          * @return {Roo.MessageBox} This message box
30237          */
30238         show : function(options)
30239         {
30240             
30241             // this causes nightmares if you show one dialog after another
30242             // especially on callbacks..
30243              
30244             if(this.isVisible()){
30245                 
30246                 this.hide();
30247                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30248                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30249                 Roo.log("New Dialog Message:" +  options.msg )
30250                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30251                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30252                 
30253             }
30254             var d = this.getDialog();
30255             opt = options;
30256             d.setTitle(opt.title || "&#160;");
30257             d.close.setDisplayed(opt.closable !== false);
30258             activeTextEl = textboxEl;
30259             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30260             if(opt.prompt){
30261                 if(opt.multiline){
30262                     textboxEl.hide();
30263                     textareaEl.show();
30264                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30265                         opt.multiline : this.defaultTextHeight);
30266                     activeTextEl = textareaEl;
30267                 }else{
30268                     textboxEl.show();
30269                     textareaEl.hide();
30270                 }
30271             }else{
30272                 textboxEl.hide();
30273                 textareaEl.hide();
30274             }
30275             progressEl.setDisplayed(opt.progress === true);
30276             this.updateProgress(0);
30277             activeTextEl.dom.value = opt.value || "";
30278             if(opt.prompt){
30279                 dlg.setDefaultButton(activeTextEl);
30280             }else{
30281                 var bs = opt.buttons;
30282                 var db = null;
30283                 if(bs && bs.ok){
30284                     db = buttons["ok"];
30285                 }else if(bs && bs.yes){
30286                     db = buttons["yes"];
30287                 }
30288                 dlg.setDefaultButton(db);
30289             }
30290             bwidth = updateButtons(opt.buttons);
30291             this.updateText(opt.msg);
30292             if(opt.cls){
30293                 d.el.addClass(opt.cls);
30294             }
30295             d.proxyDrag = opt.proxyDrag === true;
30296             d.modal = opt.modal !== false;
30297             d.mask = opt.modal !== false ? mask : false;
30298             if(!d.isVisible()){
30299                 // force it to the end of the z-index stack so it gets a cursor in FF
30300                 document.body.appendChild(dlg.el.dom);
30301                 d.animateTarget = null;
30302                 d.show(options.animEl);
30303             }
30304             return this;
30305         },
30306
30307         /**
30308          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30309          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30310          * and closing the message box when the process is complete.
30311          * @param {String} title The title bar text
30312          * @param {String} msg The message box body text
30313          * @return {Roo.MessageBox} This message box
30314          */
30315         progress : function(title, msg){
30316             this.show({
30317                 title : title,
30318                 msg : msg,
30319                 buttons: false,
30320                 progress:true,
30321                 closable:false,
30322                 minWidth: this.minProgressWidth,
30323                 modal : true
30324             });
30325             return this;
30326         },
30327
30328         /**
30329          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30330          * If a callback function is passed it will be called after the user clicks the button, and the
30331          * id of the button that was clicked will be passed as the only parameter to the callback
30332          * (could also be the top-right close button).
30333          * @param {String} title The title bar text
30334          * @param {String} msg The message box body text
30335          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30336          * @param {Object} scope (optional) The scope of the callback function
30337          * @return {Roo.MessageBox} This message box
30338          */
30339         alert : function(title, msg, fn, scope){
30340             this.show({
30341                 title : title,
30342                 msg : msg,
30343                 buttons: this.OK,
30344                 fn: fn,
30345                 scope : scope,
30346                 modal : true
30347             });
30348             return this;
30349         },
30350
30351         /**
30352          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30353          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30354          * You are responsible for closing the message box when the process is complete.
30355          * @param {String} msg The message box body text
30356          * @param {String} title (optional) The title bar text
30357          * @return {Roo.MessageBox} This message box
30358          */
30359         wait : function(msg, title){
30360             this.show({
30361                 title : title,
30362                 msg : msg,
30363                 buttons: false,
30364                 closable:false,
30365                 progress:true,
30366                 modal:true,
30367                 width:300,
30368                 wait:true
30369             });
30370             waitTimer = Roo.TaskMgr.start({
30371                 run: function(i){
30372                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30373                 },
30374                 interval: 1000
30375             });
30376             return this;
30377         },
30378
30379         /**
30380          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30381          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30382          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30383          * @param {String} title The title bar text
30384          * @param {String} msg The message box body text
30385          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30386          * @param {Object} scope (optional) The scope of the callback function
30387          * @return {Roo.MessageBox} This message box
30388          */
30389         confirm : function(title, msg, fn, scope){
30390             this.show({
30391                 title : title,
30392                 msg : msg,
30393                 buttons: this.YESNO,
30394                 fn: fn,
30395                 scope : scope,
30396                 modal : true
30397             });
30398             return this;
30399         },
30400
30401         /**
30402          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30403          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30404          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30405          * (could also be the top-right close button) and the text that was entered will be passed as the two
30406          * parameters to the callback.
30407          * @param {String} title The title bar text
30408          * @param {String} msg The message box body text
30409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30410          * @param {Object} scope (optional) The scope of the callback function
30411          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30412          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30413          * @return {Roo.MessageBox} This message box
30414          */
30415         prompt : function(title, msg, fn, scope, multiline){
30416             this.show({
30417                 title : title,
30418                 msg : msg,
30419                 buttons: this.OKCANCEL,
30420                 fn: fn,
30421                 minWidth:250,
30422                 scope : scope,
30423                 prompt:true,
30424                 multiline: multiline,
30425                 modal : true
30426             });
30427             return this;
30428         },
30429
30430         /**
30431          * Button config that displays a single OK button
30432          * @type Object
30433          */
30434         OK : {ok:true},
30435         /**
30436          * Button config that displays Yes and No buttons
30437          * @type Object
30438          */
30439         YESNO : {yes:true, no:true},
30440         /**
30441          * Button config that displays OK and Cancel buttons
30442          * @type Object
30443          */
30444         OKCANCEL : {ok:true, cancel:true},
30445         /**
30446          * Button config that displays Yes, No and Cancel buttons
30447          * @type Object
30448          */
30449         YESNOCANCEL : {yes:true, no:true, cancel:true},
30450
30451         /**
30452          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30453          * @type Number
30454          */
30455         defaultTextHeight : 75,
30456         /**
30457          * The maximum width in pixels of the message box (defaults to 600)
30458          * @type Number
30459          */
30460         maxWidth : 600,
30461         /**
30462          * The minimum width in pixels of the message box (defaults to 100)
30463          * @type Number
30464          */
30465         minWidth : 100,
30466         /**
30467          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30468          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30469          * @type Number
30470          */
30471         minProgressWidth : 250,
30472         /**
30473          * An object containing the default button text strings that can be overriden for localized language support.
30474          * Supported properties are: ok, cancel, yes and no.
30475          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30476          * @type Object
30477          */
30478         buttonText : {
30479             ok : "OK",
30480             cancel : "Cancel",
30481             yes : "Yes",
30482             no : "No"
30483         }
30484     };
30485 }();
30486
30487 /**
30488  * Shorthand for {@link Roo.MessageBox}
30489  */
30490 Roo.Msg = Roo.MessageBox;/*
30491  * Based on:
30492  * Ext JS Library 1.1.1
30493  * Copyright(c) 2006-2007, Ext JS, LLC.
30494  *
30495  * Originally Released Under LGPL - original licence link has changed is not relivant.
30496  *
30497  * Fork - LGPL
30498  * <script type="text/javascript">
30499  */
30500 /**
30501  * @class Roo.QuickTips
30502  * Provides attractive and customizable tooltips for any element.
30503  * @singleton
30504  */
30505 Roo.QuickTips = function(){
30506     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30507     var ce, bd, xy, dd;
30508     var visible = false, disabled = true, inited = false;
30509     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30510     
30511     var onOver = function(e){
30512         if(disabled){
30513             return;
30514         }
30515         var t = e.getTarget();
30516         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30517             return;
30518         }
30519         if(ce && t == ce.el){
30520             clearTimeout(hideProc);
30521             return;
30522         }
30523         if(t && tagEls[t.id]){
30524             tagEls[t.id].el = t;
30525             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30526             return;
30527         }
30528         var ttp, et = Roo.fly(t);
30529         var ns = cfg.namespace;
30530         if(tm.interceptTitles && t.title){
30531             ttp = t.title;
30532             t.qtip = ttp;
30533             t.removeAttribute("title");
30534             e.preventDefault();
30535         }else{
30536             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30537         }
30538         if(ttp){
30539             showProc = show.defer(tm.showDelay, tm, [{
30540                 el: t, 
30541                 text: ttp, 
30542                 width: et.getAttributeNS(ns, cfg.width),
30543                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30544                 title: et.getAttributeNS(ns, cfg.title),
30545                     cls: et.getAttributeNS(ns, cfg.cls)
30546             }]);
30547         }
30548     };
30549     
30550     var onOut = function(e){
30551         clearTimeout(showProc);
30552         var t = e.getTarget();
30553         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30554             hideProc = setTimeout(hide, tm.hideDelay);
30555         }
30556     };
30557     
30558     var onMove = function(e){
30559         if(disabled){
30560             return;
30561         }
30562         xy = e.getXY();
30563         xy[1] += 18;
30564         if(tm.trackMouse && ce){
30565             el.setXY(xy);
30566         }
30567     };
30568     
30569     var onDown = function(e){
30570         clearTimeout(showProc);
30571         clearTimeout(hideProc);
30572         if(!e.within(el)){
30573             if(tm.hideOnClick){
30574                 hide();
30575                 tm.disable();
30576                 tm.enable.defer(100, tm);
30577             }
30578         }
30579     };
30580     
30581     var getPad = function(){
30582         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30583     };
30584
30585     var show = function(o){
30586         if(disabled){
30587             return;
30588         }
30589         clearTimeout(dismissProc);
30590         ce = o;
30591         if(removeCls){ // in case manually hidden
30592             el.removeClass(removeCls);
30593             removeCls = null;
30594         }
30595         if(ce.cls){
30596             el.addClass(ce.cls);
30597             removeCls = ce.cls;
30598         }
30599         if(ce.title){
30600             tipTitle.update(ce.title);
30601             tipTitle.show();
30602         }else{
30603             tipTitle.update('');
30604             tipTitle.hide();
30605         }
30606         el.dom.style.width  = tm.maxWidth+'px';
30607         //tipBody.dom.style.width = '';
30608         tipBodyText.update(o.text);
30609         var p = getPad(), w = ce.width;
30610         if(!w){
30611             var td = tipBodyText.dom;
30612             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30613             if(aw > tm.maxWidth){
30614                 w = tm.maxWidth;
30615             }else if(aw < tm.minWidth){
30616                 w = tm.minWidth;
30617             }else{
30618                 w = aw;
30619             }
30620         }
30621         //tipBody.setWidth(w);
30622         el.setWidth(parseInt(w, 10) + p);
30623         if(ce.autoHide === false){
30624             close.setDisplayed(true);
30625             if(dd){
30626                 dd.unlock();
30627             }
30628         }else{
30629             close.setDisplayed(false);
30630             if(dd){
30631                 dd.lock();
30632             }
30633         }
30634         if(xy){
30635             el.avoidY = xy[1]-18;
30636             el.setXY(xy);
30637         }
30638         if(tm.animate){
30639             el.setOpacity(.1);
30640             el.setStyle("visibility", "visible");
30641             el.fadeIn({callback: afterShow});
30642         }else{
30643             afterShow();
30644         }
30645     };
30646     
30647     var afterShow = function(){
30648         if(ce){
30649             el.show();
30650             esc.enable();
30651             if(tm.autoDismiss && ce.autoHide !== false){
30652                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30653             }
30654         }
30655     };
30656     
30657     var hide = function(noanim){
30658         clearTimeout(dismissProc);
30659         clearTimeout(hideProc);
30660         ce = null;
30661         if(el.isVisible()){
30662             esc.disable();
30663             if(noanim !== true && tm.animate){
30664                 el.fadeOut({callback: afterHide});
30665             }else{
30666                 afterHide();
30667             } 
30668         }
30669     };
30670     
30671     var afterHide = function(){
30672         el.hide();
30673         if(removeCls){
30674             el.removeClass(removeCls);
30675             removeCls = null;
30676         }
30677     };
30678     
30679     return {
30680         /**
30681         * @cfg {Number} minWidth
30682         * The minimum width of the quick tip (defaults to 40)
30683         */
30684        minWidth : 40,
30685         /**
30686         * @cfg {Number} maxWidth
30687         * The maximum width of the quick tip (defaults to 300)
30688         */
30689        maxWidth : 300,
30690         /**
30691         * @cfg {Boolean} interceptTitles
30692         * True to automatically use the element's DOM title value if available (defaults to false)
30693         */
30694        interceptTitles : false,
30695         /**
30696         * @cfg {Boolean} trackMouse
30697         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30698         */
30699        trackMouse : false,
30700         /**
30701         * @cfg {Boolean} hideOnClick
30702         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30703         */
30704        hideOnClick : true,
30705         /**
30706         * @cfg {Number} showDelay
30707         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30708         */
30709        showDelay : 500,
30710         /**
30711         * @cfg {Number} hideDelay
30712         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30713         */
30714        hideDelay : 200,
30715         /**
30716         * @cfg {Boolean} autoHide
30717         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30718         * Used in conjunction with hideDelay.
30719         */
30720        autoHide : true,
30721         /**
30722         * @cfg {Boolean}
30723         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30724         * (defaults to true).  Used in conjunction with autoDismissDelay.
30725         */
30726        autoDismiss : true,
30727         /**
30728         * @cfg {Number}
30729         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30730         */
30731        autoDismissDelay : 5000,
30732        /**
30733         * @cfg {Boolean} animate
30734         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30735         */
30736        animate : false,
30737
30738        /**
30739         * @cfg {String} title
30740         * Title text to display (defaults to '').  This can be any valid HTML markup.
30741         */
30742         title: '',
30743        /**
30744         * @cfg {String} text
30745         * Body text to display (defaults to '').  This can be any valid HTML markup.
30746         */
30747         text : '',
30748        /**
30749         * @cfg {String} cls
30750         * A CSS class to apply to the base quick tip element (defaults to '').
30751         */
30752         cls : '',
30753        /**
30754         * @cfg {Number} width
30755         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30756         * minWidth or maxWidth.
30757         */
30758         width : null,
30759
30760     /**
30761      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30762      * or display QuickTips in a page.
30763      */
30764        init : function(){
30765           tm = Roo.QuickTips;
30766           cfg = tm.tagConfig;
30767           if(!inited){
30768               if(!Roo.isReady){ // allow calling of init() before onReady
30769                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30770                   return;
30771               }
30772               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30773               el.fxDefaults = {stopFx: true};
30774               // maximum custom styling
30775               //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>');
30776               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>');              
30777               tipTitle = el.child('h3');
30778               tipTitle.enableDisplayMode("block");
30779               tipBody = el.child('div.x-tip-bd');
30780               tipBodyText = el.child('div.x-tip-bd-inner');
30781               //bdLeft = el.child('div.x-tip-bd-left');
30782               //bdRight = el.child('div.x-tip-bd-right');
30783               close = el.child('div.x-tip-close');
30784               close.enableDisplayMode("block");
30785               close.on("click", hide);
30786               var d = Roo.get(document);
30787               d.on("mousedown", onDown);
30788               d.on("mouseover", onOver);
30789               d.on("mouseout", onOut);
30790               d.on("mousemove", onMove);
30791               esc = d.addKeyListener(27, hide);
30792               esc.disable();
30793               if(Roo.dd.DD){
30794                   dd = el.initDD("default", null, {
30795                       onDrag : function(){
30796                           el.sync();  
30797                       }
30798                   });
30799                   dd.setHandleElId(tipTitle.id);
30800                   dd.lock();
30801               }
30802               inited = true;
30803           }
30804           this.enable(); 
30805        },
30806
30807     /**
30808      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30809      * are supported:
30810      * <pre>
30811 Property    Type                   Description
30812 ----------  ---------------------  ------------------------------------------------------------------------
30813 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30814      * </ul>
30815      * @param {Object} config The config object
30816      */
30817        register : function(config){
30818            var cs = config instanceof Array ? config : arguments;
30819            for(var i = 0, len = cs.length; i < len; i++) {
30820                var c = cs[i];
30821                var target = c.target;
30822                if(target){
30823                    if(target instanceof Array){
30824                        for(var j = 0, jlen = target.length; j < jlen; j++){
30825                            tagEls[target[j]] = c;
30826                        }
30827                    }else{
30828                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30829                    }
30830                }
30831            }
30832        },
30833
30834     /**
30835      * Removes this quick tip from its element and destroys it.
30836      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30837      */
30838        unregister : function(el){
30839            delete tagEls[Roo.id(el)];
30840        },
30841
30842     /**
30843      * Enable this quick tip.
30844      */
30845        enable : function(){
30846            if(inited && disabled){
30847                locks.pop();
30848                if(locks.length < 1){
30849                    disabled = false;
30850                }
30851            }
30852        },
30853
30854     /**
30855      * Disable this quick tip.
30856      */
30857        disable : function(){
30858           disabled = true;
30859           clearTimeout(showProc);
30860           clearTimeout(hideProc);
30861           clearTimeout(dismissProc);
30862           if(ce){
30863               hide(true);
30864           }
30865           locks.push(1);
30866        },
30867
30868     /**
30869      * Returns true if the quick tip is enabled, else false.
30870      */
30871        isEnabled : function(){
30872             return !disabled;
30873        },
30874
30875         // private
30876        tagConfig : {
30877            namespace : "ext",
30878            attribute : "qtip",
30879            width : "width",
30880            target : "target",
30881            title : "qtitle",
30882            hide : "hide",
30883            cls : "qclass"
30884        }
30885    };
30886 }();
30887
30888 // backwards compat
30889 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30890  * Based on:
30891  * Ext JS Library 1.1.1
30892  * Copyright(c) 2006-2007, Ext JS, LLC.
30893  *
30894  * Originally Released Under LGPL - original licence link has changed is not relivant.
30895  *
30896  * Fork - LGPL
30897  * <script type="text/javascript">
30898  */
30899  
30900
30901 /**
30902  * @class Roo.tree.TreePanel
30903  * @extends Roo.data.Tree
30904
30905  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30906  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30907  * @cfg {Boolean} enableDD true to enable drag and drop
30908  * @cfg {Boolean} enableDrag true to enable just drag
30909  * @cfg {Boolean} enableDrop true to enable just drop
30910  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30911  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30912  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30913  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30914  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30915  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30916  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30917  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30918  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30919  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30920  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30921  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30922  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30923  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30924  * @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>
30925  * @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>
30926  * 
30927  * @constructor
30928  * @param {String/HTMLElement/Element} el The container element
30929  * @param {Object} config
30930  */
30931 Roo.tree.TreePanel = function(el, config){
30932     var root = false;
30933     var loader = false;
30934     if (config.root) {
30935         root = config.root;
30936         delete config.root;
30937     }
30938     if (config.loader) {
30939         loader = config.loader;
30940         delete config.loader;
30941     }
30942     
30943     Roo.apply(this, config);
30944     Roo.tree.TreePanel.superclass.constructor.call(this);
30945     this.el = Roo.get(el);
30946     this.el.addClass('x-tree');
30947     //console.log(root);
30948     if (root) {
30949         this.setRootNode( Roo.factory(root, Roo.tree));
30950     }
30951     if (loader) {
30952         this.loader = Roo.factory(loader, Roo.tree);
30953     }
30954    /**
30955     * Read-only. The id of the container element becomes this TreePanel's id.
30956     */
30957     this.id = this.el.id;
30958     this.addEvents({
30959         /**
30960         * @event beforeload
30961         * Fires before a node is loaded, return false to cancel
30962         * @param {Node} node The node being loaded
30963         */
30964         "beforeload" : true,
30965         /**
30966         * @event load
30967         * Fires when a node is loaded
30968         * @param {Node} node The node that was loaded
30969         */
30970         "load" : true,
30971         /**
30972         * @event textchange
30973         * Fires when the text for a node is changed
30974         * @param {Node} node The node
30975         * @param {String} text The new text
30976         * @param {String} oldText The old text
30977         */
30978         "textchange" : true,
30979         /**
30980         * @event beforeexpand
30981         * Fires before a node is expanded, return false to cancel.
30982         * @param {Node} node The node
30983         * @param {Boolean} deep
30984         * @param {Boolean} anim
30985         */
30986         "beforeexpand" : true,
30987         /**
30988         * @event beforecollapse
30989         * Fires before a node is collapsed, return false to cancel.
30990         * @param {Node} node The node
30991         * @param {Boolean} deep
30992         * @param {Boolean} anim
30993         */
30994         "beforecollapse" : true,
30995         /**
30996         * @event expand
30997         * Fires when a node is expanded
30998         * @param {Node} node The node
30999         */
31000         "expand" : true,
31001         /**
31002         * @event disabledchange
31003         * Fires when the disabled status of a node changes
31004         * @param {Node} node The node
31005         * @param {Boolean} disabled
31006         */
31007         "disabledchange" : true,
31008         /**
31009         * @event collapse
31010         * Fires when a node is collapsed
31011         * @param {Node} node The node
31012         */
31013         "collapse" : true,
31014         /**
31015         * @event beforeclick
31016         * Fires before click processing on a node. Return false to cancel the default action.
31017         * @param {Node} node The node
31018         * @param {Roo.EventObject} e The event object
31019         */
31020         "beforeclick":true,
31021         /**
31022         * @event checkchange
31023         * Fires when a node with a checkbox's checked property changes
31024         * @param {Node} this This node
31025         * @param {Boolean} checked
31026         */
31027         "checkchange":true,
31028         /**
31029         * @event click
31030         * Fires when a node is clicked
31031         * @param {Node} node The node
31032         * @param {Roo.EventObject} e The event object
31033         */
31034         "click":true,
31035         /**
31036         * @event dblclick
31037         * Fires when a node is double clicked
31038         * @param {Node} node The node
31039         * @param {Roo.EventObject} e The event object
31040         */
31041         "dblclick":true,
31042         /**
31043         * @event contextmenu
31044         * Fires when a node is right clicked
31045         * @param {Node} node The node
31046         * @param {Roo.EventObject} e The event object
31047         */
31048         "contextmenu":true,
31049         /**
31050         * @event beforechildrenrendered
31051         * Fires right before the child nodes for a node are rendered
31052         * @param {Node} node The node
31053         */
31054         "beforechildrenrendered":true,
31055         /**
31056         * @event startdrag
31057         * Fires when a node starts being dragged
31058         * @param {Roo.tree.TreePanel} this
31059         * @param {Roo.tree.TreeNode} node
31060         * @param {event} e The raw browser event
31061         */ 
31062        "startdrag" : true,
31063        /**
31064         * @event enddrag
31065         * Fires when a drag operation is complete
31066         * @param {Roo.tree.TreePanel} this
31067         * @param {Roo.tree.TreeNode} node
31068         * @param {event} e The raw browser event
31069         */
31070        "enddrag" : true,
31071        /**
31072         * @event dragdrop
31073         * Fires when a dragged node is dropped on a valid DD target
31074         * @param {Roo.tree.TreePanel} this
31075         * @param {Roo.tree.TreeNode} node
31076         * @param {DD} dd The dd it was dropped on
31077         * @param {event} e The raw browser event
31078         */
31079        "dragdrop" : true,
31080        /**
31081         * @event beforenodedrop
31082         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31083         * passed to handlers has the following properties:<br />
31084         * <ul style="padding:5px;padding-left:16px;">
31085         * <li>tree - The TreePanel</li>
31086         * <li>target - The node being targeted for the drop</li>
31087         * <li>data - The drag data from the drag source</li>
31088         * <li>point - The point of the drop - append, above or below</li>
31089         * <li>source - The drag source</li>
31090         * <li>rawEvent - Raw mouse event</li>
31091         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31092         * to be inserted by setting them on this object.</li>
31093         * <li>cancel - Set this to true to cancel the drop.</li>
31094         * </ul>
31095         * @param {Object} dropEvent
31096         */
31097        "beforenodedrop" : true,
31098        /**
31099         * @event nodedrop
31100         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31101         * passed to handlers has the following properties:<br />
31102         * <ul style="padding:5px;padding-left:16px;">
31103         * <li>tree - The TreePanel</li>
31104         * <li>target - The node being targeted for the drop</li>
31105         * <li>data - The drag data from the drag source</li>
31106         * <li>point - The point of the drop - append, above or below</li>
31107         * <li>source - The drag source</li>
31108         * <li>rawEvent - Raw mouse event</li>
31109         * <li>dropNode - Dropped node(s).</li>
31110         * </ul>
31111         * @param {Object} dropEvent
31112         */
31113        "nodedrop" : true,
31114         /**
31115         * @event nodedragover
31116         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31117         * passed to handlers has the following properties:<br />
31118         * <ul style="padding:5px;padding-left:16px;">
31119         * <li>tree - The TreePanel</li>
31120         * <li>target - The node being targeted for the drop</li>
31121         * <li>data - The drag data from the drag source</li>
31122         * <li>point - The point of the drop - append, above or below</li>
31123         * <li>source - The drag source</li>
31124         * <li>rawEvent - Raw mouse event</li>
31125         * <li>dropNode - Drop node(s) provided by the source.</li>
31126         * <li>cancel - Set this to true to signal drop not allowed.</li>
31127         * </ul>
31128         * @param {Object} dragOverEvent
31129         */
31130        "nodedragover" : true
31131         
31132     });
31133     if(this.singleExpand){
31134        this.on("beforeexpand", this.restrictExpand, this);
31135     }
31136     if (this.editor) {
31137         this.editor.tree = this;
31138         this.editor = Roo.factory(this.editor, Roo.tree);
31139     }
31140     
31141     if (this.selModel) {
31142         this.selModel = Roo.factory(this.selModel, Roo.tree);
31143     }
31144    
31145 };
31146 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31147     rootVisible : true,
31148     animate: Roo.enableFx,
31149     lines : true,
31150     enableDD : false,
31151     hlDrop : Roo.enableFx,
31152   
31153     renderer: false,
31154     
31155     rendererTip: false,
31156     // private
31157     restrictExpand : function(node){
31158         var p = node.parentNode;
31159         if(p){
31160             if(p.expandedChild && p.expandedChild.parentNode == p){
31161                 p.expandedChild.collapse();
31162             }
31163             p.expandedChild = node;
31164         }
31165     },
31166
31167     // private override
31168     setRootNode : function(node){
31169         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31170         if(!this.rootVisible){
31171             node.ui = new Roo.tree.RootTreeNodeUI(node);
31172         }
31173         return node;
31174     },
31175
31176     /**
31177      * Returns the container element for this TreePanel
31178      */
31179     getEl : function(){
31180         return this.el;
31181     },
31182
31183     /**
31184      * Returns the default TreeLoader for this TreePanel
31185      */
31186     getLoader : function(){
31187         return this.loader;
31188     },
31189
31190     /**
31191      * Expand all nodes
31192      */
31193     expandAll : function(){
31194         this.root.expand(true);
31195     },
31196
31197     /**
31198      * Collapse all nodes
31199      */
31200     collapseAll : function(){
31201         this.root.collapse(true);
31202     },
31203
31204     /**
31205      * Returns the selection model used by this TreePanel
31206      */
31207     getSelectionModel : function(){
31208         if(!this.selModel){
31209             this.selModel = new Roo.tree.DefaultSelectionModel();
31210         }
31211         return this.selModel;
31212     },
31213
31214     /**
31215      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31216      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31217      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31218      * @return {Array}
31219      */
31220     getChecked : function(a, startNode){
31221         startNode = startNode || this.root;
31222         var r = [];
31223         var f = function(){
31224             if(this.attributes.checked){
31225                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31226             }
31227         }
31228         startNode.cascade(f);
31229         return r;
31230     },
31231
31232     /**
31233      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31234      * @param {String} path
31235      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31236      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31237      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31238      */
31239     expandPath : function(path, attr, callback){
31240         attr = attr || "id";
31241         var keys = path.split(this.pathSeparator);
31242         var curNode = this.root;
31243         if(curNode.attributes[attr] != keys[1]){ // invalid root
31244             if(callback){
31245                 callback(false, null);
31246             }
31247             return;
31248         }
31249         var index = 1;
31250         var f = function(){
31251             if(++index == keys.length){
31252                 if(callback){
31253                     callback(true, curNode);
31254                 }
31255                 return;
31256             }
31257             var c = curNode.findChild(attr, keys[index]);
31258             if(!c){
31259                 if(callback){
31260                     callback(false, curNode);
31261                 }
31262                 return;
31263             }
31264             curNode = c;
31265             c.expand(false, false, f);
31266         };
31267         curNode.expand(false, false, f);
31268     },
31269
31270     /**
31271      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31272      * @param {String} path
31273      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31274      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31275      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31276      */
31277     selectPath : function(path, attr, callback){
31278         attr = attr || "id";
31279         var keys = path.split(this.pathSeparator);
31280         var v = keys.pop();
31281         if(keys.length > 0){
31282             var f = function(success, node){
31283                 if(success && node){
31284                     var n = node.findChild(attr, v);
31285                     if(n){
31286                         n.select();
31287                         if(callback){
31288                             callback(true, n);
31289                         }
31290                     }else if(callback){
31291                         callback(false, n);
31292                     }
31293                 }else{
31294                     if(callback){
31295                         callback(false, n);
31296                     }
31297                 }
31298             };
31299             this.expandPath(keys.join(this.pathSeparator), attr, f);
31300         }else{
31301             this.root.select();
31302             if(callback){
31303                 callback(true, this.root);
31304             }
31305         }
31306     },
31307
31308     getTreeEl : function(){
31309         return this.el;
31310     },
31311
31312     /**
31313      * Trigger rendering of this TreePanel
31314      */
31315     render : function(){
31316         if (this.innerCt) {
31317             return this; // stop it rendering more than once!!
31318         }
31319         
31320         this.innerCt = this.el.createChild({tag:"ul",
31321                cls:"x-tree-root-ct " +
31322                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31323
31324         if(this.containerScroll){
31325             Roo.dd.ScrollManager.register(this.el);
31326         }
31327         if((this.enableDD || this.enableDrop) && !this.dropZone){
31328            /**
31329             * The dropZone used by this tree if drop is enabled
31330             * @type Roo.tree.TreeDropZone
31331             */
31332              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31333                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31334            });
31335         }
31336         if((this.enableDD || this.enableDrag) && !this.dragZone){
31337            /**
31338             * The dragZone used by this tree if drag is enabled
31339             * @type Roo.tree.TreeDragZone
31340             */
31341             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31342                ddGroup: this.ddGroup || "TreeDD",
31343                scroll: this.ddScroll
31344            });
31345         }
31346         this.getSelectionModel().init(this);
31347         if (!this.root) {
31348             console.log("ROOT not set in tree");
31349             return;
31350         }
31351         this.root.render();
31352         if(!this.rootVisible){
31353             this.root.renderChildren();
31354         }
31355         return this;
31356     }
31357 });/*
31358  * Based on:
31359  * Ext JS Library 1.1.1
31360  * Copyright(c) 2006-2007, Ext JS, LLC.
31361  *
31362  * Originally Released Under LGPL - original licence link has changed is not relivant.
31363  *
31364  * Fork - LGPL
31365  * <script type="text/javascript">
31366  */
31367  
31368
31369 /**
31370  * @class Roo.tree.DefaultSelectionModel
31371  * @extends Roo.util.Observable
31372  * The default single selection for a TreePanel.
31373  * @param {Object} cfg Configuration
31374  */
31375 Roo.tree.DefaultSelectionModel = function(cfg){
31376    this.selNode = null;
31377    
31378    
31379    
31380    this.addEvents({
31381        /**
31382         * @event selectionchange
31383         * Fires when the selected node changes
31384         * @param {DefaultSelectionModel} this
31385         * @param {TreeNode} node the new selection
31386         */
31387        "selectionchange" : true,
31388
31389        /**
31390         * @event beforeselect
31391         * Fires before the selected node changes, return false to cancel the change
31392         * @param {DefaultSelectionModel} this
31393         * @param {TreeNode} node the new selection
31394         * @param {TreeNode} node the old selection
31395         */
31396        "beforeselect" : true
31397    });
31398    
31399     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31400 };
31401
31402 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31403     init : function(tree){
31404         this.tree = tree;
31405         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31406         tree.on("click", this.onNodeClick, this);
31407     },
31408     
31409     onNodeClick : function(node, e){
31410         if (e.ctrlKey && this.selNode == node)  {
31411             this.unselect(node);
31412             return;
31413         }
31414         this.select(node);
31415     },
31416     
31417     /**
31418      * Select a node.
31419      * @param {TreeNode} node The node to select
31420      * @return {TreeNode} The selected node
31421      */
31422     select : function(node){
31423         var last = this.selNode;
31424         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31425             if(last){
31426                 last.ui.onSelectedChange(false);
31427             }
31428             this.selNode = node;
31429             node.ui.onSelectedChange(true);
31430             this.fireEvent("selectionchange", this, node, last);
31431         }
31432         return node;
31433     },
31434     
31435     /**
31436      * Deselect a node.
31437      * @param {TreeNode} node The node to unselect
31438      */
31439     unselect : function(node){
31440         if(this.selNode == node){
31441             this.clearSelections();
31442         }    
31443     },
31444     
31445     /**
31446      * Clear all selections
31447      */
31448     clearSelections : function(){
31449         var n = this.selNode;
31450         if(n){
31451             n.ui.onSelectedChange(false);
31452             this.selNode = null;
31453             this.fireEvent("selectionchange", this, null);
31454         }
31455         return n;
31456     },
31457     
31458     /**
31459      * Get the selected node
31460      * @return {TreeNode} The selected node
31461      */
31462     getSelectedNode : function(){
31463         return this.selNode;    
31464     },
31465     
31466     /**
31467      * Returns true if the node is selected
31468      * @param {TreeNode} node The node to check
31469      * @return {Boolean}
31470      */
31471     isSelected : function(node){
31472         return this.selNode == node;  
31473     },
31474
31475     /**
31476      * Selects the node above the selected node in the tree, intelligently walking the nodes
31477      * @return TreeNode The new selection
31478      */
31479     selectPrevious : function(){
31480         var s = this.selNode || this.lastSelNode;
31481         if(!s){
31482             return null;
31483         }
31484         var ps = s.previousSibling;
31485         if(ps){
31486             if(!ps.isExpanded() || ps.childNodes.length < 1){
31487                 return this.select(ps);
31488             } else{
31489                 var lc = ps.lastChild;
31490                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31491                     lc = lc.lastChild;
31492                 }
31493                 return this.select(lc);
31494             }
31495         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31496             return this.select(s.parentNode);
31497         }
31498         return null;
31499     },
31500
31501     /**
31502      * Selects the node above the selected node in the tree, intelligently walking the nodes
31503      * @return TreeNode The new selection
31504      */
31505     selectNext : function(){
31506         var s = this.selNode || this.lastSelNode;
31507         if(!s){
31508             return null;
31509         }
31510         if(s.firstChild && s.isExpanded()){
31511              return this.select(s.firstChild);
31512          }else if(s.nextSibling){
31513              return this.select(s.nextSibling);
31514          }else if(s.parentNode){
31515             var newS = null;
31516             s.parentNode.bubble(function(){
31517                 if(this.nextSibling){
31518                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31519                     return false;
31520                 }
31521             });
31522             return newS;
31523          }
31524         return null;
31525     },
31526
31527     onKeyDown : function(e){
31528         var s = this.selNode || this.lastSelNode;
31529         // undesirable, but required
31530         var sm = this;
31531         if(!s){
31532             return;
31533         }
31534         var k = e.getKey();
31535         switch(k){
31536              case e.DOWN:
31537                  e.stopEvent();
31538                  this.selectNext();
31539              break;
31540              case e.UP:
31541                  e.stopEvent();
31542                  this.selectPrevious();
31543              break;
31544              case e.RIGHT:
31545                  e.preventDefault();
31546                  if(s.hasChildNodes()){
31547                      if(!s.isExpanded()){
31548                          s.expand();
31549                      }else if(s.firstChild){
31550                          this.select(s.firstChild, e);
31551                      }
31552                  }
31553              break;
31554              case e.LEFT:
31555                  e.preventDefault();
31556                  if(s.hasChildNodes() && s.isExpanded()){
31557                      s.collapse();
31558                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31559                      this.select(s.parentNode, e);
31560                  }
31561              break;
31562         };
31563     }
31564 });
31565
31566 /**
31567  * @class Roo.tree.MultiSelectionModel
31568  * @extends Roo.util.Observable
31569  * Multi selection for a TreePanel.
31570  * @param {Object} cfg Configuration
31571  */
31572 Roo.tree.MultiSelectionModel = function(){
31573    this.selNodes = [];
31574    this.selMap = {};
31575    this.addEvents({
31576        /**
31577         * @event selectionchange
31578         * Fires when the selected nodes change
31579         * @param {MultiSelectionModel} this
31580         * @param {Array} nodes Array of the selected nodes
31581         */
31582        "selectionchange" : true
31583    });
31584    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31585    
31586 };
31587
31588 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31589     init : function(tree){
31590         this.tree = tree;
31591         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31592         tree.on("click", this.onNodeClick, this);
31593     },
31594     
31595     onNodeClick : function(node, e){
31596         this.select(node, e, e.ctrlKey);
31597     },
31598     
31599     /**
31600      * Select a node.
31601      * @param {TreeNode} node The node to select
31602      * @param {EventObject} e (optional) An event associated with the selection
31603      * @param {Boolean} keepExisting True to retain existing selections
31604      * @return {TreeNode} The selected node
31605      */
31606     select : function(node, e, keepExisting){
31607         if(keepExisting !== true){
31608             this.clearSelections(true);
31609         }
31610         if(this.isSelected(node)){
31611             this.lastSelNode = node;
31612             return node;
31613         }
31614         this.selNodes.push(node);
31615         this.selMap[node.id] = node;
31616         this.lastSelNode = node;
31617         node.ui.onSelectedChange(true);
31618         this.fireEvent("selectionchange", this, this.selNodes);
31619         return node;
31620     },
31621     
31622     /**
31623      * Deselect a node.
31624      * @param {TreeNode} node The node to unselect
31625      */
31626     unselect : function(node){
31627         if(this.selMap[node.id]){
31628             node.ui.onSelectedChange(false);
31629             var sn = this.selNodes;
31630             var index = -1;
31631             if(sn.indexOf){
31632                 index = sn.indexOf(node);
31633             }else{
31634                 for(var i = 0, len = sn.length; i < len; i++){
31635                     if(sn[i] == node){
31636                         index = i;
31637                         break;
31638                     }
31639                 }
31640             }
31641             if(index != -1){
31642                 this.selNodes.splice(index, 1);
31643             }
31644             delete this.selMap[node.id];
31645             this.fireEvent("selectionchange", this, this.selNodes);
31646         }
31647     },
31648     
31649     /**
31650      * Clear all selections
31651      */
31652     clearSelections : function(suppressEvent){
31653         var sn = this.selNodes;
31654         if(sn.length > 0){
31655             for(var i = 0, len = sn.length; i < len; i++){
31656                 sn[i].ui.onSelectedChange(false);
31657             }
31658             this.selNodes = [];
31659             this.selMap = {};
31660             if(suppressEvent !== true){
31661                 this.fireEvent("selectionchange", this, this.selNodes);
31662             }
31663         }
31664     },
31665     
31666     /**
31667      * Returns true if the node is selected
31668      * @param {TreeNode} node The node to check
31669      * @return {Boolean}
31670      */
31671     isSelected : function(node){
31672         return this.selMap[node.id] ? true : false;  
31673     },
31674     
31675     /**
31676      * Returns an array of the selected nodes
31677      * @return {Array}
31678      */
31679     getSelectedNodes : function(){
31680         return this.selNodes;    
31681     },
31682
31683     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31684
31685     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31686
31687     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31688 });/*
31689  * Based on:
31690  * Ext JS Library 1.1.1
31691  * Copyright(c) 2006-2007, Ext JS, LLC.
31692  *
31693  * Originally Released Under LGPL - original licence link has changed is not relivant.
31694  *
31695  * Fork - LGPL
31696  * <script type="text/javascript">
31697  */
31698  
31699 /**
31700  * @class Roo.tree.TreeNode
31701  * @extends Roo.data.Node
31702  * @cfg {String} text The text for this node
31703  * @cfg {Boolean} expanded true to start the node expanded
31704  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31705  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31706  * @cfg {Boolean} disabled true to start the node disabled
31707  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31708  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31709  * @cfg {String} cls A css class to be added to the node
31710  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31711  * @cfg {String} href URL of the link used for the node (defaults to #)
31712  * @cfg {String} hrefTarget target frame for the link
31713  * @cfg {String} qtip An Ext QuickTip for the node
31714  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31715  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31716  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31717  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31718  * (defaults to undefined with no checkbox rendered)
31719  * @constructor
31720  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31721  */
31722 Roo.tree.TreeNode = function(attributes){
31723     attributes = attributes || {};
31724     if(typeof attributes == "string"){
31725         attributes = {text: attributes};
31726     }
31727     this.childrenRendered = false;
31728     this.rendered = false;
31729     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31730     this.expanded = attributes.expanded === true;
31731     this.isTarget = attributes.isTarget !== false;
31732     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31733     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31734
31735     /**
31736      * Read-only. The text for this node. To change it use setText().
31737      * @type String
31738      */
31739     this.text = attributes.text;
31740     /**
31741      * True if this node is disabled.
31742      * @type Boolean
31743      */
31744     this.disabled = attributes.disabled === true;
31745
31746     this.addEvents({
31747         /**
31748         * @event textchange
31749         * Fires when the text for this node is changed
31750         * @param {Node} this This node
31751         * @param {String} text The new text
31752         * @param {String} oldText The old text
31753         */
31754         "textchange" : true,
31755         /**
31756         * @event beforeexpand
31757         * Fires before this node is expanded, return false to cancel.
31758         * @param {Node} this This node
31759         * @param {Boolean} deep
31760         * @param {Boolean} anim
31761         */
31762         "beforeexpand" : true,
31763         /**
31764         * @event beforecollapse
31765         * Fires before this node is collapsed, return false to cancel.
31766         * @param {Node} this This node
31767         * @param {Boolean} deep
31768         * @param {Boolean} anim
31769         */
31770         "beforecollapse" : true,
31771         /**
31772         * @event expand
31773         * Fires when this node is expanded
31774         * @param {Node} this This node
31775         */
31776         "expand" : true,
31777         /**
31778         * @event disabledchange
31779         * Fires when the disabled status of this node changes
31780         * @param {Node} this This node
31781         * @param {Boolean} disabled
31782         */
31783         "disabledchange" : true,
31784         /**
31785         * @event collapse
31786         * Fires when this node is collapsed
31787         * @param {Node} this This node
31788         */
31789         "collapse" : true,
31790         /**
31791         * @event beforeclick
31792         * Fires before click processing. Return false to cancel the default action.
31793         * @param {Node} this This node
31794         * @param {Roo.EventObject} e The event object
31795         */
31796         "beforeclick":true,
31797         /**
31798         * @event checkchange
31799         * Fires when a node with a checkbox's checked property changes
31800         * @param {Node} this This node
31801         * @param {Boolean} checked
31802         */
31803         "checkchange":true,
31804         /**
31805         * @event click
31806         * Fires when this node is clicked
31807         * @param {Node} this This node
31808         * @param {Roo.EventObject} e The event object
31809         */
31810         "click":true,
31811         /**
31812         * @event dblclick
31813         * Fires when this node is double clicked
31814         * @param {Node} this This node
31815         * @param {Roo.EventObject} e The event object
31816         */
31817         "dblclick":true,
31818         /**
31819         * @event contextmenu
31820         * Fires when this node is right clicked
31821         * @param {Node} this This node
31822         * @param {Roo.EventObject} e The event object
31823         */
31824         "contextmenu":true,
31825         /**
31826         * @event beforechildrenrendered
31827         * Fires right before the child nodes for this node are rendered
31828         * @param {Node} this This node
31829         */
31830         "beforechildrenrendered":true
31831     });
31832
31833     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31834
31835     /**
31836      * Read-only. The UI for this node
31837      * @type TreeNodeUI
31838      */
31839     this.ui = new uiClass(this);
31840     
31841     // finally support items[]
31842     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
31843         return;
31844     }
31845     
31846     
31847     Roo.each(this.attributes.items, function(c) {
31848         this.appendChild(Roo.factory(c,Roo.Tree));
31849     }, this);
31850     delete this.attributes.items;
31851     
31852     
31853     
31854 };
31855 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31856     preventHScroll: true,
31857     /**
31858      * Returns true if this node is expanded
31859      * @return {Boolean}
31860      */
31861     isExpanded : function(){
31862         return this.expanded;
31863     },
31864
31865     /**
31866      * Returns the UI object for this node
31867      * @return {TreeNodeUI}
31868      */
31869     getUI : function(){
31870         return this.ui;
31871     },
31872
31873     // private override
31874     setFirstChild : function(node){
31875         var of = this.firstChild;
31876         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31877         if(this.childrenRendered && of && node != of){
31878             of.renderIndent(true, true);
31879         }
31880         if(this.rendered){
31881             this.renderIndent(true, true);
31882         }
31883     },
31884
31885     // private override
31886     setLastChild : function(node){
31887         var ol = this.lastChild;
31888         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31889         if(this.childrenRendered && ol && node != ol){
31890             ol.renderIndent(true, true);
31891         }
31892         if(this.rendered){
31893             this.renderIndent(true, true);
31894         }
31895     },
31896
31897     // these methods are overridden to provide lazy rendering support
31898     // private override
31899     appendChild : function()
31900     {
31901         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31902         if(node && this.childrenRendered){
31903             node.render();
31904         }
31905         this.ui.updateExpandIcon();
31906         return node;
31907     },
31908
31909     // private override
31910     removeChild : function(node){
31911         this.ownerTree.getSelectionModel().unselect(node);
31912         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31913         // if it's been rendered remove dom node
31914         if(this.childrenRendered){
31915             node.ui.remove();
31916         }
31917         if(this.childNodes.length < 1){
31918             this.collapse(false, false);
31919         }else{
31920             this.ui.updateExpandIcon();
31921         }
31922         if(!this.firstChild) {
31923             this.childrenRendered = false;
31924         }
31925         return node;
31926     },
31927
31928     // private override
31929     insertBefore : function(node, refNode){
31930         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31931         if(newNode && refNode && this.childrenRendered){
31932             node.render();
31933         }
31934         this.ui.updateExpandIcon();
31935         return newNode;
31936     },
31937
31938     /**
31939      * Sets the text for this node
31940      * @param {String} text
31941      */
31942     setText : function(text){
31943         var oldText = this.text;
31944         this.text = text;
31945         this.attributes.text = text;
31946         if(this.rendered){ // event without subscribing
31947             this.ui.onTextChange(this, text, oldText);
31948         }
31949         this.fireEvent("textchange", this, text, oldText);
31950     },
31951
31952     /**
31953      * Triggers selection of this node
31954      */
31955     select : function(){
31956         this.getOwnerTree().getSelectionModel().select(this);
31957     },
31958
31959     /**
31960      * Triggers deselection of this node
31961      */
31962     unselect : function(){
31963         this.getOwnerTree().getSelectionModel().unselect(this);
31964     },
31965
31966     /**
31967      * Returns true if this node is selected
31968      * @return {Boolean}
31969      */
31970     isSelected : function(){
31971         return this.getOwnerTree().getSelectionModel().isSelected(this);
31972     },
31973
31974     /**
31975      * Expand this node.
31976      * @param {Boolean} deep (optional) True to expand all children as well
31977      * @param {Boolean} anim (optional) false to cancel the default animation
31978      * @param {Function} callback (optional) A callback to be called when
31979      * expanding this node completes (does not wait for deep expand to complete).
31980      * Called with 1 parameter, this node.
31981      */
31982     expand : function(deep, anim, callback){
31983         if(!this.expanded){
31984             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31985                 return;
31986             }
31987             if(!this.childrenRendered){
31988                 this.renderChildren();
31989             }
31990             this.expanded = true;
31991             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31992                 this.ui.animExpand(function(){
31993                     this.fireEvent("expand", this);
31994                     if(typeof callback == "function"){
31995                         callback(this);
31996                     }
31997                     if(deep === true){
31998                         this.expandChildNodes(true);
31999                     }
32000                 }.createDelegate(this));
32001                 return;
32002             }else{
32003                 this.ui.expand();
32004                 this.fireEvent("expand", this);
32005                 if(typeof callback == "function"){
32006                     callback(this);
32007                 }
32008             }
32009         }else{
32010            if(typeof callback == "function"){
32011                callback(this);
32012            }
32013         }
32014         if(deep === true){
32015             this.expandChildNodes(true);
32016         }
32017     },
32018
32019     isHiddenRoot : function(){
32020         return this.isRoot && !this.getOwnerTree().rootVisible;
32021     },
32022
32023     /**
32024      * Collapse this node.
32025      * @param {Boolean} deep (optional) True to collapse all children as well
32026      * @param {Boolean} anim (optional) false to cancel the default animation
32027      */
32028     collapse : function(deep, anim){
32029         if(this.expanded && !this.isHiddenRoot()){
32030             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32031                 return;
32032             }
32033             this.expanded = false;
32034             if((this.getOwnerTree().animate && anim !== false) || anim){
32035                 this.ui.animCollapse(function(){
32036                     this.fireEvent("collapse", this);
32037                     if(deep === true){
32038                         this.collapseChildNodes(true);
32039                     }
32040                 }.createDelegate(this));
32041                 return;
32042             }else{
32043                 this.ui.collapse();
32044                 this.fireEvent("collapse", this);
32045             }
32046         }
32047         if(deep === true){
32048             var cs = this.childNodes;
32049             for(var i = 0, len = cs.length; i < len; i++) {
32050                 cs[i].collapse(true, false);
32051             }
32052         }
32053     },
32054
32055     // private
32056     delayedExpand : function(delay){
32057         if(!this.expandProcId){
32058             this.expandProcId = this.expand.defer(delay, this);
32059         }
32060     },
32061
32062     // private
32063     cancelExpand : function(){
32064         if(this.expandProcId){
32065             clearTimeout(this.expandProcId);
32066         }
32067         this.expandProcId = false;
32068     },
32069
32070     /**
32071      * Toggles expanded/collapsed state of the node
32072      */
32073     toggle : function(){
32074         if(this.expanded){
32075             this.collapse();
32076         }else{
32077             this.expand();
32078         }
32079     },
32080
32081     /**
32082      * Ensures all parent nodes are expanded
32083      */
32084     ensureVisible : function(callback){
32085         var tree = this.getOwnerTree();
32086         tree.expandPath(this.parentNode.getPath(), false, function(){
32087             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32088             Roo.callback(callback);
32089         }.createDelegate(this));
32090     },
32091
32092     /**
32093      * Expand all child nodes
32094      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32095      */
32096     expandChildNodes : function(deep){
32097         var cs = this.childNodes;
32098         for(var i = 0, len = cs.length; i < len; i++) {
32099                 cs[i].expand(deep);
32100         }
32101     },
32102
32103     /**
32104      * Collapse all child nodes
32105      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32106      */
32107     collapseChildNodes : function(deep){
32108         var cs = this.childNodes;
32109         for(var i = 0, len = cs.length; i < len; i++) {
32110                 cs[i].collapse(deep);
32111         }
32112     },
32113
32114     /**
32115      * Disables this node
32116      */
32117     disable : function(){
32118         this.disabled = true;
32119         this.unselect();
32120         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32121             this.ui.onDisableChange(this, true);
32122         }
32123         this.fireEvent("disabledchange", this, true);
32124     },
32125
32126     /**
32127      * Enables this node
32128      */
32129     enable : function(){
32130         this.disabled = false;
32131         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32132             this.ui.onDisableChange(this, false);
32133         }
32134         this.fireEvent("disabledchange", this, false);
32135     },
32136
32137     // private
32138     renderChildren : function(suppressEvent){
32139         if(suppressEvent !== false){
32140             this.fireEvent("beforechildrenrendered", this);
32141         }
32142         var cs = this.childNodes;
32143         for(var i = 0, len = cs.length; i < len; i++){
32144             cs[i].render(true);
32145         }
32146         this.childrenRendered = true;
32147     },
32148
32149     // private
32150     sort : function(fn, scope){
32151         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32152         if(this.childrenRendered){
32153             var cs = this.childNodes;
32154             for(var i = 0, len = cs.length; i < len; i++){
32155                 cs[i].render(true);
32156             }
32157         }
32158     },
32159
32160     // private
32161     render : function(bulkRender){
32162         this.ui.render(bulkRender);
32163         if(!this.rendered){
32164             this.rendered = true;
32165             if(this.expanded){
32166                 this.expanded = false;
32167                 this.expand(false, false);
32168             }
32169         }
32170     },
32171
32172     // private
32173     renderIndent : function(deep, refresh){
32174         if(refresh){
32175             this.ui.childIndent = null;
32176         }
32177         this.ui.renderIndent();
32178         if(deep === true && this.childrenRendered){
32179             var cs = this.childNodes;
32180             for(var i = 0, len = cs.length; i < len; i++){
32181                 cs[i].renderIndent(true, refresh);
32182             }
32183         }
32184     }
32185 });/*
32186  * Based on:
32187  * Ext JS Library 1.1.1
32188  * Copyright(c) 2006-2007, Ext JS, LLC.
32189  *
32190  * Originally Released Under LGPL - original licence link has changed is not relivant.
32191  *
32192  * Fork - LGPL
32193  * <script type="text/javascript">
32194  */
32195  
32196 /**
32197  * @class Roo.tree.AsyncTreeNode
32198  * @extends Roo.tree.TreeNode
32199  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32200  * @constructor
32201  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32202  */
32203  Roo.tree.AsyncTreeNode = function(config){
32204     this.loaded = false;
32205     this.loading = false;
32206     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32207     /**
32208     * @event beforeload
32209     * Fires before this node is loaded, return false to cancel
32210     * @param {Node} this This node
32211     */
32212     this.addEvents({'beforeload':true, 'load': true});
32213     /**
32214     * @event load
32215     * Fires when this node is loaded
32216     * @param {Node} this This node
32217     */
32218     /**
32219      * The loader used by this node (defaults to using the tree's defined loader)
32220      * @type TreeLoader
32221      * @property loader
32222      */
32223 };
32224 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32225     expand : function(deep, anim, callback){
32226         if(this.loading){ // if an async load is already running, waiting til it's done
32227             var timer;
32228             var f = function(){
32229                 if(!this.loading){ // done loading
32230                     clearInterval(timer);
32231                     this.expand(deep, anim, callback);
32232                 }
32233             }.createDelegate(this);
32234             timer = setInterval(f, 200);
32235             return;
32236         }
32237         if(!this.loaded){
32238             if(this.fireEvent("beforeload", this) === false){
32239                 return;
32240             }
32241             this.loading = true;
32242             this.ui.beforeLoad(this);
32243             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32244             if(loader){
32245                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32246                 return;
32247             }
32248         }
32249         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32250     },
32251     
32252     /**
32253      * Returns true if this node is currently loading
32254      * @return {Boolean}
32255      */
32256     isLoading : function(){
32257         return this.loading;  
32258     },
32259     
32260     loadComplete : function(deep, anim, callback){
32261         this.loading = false;
32262         this.loaded = true;
32263         this.ui.afterLoad(this);
32264         this.fireEvent("load", this);
32265         this.expand(deep, anim, callback);
32266     },
32267     
32268     /**
32269      * Returns true if this node has been loaded
32270      * @return {Boolean}
32271      */
32272     isLoaded : function(){
32273         return this.loaded;
32274     },
32275     
32276     hasChildNodes : function(){
32277         if(!this.isLeaf() && !this.loaded){
32278             return true;
32279         }else{
32280             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32281         }
32282     },
32283
32284     /**
32285      * Trigger a reload for this node
32286      * @param {Function} callback
32287      */
32288     reload : function(callback){
32289         this.collapse(false, false);
32290         while(this.firstChild){
32291             this.removeChild(this.firstChild);
32292         }
32293         this.childrenRendered = false;
32294         this.loaded = false;
32295         if(this.isHiddenRoot()){
32296             this.expanded = false;
32297         }
32298         this.expand(false, false, callback);
32299     }
32300 });/*
32301  * Based on:
32302  * Ext JS Library 1.1.1
32303  * Copyright(c) 2006-2007, Ext JS, LLC.
32304  *
32305  * Originally Released Under LGPL - original licence link has changed is not relivant.
32306  *
32307  * Fork - LGPL
32308  * <script type="text/javascript">
32309  */
32310  
32311 /**
32312  * @class Roo.tree.TreeNodeUI
32313  * @constructor
32314  * @param {Object} node The node to render
32315  * The TreeNode UI implementation is separate from the
32316  * tree implementation. Unless you are customizing the tree UI,
32317  * you should never have to use this directly.
32318  */
32319 Roo.tree.TreeNodeUI = function(node){
32320     this.node = node;
32321     this.rendered = false;
32322     this.animating = false;
32323     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32324 };
32325
32326 Roo.tree.TreeNodeUI.prototype = {
32327     removeChild : function(node){
32328         if(this.rendered){
32329             this.ctNode.removeChild(node.ui.getEl());
32330         }
32331     },
32332
32333     beforeLoad : function(){
32334          this.addClass("x-tree-node-loading");
32335     },
32336
32337     afterLoad : function(){
32338          this.removeClass("x-tree-node-loading");
32339     },
32340
32341     onTextChange : function(node, text, oldText){
32342         if(this.rendered){
32343             this.textNode.innerHTML = text;
32344         }
32345     },
32346
32347     onDisableChange : function(node, state){
32348         this.disabled = state;
32349         if(state){
32350             this.addClass("x-tree-node-disabled");
32351         }else{
32352             this.removeClass("x-tree-node-disabled");
32353         }
32354     },
32355
32356     onSelectedChange : function(state){
32357         if(state){
32358             this.focus();
32359             this.addClass("x-tree-selected");
32360         }else{
32361             //this.blur();
32362             this.removeClass("x-tree-selected");
32363         }
32364     },
32365
32366     onMove : function(tree, node, oldParent, newParent, index, refNode){
32367         this.childIndent = null;
32368         if(this.rendered){
32369             var targetNode = newParent.ui.getContainer();
32370             if(!targetNode){//target not rendered
32371                 this.holder = document.createElement("div");
32372                 this.holder.appendChild(this.wrap);
32373                 return;
32374             }
32375             var insertBefore = refNode ? refNode.ui.getEl() : null;
32376             if(insertBefore){
32377                 targetNode.insertBefore(this.wrap, insertBefore);
32378             }else{
32379                 targetNode.appendChild(this.wrap);
32380             }
32381             this.node.renderIndent(true);
32382         }
32383     },
32384
32385     addClass : function(cls){
32386         if(this.elNode){
32387             Roo.fly(this.elNode).addClass(cls);
32388         }
32389     },
32390
32391     removeClass : function(cls){
32392         if(this.elNode){
32393             Roo.fly(this.elNode).removeClass(cls);
32394         }
32395     },
32396
32397     remove : function(){
32398         if(this.rendered){
32399             this.holder = document.createElement("div");
32400             this.holder.appendChild(this.wrap);
32401         }
32402     },
32403
32404     fireEvent : function(){
32405         return this.node.fireEvent.apply(this.node, arguments);
32406     },
32407
32408     initEvents : function(){
32409         this.node.on("move", this.onMove, this);
32410         var E = Roo.EventManager;
32411         var a = this.anchor;
32412
32413         var el = Roo.fly(a, '_treeui');
32414
32415         if(Roo.isOpera){ // opera render bug ignores the CSS
32416             el.setStyle("text-decoration", "none");
32417         }
32418
32419         el.on("click", this.onClick, this);
32420         el.on("dblclick", this.onDblClick, this);
32421
32422         if(this.checkbox){
32423             Roo.EventManager.on(this.checkbox,
32424                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32425         }
32426
32427         el.on("contextmenu", this.onContextMenu, this);
32428
32429         var icon = Roo.fly(this.iconNode);
32430         icon.on("click", this.onClick, this);
32431         icon.on("dblclick", this.onDblClick, this);
32432         icon.on("contextmenu", this.onContextMenu, this);
32433         E.on(this.ecNode, "click", this.ecClick, this, true);
32434
32435         if(this.node.disabled){
32436             this.addClass("x-tree-node-disabled");
32437         }
32438         if(this.node.hidden){
32439             this.addClass("x-tree-node-disabled");
32440         }
32441         var ot = this.node.getOwnerTree();
32442         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32443         if(dd && (!this.node.isRoot || ot.rootVisible)){
32444             Roo.dd.Registry.register(this.elNode, {
32445                 node: this.node,
32446                 handles: this.getDDHandles(),
32447                 isHandle: false
32448             });
32449         }
32450     },
32451
32452     getDDHandles : function(){
32453         return [this.iconNode, this.textNode];
32454     },
32455
32456     hide : function(){
32457         if(this.rendered){
32458             this.wrap.style.display = "none";
32459         }
32460     },
32461
32462     show : function(){
32463         if(this.rendered){
32464             this.wrap.style.display = "";
32465         }
32466     },
32467
32468     onContextMenu : function(e){
32469         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32470             e.preventDefault();
32471             this.focus();
32472             this.fireEvent("contextmenu", this.node, e);
32473         }
32474     },
32475
32476     onClick : function(e){
32477         if(this.dropping){
32478             e.stopEvent();
32479             return;
32480         }
32481         if(this.fireEvent("beforeclick", this.node, e) !== false){
32482             if(!this.disabled && this.node.attributes.href){
32483                 this.fireEvent("click", this.node, e);
32484                 return;
32485             }
32486             e.preventDefault();
32487             if(this.disabled){
32488                 return;
32489             }
32490
32491             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32492                 this.node.toggle();
32493             }
32494
32495             this.fireEvent("click", this.node, e);
32496         }else{
32497             e.stopEvent();
32498         }
32499     },
32500
32501     onDblClick : function(e){
32502         e.preventDefault();
32503         if(this.disabled){
32504             return;
32505         }
32506         if(this.checkbox){
32507             this.toggleCheck();
32508         }
32509         if(!this.animating && this.node.hasChildNodes()){
32510             this.node.toggle();
32511         }
32512         this.fireEvent("dblclick", this.node, e);
32513     },
32514
32515     onCheckChange : function(){
32516         var checked = this.checkbox.checked;
32517         this.node.attributes.checked = checked;
32518         this.fireEvent('checkchange', this.node, checked);
32519     },
32520
32521     ecClick : function(e){
32522         if(!this.animating && this.node.hasChildNodes()){
32523             this.node.toggle();
32524         }
32525     },
32526
32527     startDrop : function(){
32528         this.dropping = true;
32529     },
32530
32531     // delayed drop so the click event doesn't get fired on a drop
32532     endDrop : function(){
32533        setTimeout(function(){
32534            this.dropping = false;
32535        }.createDelegate(this), 50);
32536     },
32537
32538     expand : function(){
32539         this.updateExpandIcon();
32540         this.ctNode.style.display = "";
32541     },
32542
32543     focus : function(){
32544         if(!this.node.preventHScroll){
32545             try{this.anchor.focus();
32546             }catch(e){}
32547         }else if(!Roo.isIE){
32548             try{
32549                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32550                 var l = noscroll.scrollLeft;
32551                 this.anchor.focus();
32552                 noscroll.scrollLeft = l;
32553             }catch(e){}
32554         }
32555     },
32556
32557     toggleCheck : function(value){
32558         var cb = this.checkbox;
32559         if(cb){
32560             cb.checked = (value === undefined ? !cb.checked : value);
32561         }
32562     },
32563
32564     blur : function(){
32565         try{
32566             this.anchor.blur();
32567         }catch(e){}
32568     },
32569
32570     animExpand : function(callback){
32571         var ct = Roo.get(this.ctNode);
32572         ct.stopFx();
32573         if(!this.node.hasChildNodes()){
32574             this.updateExpandIcon();
32575             this.ctNode.style.display = "";
32576             Roo.callback(callback);
32577             return;
32578         }
32579         this.animating = true;
32580         this.updateExpandIcon();
32581
32582         ct.slideIn('t', {
32583            callback : function(){
32584                this.animating = false;
32585                Roo.callback(callback);
32586             },
32587             scope: this,
32588             duration: this.node.ownerTree.duration || .25
32589         });
32590     },
32591
32592     highlight : function(){
32593         var tree = this.node.getOwnerTree();
32594         Roo.fly(this.wrap).highlight(
32595             tree.hlColor || "C3DAF9",
32596             {endColor: tree.hlBaseColor}
32597         );
32598     },
32599
32600     collapse : function(){
32601         this.updateExpandIcon();
32602         this.ctNode.style.display = "none";
32603     },
32604
32605     animCollapse : function(callback){
32606         var ct = Roo.get(this.ctNode);
32607         ct.enableDisplayMode('block');
32608         ct.stopFx();
32609
32610         this.animating = true;
32611         this.updateExpandIcon();
32612
32613         ct.slideOut('t', {
32614             callback : function(){
32615                this.animating = false;
32616                Roo.callback(callback);
32617             },
32618             scope: this,
32619             duration: this.node.ownerTree.duration || .25
32620         });
32621     },
32622
32623     getContainer : function(){
32624         return this.ctNode;
32625     },
32626
32627     getEl : function(){
32628         return this.wrap;
32629     },
32630
32631     appendDDGhost : function(ghostNode){
32632         ghostNode.appendChild(this.elNode.cloneNode(true));
32633     },
32634
32635     getDDRepairXY : function(){
32636         return Roo.lib.Dom.getXY(this.iconNode);
32637     },
32638
32639     onRender : function(){
32640         this.render();
32641     },
32642
32643     render : function(bulkRender){
32644         var n = this.node, a = n.attributes;
32645         var targetNode = n.parentNode ?
32646               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32647
32648         if(!this.rendered){
32649             this.rendered = true;
32650
32651             this.renderElements(n, a, targetNode, bulkRender);
32652
32653             if(a.qtip){
32654                if(this.textNode.setAttributeNS){
32655                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32656                    if(a.qtipTitle){
32657                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32658                    }
32659                }else{
32660                    this.textNode.setAttribute("ext:qtip", a.qtip);
32661                    if(a.qtipTitle){
32662                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32663                    }
32664                }
32665             }else if(a.qtipCfg){
32666                 a.qtipCfg.target = Roo.id(this.textNode);
32667                 Roo.QuickTips.register(a.qtipCfg);
32668             }
32669             this.initEvents();
32670             if(!this.node.expanded){
32671                 this.updateExpandIcon();
32672             }
32673         }else{
32674             if(bulkRender === true) {
32675                 targetNode.appendChild(this.wrap);
32676             }
32677         }
32678     },
32679
32680     renderElements : function(n, a, targetNode, bulkRender)
32681     {
32682         // add some indent caching, this helps performance when rendering a large tree
32683         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32684         var t = n.getOwnerTree();
32685         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32686         if (typeof(n.attributes.html) != 'undefined') {
32687             txt = n.attributes.html;
32688         }
32689         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32690         var cb = typeof a.checked == 'boolean';
32691         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32692         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32693             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32694             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32695             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32696             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32697             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32698              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32699                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32700             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32701             "</li>"];
32702
32703         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32704             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32705                                 n.nextSibling.ui.getEl(), buf.join(""));
32706         }else{
32707             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32708         }
32709
32710         this.elNode = this.wrap.childNodes[0];
32711         this.ctNode = this.wrap.childNodes[1];
32712         var cs = this.elNode.childNodes;
32713         this.indentNode = cs[0];
32714         this.ecNode = cs[1];
32715         this.iconNode = cs[2];
32716         var index = 3;
32717         if(cb){
32718             this.checkbox = cs[3];
32719             index++;
32720         }
32721         this.anchor = cs[index];
32722         this.textNode = cs[index].firstChild;
32723     },
32724
32725     getAnchor : function(){
32726         return this.anchor;
32727     },
32728
32729     getTextEl : function(){
32730         return this.textNode;
32731     },
32732
32733     getIconEl : function(){
32734         return this.iconNode;
32735     },
32736
32737     isChecked : function(){
32738         return this.checkbox ? this.checkbox.checked : false;
32739     },
32740
32741     updateExpandIcon : function(){
32742         if(this.rendered){
32743             var n = this.node, c1, c2;
32744             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32745             var hasChild = n.hasChildNodes();
32746             if(hasChild){
32747                 if(n.expanded){
32748                     cls += "-minus";
32749                     c1 = "x-tree-node-collapsed";
32750                     c2 = "x-tree-node-expanded";
32751                 }else{
32752                     cls += "-plus";
32753                     c1 = "x-tree-node-expanded";
32754                     c2 = "x-tree-node-collapsed";
32755                 }
32756                 if(this.wasLeaf){
32757                     this.removeClass("x-tree-node-leaf");
32758                     this.wasLeaf = false;
32759                 }
32760                 if(this.c1 != c1 || this.c2 != c2){
32761                     Roo.fly(this.elNode).replaceClass(c1, c2);
32762                     this.c1 = c1; this.c2 = c2;
32763                 }
32764             }else{
32765                 // this changes non-leafs into leafs if they have no children.
32766                 // it's not very rational behaviour..
32767                 
32768                 if(!this.wasLeaf && this.node.leaf){
32769                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32770                     delete this.c1;
32771                     delete this.c2;
32772                     this.wasLeaf = true;
32773                 }
32774             }
32775             var ecc = "x-tree-ec-icon "+cls;
32776             if(this.ecc != ecc){
32777                 this.ecNode.className = ecc;
32778                 this.ecc = ecc;
32779             }
32780         }
32781     },
32782
32783     getChildIndent : function(){
32784         if(!this.childIndent){
32785             var buf = [];
32786             var p = this.node;
32787             while(p){
32788                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32789                     if(!p.isLast()) {
32790                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32791                     } else {
32792                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32793                     }
32794                 }
32795                 p = p.parentNode;
32796             }
32797             this.childIndent = buf.join("");
32798         }
32799         return this.childIndent;
32800     },
32801
32802     renderIndent : function(){
32803         if(this.rendered){
32804             var indent = "";
32805             var p = this.node.parentNode;
32806             if(p){
32807                 indent = p.ui.getChildIndent();
32808             }
32809             if(this.indentMarkup != indent){ // don't rerender if not required
32810                 this.indentNode.innerHTML = indent;
32811                 this.indentMarkup = indent;
32812             }
32813             this.updateExpandIcon();
32814         }
32815     }
32816 };
32817
32818 Roo.tree.RootTreeNodeUI = function(){
32819     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32820 };
32821 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32822     render : function(){
32823         if(!this.rendered){
32824             var targetNode = this.node.ownerTree.innerCt.dom;
32825             this.node.expanded = true;
32826             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32827             this.wrap = this.ctNode = targetNode.firstChild;
32828         }
32829     },
32830     collapse : function(){
32831     },
32832     expand : function(){
32833     }
32834 });/*
32835  * Based on:
32836  * Ext JS Library 1.1.1
32837  * Copyright(c) 2006-2007, Ext JS, LLC.
32838  *
32839  * Originally Released Under LGPL - original licence link has changed is not relivant.
32840  *
32841  * Fork - LGPL
32842  * <script type="text/javascript">
32843  */
32844 /**
32845  * @class Roo.tree.TreeLoader
32846  * @extends Roo.util.Observable
32847  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32848  * nodes from a specified URL. The response must be a javascript Array definition
32849  * who's elements are node definition objects. eg:
32850  * <pre><code>
32851 {  success : true,
32852    data :      [
32853    
32854     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
32855     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
32856     ]
32857 }
32858
32859
32860 </code></pre>
32861  * <br><br>
32862  * The old style respose with just an array is still supported, but not recommended.
32863  * <br><br>
32864  *
32865  * A server request is sent, and child nodes are loaded only when a node is expanded.
32866  * The loading node's id is passed to the server under the parameter name "node" to
32867  * enable the server to produce the correct child nodes.
32868  * <br><br>
32869  * To pass extra parameters, an event handler may be attached to the "beforeload"
32870  * event, and the parameters specified in the TreeLoader's baseParams property:
32871  * <pre><code>
32872     myTreeLoader.on("beforeload", function(treeLoader, node) {
32873         this.baseParams.category = node.attributes.category;
32874     }, this);
32875 </code></pre><
32876  * This would pass an HTTP parameter called "category" to the server containing
32877  * the value of the Node's "category" attribute.
32878  * @constructor
32879  * Creates a new Treeloader.
32880  * @param {Object} config A config object containing config properties.
32881  */
32882 Roo.tree.TreeLoader = function(config){
32883     this.baseParams = {};
32884     this.requestMethod = "POST";
32885     Roo.apply(this, config);
32886
32887     this.addEvents({
32888     
32889         /**
32890          * @event beforeload
32891          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32892          * @param {Object} This TreeLoader object.
32893          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32894          * @param {Object} callback The callback function specified in the {@link #load} call.
32895          */
32896         beforeload : true,
32897         /**
32898          * @event load
32899          * Fires when the node has been successfuly loaded.
32900          * @param {Object} This TreeLoader object.
32901          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32902          * @param {Object} response The response object containing the data from the server.
32903          */
32904         load : true,
32905         /**
32906          * @event loadexception
32907          * Fires if the network request failed.
32908          * @param {Object} This TreeLoader object.
32909          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32910          * @param {Object} response The response object containing the data from the server.
32911          */
32912         loadexception : true,
32913         /**
32914          * @event create
32915          * Fires before a node is created, enabling you to return custom Node types 
32916          * @param {Object} This TreeLoader object.
32917          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32918          */
32919         create : true
32920     });
32921
32922     Roo.tree.TreeLoader.superclass.constructor.call(this);
32923 };
32924
32925 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32926     /**
32927     * @cfg {String} dataUrl The URL from which to request a Json string which
32928     * specifies an array of node definition object representing the child nodes
32929     * to be loaded.
32930     */
32931     /**
32932     * @cfg {Object} baseParams (optional) An object containing properties which
32933     * specify HTTP parameters to be passed to each request for child nodes.
32934     */
32935     /**
32936     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32937     * created by this loader. If the attributes sent by the server have an attribute in this object,
32938     * they take priority.
32939     */
32940     /**
32941     * @cfg {Object} uiProviders (optional) An object containing properties which
32942     * 
32943     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32944     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32945     * <i>uiProvider</i> attribute of a returned child node is a string rather
32946     * than a reference to a TreeNodeUI implementation, this that string value
32947     * is used as a property name in the uiProviders object. You can define the provider named
32948     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32949     */
32950     uiProviders : {},
32951
32952     /**
32953     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32954     * child nodes before loading.
32955     */
32956     clearOnLoad : true,
32957
32958     /**
32959     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32960     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32961     * Grid query { data : [ .....] }
32962     */
32963     
32964     root : false,
32965      /**
32966     * @cfg {String} queryParam (optional) 
32967     * Name of the query as it will be passed on the querystring (defaults to 'node')
32968     * eg. the request will be ?node=[id]
32969     */
32970     
32971     
32972     queryParam: false,
32973     
32974     /**
32975      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32976      * This is called automatically when a node is expanded, but may be used to reload
32977      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32978      * @param {Roo.tree.TreeNode} node
32979      * @param {Function} callback
32980      */
32981     load : function(node, callback){
32982         if(this.clearOnLoad){
32983             while(node.firstChild){
32984                 node.removeChild(node.firstChild);
32985             }
32986         }
32987         if(node.attributes.children){ // preloaded json children
32988             var cs = node.attributes.children;
32989             for(var i = 0, len = cs.length; i < len; i++){
32990                 node.appendChild(this.createNode(cs[i]));
32991             }
32992             if(typeof callback == "function"){
32993                 callback();
32994             }
32995         }else if(this.dataUrl){
32996             this.requestData(node, callback);
32997         }
32998     },
32999
33000     getParams: function(node){
33001         var buf = [], bp = this.baseParams;
33002         for(var key in bp){
33003             if(typeof bp[key] != "function"){
33004                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33005             }
33006         }
33007         var n = this.queryParam === false ? 'node' : this.queryParam;
33008         buf.push(n + "=", encodeURIComponent(node.id));
33009         return buf.join("");
33010     },
33011
33012     requestData : function(node, callback){
33013         if(this.fireEvent("beforeload", this, node, callback) !== false){
33014             this.transId = Roo.Ajax.request({
33015                 method:this.requestMethod,
33016                 url: this.dataUrl||this.url,
33017                 success: this.handleResponse,
33018                 failure: this.handleFailure,
33019                 scope: this,
33020                 argument: {callback: callback, node: node},
33021                 params: this.getParams(node)
33022             });
33023         }else{
33024             // if the load is cancelled, make sure we notify
33025             // the node that we are done
33026             if(typeof callback == "function"){
33027                 callback();
33028             }
33029         }
33030     },
33031
33032     isLoading : function(){
33033         return this.transId ? true : false;
33034     },
33035
33036     abort : function(){
33037         if(this.isLoading()){
33038             Roo.Ajax.abort(this.transId);
33039         }
33040     },
33041
33042     // private
33043     createNode : function(attr)
33044     {
33045         // apply baseAttrs, nice idea Corey!
33046         if(this.baseAttrs){
33047             Roo.applyIf(attr, this.baseAttrs);
33048         }
33049         if(this.applyLoader !== false){
33050             attr.loader = this;
33051         }
33052         // uiProvider = depreciated..
33053         
33054         if(typeof(attr.uiProvider) == 'string'){
33055            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33056                 /**  eval:var:attr */ eval(attr.uiProvider);
33057         }
33058         if(typeof(this.uiProviders['default']) != 'undefined') {
33059             attr.uiProvider = this.uiProviders['default'];
33060         }
33061         
33062         this.fireEvent('create', this, attr);
33063         
33064         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33065         return(attr.leaf ?
33066                         new Roo.tree.TreeNode(attr) :
33067                         new Roo.tree.AsyncTreeNode(attr));
33068     },
33069
33070     processResponse : function(response, node, callback)
33071     {
33072         var json = response.responseText;
33073         try {
33074             
33075             var o = Roo.decode(json);
33076             
33077             if (this.root === false && typeof(o.success) != undefined) {
33078                 this.root = 'data'; // the default behaviour for list like data..
33079                 }
33080                 
33081             if (this.root !== false &&  !o.success) {
33082                 // it's a failure condition.
33083                 var a = response.argument;
33084                 this.fireEvent("loadexception", this, a.node, response);
33085                 Roo.log("Load failed - should have a handler really");
33086                 return;
33087             }
33088             
33089             
33090             
33091             if (this.root !== false) {
33092                  o = o[this.root];
33093             }
33094             
33095             for(var i = 0, len = o.length; i < len; i++){
33096                 var n = this.createNode(o[i]);
33097                 if(n){
33098                     node.appendChild(n);
33099                 }
33100             }
33101             if(typeof callback == "function"){
33102                 callback(this, node);
33103             }
33104         }catch(e){
33105             this.handleFailure(response);
33106         }
33107     },
33108
33109     handleResponse : function(response){
33110         this.transId = false;
33111         var a = response.argument;
33112         this.processResponse(response, a.node, a.callback);
33113         this.fireEvent("load", this, a.node, response);
33114     },
33115
33116     handleFailure : function(response)
33117     {
33118         // should handle failure better..
33119         this.transId = false;
33120         var a = response.argument;
33121         this.fireEvent("loadexception", this, a.node, response);
33122         if(typeof a.callback == "function"){
33123             a.callback(this, a.node);
33124         }
33125     }
33126 });/*
33127  * Based on:
33128  * Ext JS Library 1.1.1
33129  * Copyright(c) 2006-2007, Ext JS, LLC.
33130  *
33131  * Originally Released Under LGPL - original licence link has changed is not relivant.
33132  *
33133  * Fork - LGPL
33134  * <script type="text/javascript">
33135  */
33136
33137 /**
33138 * @class Roo.tree.TreeFilter
33139 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33140 * @param {TreePanel} tree
33141 * @param {Object} config (optional)
33142  */
33143 Roo.tree.TreeFilter = function(tree, config){
33144     this.tree = tree;
33145     this.filtered = {};
33146     Roo.apply(this, config);
33147 };
33148
33149 Roo.tree.TreeFilter.prototype = {
33150     clearBlank:false,
33151     reverse:false,
33152     autoClear:false,
33153     remove:false,
33154
33155      /**
33156      * Filter the data by a specific attribute.
33157      * @param {String/RegExp} value Either string that the attribute value
33158      * should start with or a RegExp to test against the attribute
33159      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33160      * @param {TreeNode} startNode (optional) The node to start the filter at.
33161      */
33162     filter : function(value, attr, startNode){
33163         attr = attr || "text";
33164         var f;
33165         if(typeof value == "string"){
33166             var vlen = value.length;
33167             // auto clear empty filter
33168             if(vlen == 0 && this.clearBlank){
33169                 this.clear();
33170                 return;
33171             }
33172             value = value.toLowerCase();
33173             f = function(n){
33174                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33175             };
33176         }else if(value.exec){ // regex?
33177             f = function(n){
33178                 return value.test(n.attributes[attr]);
33179             };
33180         }else{
33181             throw 'Illegal filter type, must be string or regex';
33182         }
33183         this.filterBy(f, null, startNode);
33184         },
33185
33186     /**
33187      * Filter by a function. The passed function will be called with each
33188      * node in the tree (or from the startNode). If the function returns true, the node is kept
33189      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33190      * @param {Function} fn The filter function
33191      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33192      */
33193     filterBy : function(fn, scope, startNode){
33194         startNode = startNode || this.tree.root;
33195         if(this.autoClear){
33196             this.clear();
33197         }
33198         var af = this.filtered, rv = this.reverse;
33199         var f = function(n){
33200             if(n == startNode){
33201                 return true;
33202             }
33203             if(af[n.id]){
33204                 return false;
33205             }
33206             var m = fn.call(scope || n, n);
33207             if(!m || rv){
33208                 af[n.id] = n;
33209                 n.ui.hide();
33210                 return false;
33211             }
33212             return true;
33213         };
33214         startNode.cascade(f);
33215         if(this.remove){
33216            for(var id in af){
33217                if(typeof id != "function"){
33218                    var n = af[id];
33219                    if(n && n.parentNode){
33220                        n.parentNode.removeChild(n);
33221                    }
33222                }
33223            }
33224         }
33225     },
33226
33227     /**
33228      * Clears the current filter. Note: with the "remove" option
33229      * set a filter cannot be cleared.
33230      */
33231     clear : function(){
33232         var t = this.tree;
33233         var af = this.filtered;
33234         for(var id in af){
33235             if(typeof id != "function"){
33236                 var n = af[id];
33237                 if(n){
33238                     n.ui.show();
33239                 }
33240             }
33241         }
33242         this.filtered = {};
33243     }
33244 };
33245 /*
33246  * Based on:
33247  * Ext JS Library 1.1.1
33248  * Copyright(c) 2006-2007, Ext JS, LLC.
33249  *
33250  * Originally Released Under LGPL - original licence link has changed is not relivant.
33251  *
33252  * Fork - LGPL
33253  * <script type="text/javascript">
33254  */
33255  
33256
33257 /**
33258  * @class Roo.tree.TreeSorter
33259  * Provides sorting of nodes in a TreePanel
33260  * 
33261  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33262  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33263  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33264  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33265  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33266  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33267  * @constructor
33268  * @param {TreePanel} tree
33269  * @param {Object} config
33270  */
33271 Roo.tree.TreeSorter = function(tree, config){
33272     Roo.apply(this, config);
33273     tree.on("beforechildrenrendered", this.doSort, this);
33274     tree.on("append", this.updateSort, this);
33275     tree.on("insert", this.updateSort, this);
33276     
33277     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33278     var p = this.property || "text";
33279     var sortType = this.sortType;
33280     var fs = this.folderSort;
33281     var cs = this.caseSensitive === true;
33282     var leafAttr = this.leafAttr || 'leaf';
33283
33284     this.sortFn = function(n1, n2){
33285         if(fs){
33286             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33287                 return 1;
33288             }
33289             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33290                 return -1;
33291             }
33292         }
33293         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33294         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33295         if(v1 < v2){
33296                         return dsc ? +1 : -1;
33297                 }else if(v1 > v2){
33298                         return dsc ? -1 : +1;
33299         }else{
33300                 return 0;
33301         }
33302     };
33303 };
33304
33305 Roo.tree.TreeSorter.prototype = {
33306     doSort : function(node){
33307         node.sort(this.sortFn);
33308     },
33309     
33310     compareNodes : function(n1, n2){
33311         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33312     },
33313     
33314     updateSort : function(tree, node){
33315         if(node.childrenRendered){
33316             this.doSort.defer(1, this, [node]);
33317         }
33318     }
33319 };/*
33320  * Based on:
33321  * Ext JS Library 1.1.1
33322  * Copyright(c) 2006-2007, Ext JS, LLC.
33323  *
33324  * Originally Released Under LGPL - original licence link has changed is not relivant.
33325  *
33326  * Fork - LGPL
33327  * <script type="text/javascript">
33328  */
33329
33330 if(Roo.dd.DropZone){
33331     
33332 Roo.tree.TreeDropZone = function(tree, config){
33333     this.allowParentInsert = false;
33334     this.allowContainerDrop = false;
33335     this.appendOnly = false;
33336     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33337     this.tree = tree;
33338     this.lastInsertClass = "x-tree-no-status";
33339     this.dragOverData = {};
33340 };
33341
33342 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33343     ddGroup : "TreeDD",
33344     
33345     expandDelay : 1000,
33346     
33347     expandNode : function(node){
33348         if(node.hasChildNodes() && !node.isExpanded()){
33349             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33350         }
33351     },
33352     
33353     queueExpand : function(node){
33354         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33355     },
33356     
33357     cancelExpand : function(){
33358         if(this.expandProcId){
33359             clearTimeout(this.expandProcId);
33360             this.expandProcId = false;
33361         }
33362     },
33363     
33364     isValidDropPoint : function(n, pt, dd, e, data){
33365         if(!n || !data){ return false; }
33366         var targetNode = n.node;
33367         var dropNode = data.node;
33368         // default drop rules
33369         if(!(targetNode && targetNode.isTarget && pt)){
33370             return false;
33371         }
33372         if(pt == "append" && targetNode.allowChildren === false){
33373             return false;
33374         }
33375         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33376             return false;
33377         }
33378         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33379             return false;
33380         }
33381         // reuse the object
33382         var overEvent = this.dragOverData;
33383         overEvent.tree = this.tree;
33384         overEvent.target = targetNode;
33385         overEvent.data = data;
33386         overEvent.point = pt;
33387         overEvent.source = dd;
33388         overEvent.rawEvent = e;
33389         overEvent.dropNode = dropNode;
33390         overEvent.cancel = false;  
33391         var result = this.tree.fireEvent("nodedragover", overEvent);
33392         return overEvent.cancel === false && result !== false;
33393     },
33394     
33395     getDropPoint : function(e, n, dd){
33396         var tn = n.node;
33397         if(tn.isRoot){
33398             return tn.allowChildren !== false ? "append" : false; // always append for root
33399         }
33400         var dragEl = n.ddel;
33401         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33402         var y = Roo.lib.Event.getPageY(e);
33403         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33404         
33405         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33406         var noAppend = tn.allowChildren === false;
33407         if(this.appendOnly || tn.parentNode.allowChildren === false){
33408             return noAppend ? false : "append";
33409         }
33410         var noBelow = false;
33411         if(!this.allowParentInsert){
33412             noBelow = tn.hasChildNodes() && tn.isExpanded();
33413         }
33414         var q = (b - t) / (noAppend ? 2 : 3);
33415         if(y >= t && y < (t + q)){
33416             return "above";
33417         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33418             return "below";
33419         }else{
33420             return "append";
33421         }
33422     },
33423     
33424     onNodeEnter : function(n, dd, e, data){
33425         this.cancelExpand();
33426     },
33427     
33428     onNodeOver : function(n, dd, e, data){
33429         var pt = this.getDropPoint(e, n, dd);
33430         var node = n.node;
33431         
33432         // auto node expand check
33433         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33434             this.queueExpand(node);
33435         }else if(pt != "append"){
33436             this.cancelExpand();
33437         }
33438         
33439         // set the insert point style on the target node
33440         var returnCls = this.dropNotAllowed;
33441         if(this.isValidDropPoint(n, pt, dd, e, data)){
33442            if(pt){
33443                var el = n.ddel;
33444                var cls;
33445                if(pt == "above"){
33446                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33447                    cls = "x-tree-drag-insert-above";
33448                }else if(pt == "below"){
33449                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33450                    cls = "x-tree-drag-insert-below";
33451                }else{
33452                    returnCls = "x-tree-drop-ok-append";
33453                    cls = "x-tree-drag-append";
33454                }
33455                if(this.lastInsertClass != cls){
33456                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33457                    this.lastInsertClass = cls;
33458                }
33459            }
33460        }
33461        return returnCls;
33462     },
33463     
33464     onNodeOut : function(n, dd, e, data){
33465         this.cancelExpand();
33466         this.removeDropIndicators(n);
33467     },
33468     
33469     onNodeDrop : function(n, dd, e, data){
33470         var point = this.getDropPoint(e, n, dd);
33471         var targetNode = n.node;
33472         targetNode.ui.startDrop();
33473         if(!this.isValidDropPoint(n, point, dd, e, data)){
33474             targetNode.ui.endDrop();
33475             return false;
33476         }
33477         // first try to find the drop node
33478         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33479         var dropEvent = {
33480             tree : this.tree,
33481             target: targetNode,
33482             data: data,
33483             point: point,
33484             source: dd,
33485             rawEvent: e,
33486             dropNode: dropNode,
33487             cancel: !dropNode   
33488         };
33489         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33490         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33491             targetNode.ui.endDrop();
33492             return false;
33493         }
33494         // allow target changing
33495         targetNode = dropEvent.target;
33496         if(point == "append" && !targetNode.isExpanded()){
33497             targetNode.expand(false, null, function(){
33498                 this.completeDrop(dropEvent);
33499             }.createDelegate(this));
33500         }else{
33501             this.completeDrop(dropEvent);
33502         }
33503         return true;
33504     },
33505     
33506     completeDrop : function(de){
33507         var ns = de.dropNode, p = de.point, t = de.target;
33508         if(!(ns instanceof Array)){
33509             ns = [ns];
33510         }
33511         var n;
33512         for(var i = 0, len = ns.length; i < len; i++){
33513             n = ns[i];
33514             if(p == "above"){
33515                 t.parentNode.insertBefore(n, t);
33516             }else if(p == "below"){
33517                 t.parentNode.insertBefore(n, t.nextSibling);
33518             }else{
33519                 t.appendChild(n);
33520             }
33521         }
33522         n.ui.focus();
33523         if(this.tree.hlDrop){
33524             n.ui.highlight();
33525         }
33526         t.ui.endDrop();
33527         this.tree.fireEvent("nodedrop", de);
33528     },
33529     
33530     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33531         if(this.tree.hlDrop){
33532             dropNode.ui.focus();
33533             dropNode.ui.highlight();
33534         }
33535         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33536     },
33537     
33538     getTree : function(){
33539         return this.tree;
33540     },
33541     
33542     removeDropIndicators : function(n){
33543         if(n && n.ddel){
33544             var el = n.ddel;
33545             Roo.fly(el).removeClass([
33546                     "x-tree-drag-insert-above",
33547                     "x-tree-drag-insert-below",
33548                     "x-tree-drag-append"]);
33549             this.lastInsertClass = "_noclass";
33550         }
33551     },
33552     
33553     beforeDragDrop : function(target, e, id){
33554         this.cancelExpand();
33555         return true;
33556     },
33557     
33558     afterRepair : function(data){
33559         if(data && Roo.enableFx){
33560             data.node.ui.highlight();
33561         }
33562         this.hideProxy();
33563     }    
33564 });
33565
33566 }
33567 /*
33568  * Based on:
33569  * Ext JS Library 1.1.1
33570  * Copyright(c) 2006-2007, Ext JS, LLC.
33571  *
33572  * Originally Released Under LGPL - original licence link has changed is not relivant.
33573  *
33574  * Fork - LGPL
33575  * <script type="text/javascript">
33576  */
33577  
33578
33579 if(Roo.dd.DragZone){
33580 Roo.tree.TreeDragZone = function(tree, config){
33581     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33582     this.tree = tree;
33583 };
33584
33585 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33586     ddGroup : "TreeDD",
33587     
33588     onBeforeDrag : function(data, e){
33589         var n = data.node;
33590         return n && n.draggable && !n.disabled;
33591     },
33592     
33593     onInitDrag : function(e){
33594         var data = this.dragData;
33595         this.tree.getSelectionModel().select(data.node);
33596         this.proxy.update("");
33597         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33598         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33599     },
33600     
33601     getRepairXY : function(e, data){
33602         return data.node.ui.getDDRepairXY();
33603     },
33604     
33605     onEndDrag : function(data, e){
33606         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33607     },
33608     
33609     onValidDrop : function(dd, e, id){
33610         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33611         this.hideProxy();
33612     },
33613     
33614     beforeInvalidDrop : function(e, id){
33615         // this scrolls the original position back into view
33616         var sm = this.tree.getSelectionModel();
33617         sm.clearSelections();
33618         sm.select(this.dragData.node);
33619     }
33620 });
33621 }/*
33622  * Based on:
33623  * Ext JS Library 1.1.1
33624  * Copyright(c) 2006-2007, Ext JS, LLC.
33625  *
33626  * Originally Released Under LGPL - original licence link has changed is not relivant.
33627  *
33628  * Fork - LGPL
33629  * <script type="text/javascript">
33630  */
33631 /**
33632  * @class Roo.tree.TreeEditor
33633  * @extends Roo.Editor
33634  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33635  * as the editor field.
33636  * @constructor
33637  * @param {Object} config (used to be the tree panel.)
33638  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33639  * 
33640  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33641  * @cfg {Roo.form.TextField|Object} field The field configuration
33642  *
33643  * 
33644  */
33645 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33646     var tree = config;
33647     var field;
33648     if (oldconfig) { // old style..
33649         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33650     } else {
33651         // new style..
33652         tree = config.tree;
33653         config.field = config.field  || {};
33654         config.field.xtype = 'TextField';
33655         field = Roo.factory(config.field, Roo.form);
33656     }
33657     config = config || {};
33658     
33659     
33660     this.addEvents({
33661         /**
33662          * @event beforenodeedit
33663          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33664          * false from the handler of this event.
33665          * @param {Editor} this
33666          * @param {Roo.tree.Node} node 
33667          */
33668         "beforenodeedit" : true
33669     });
33670     
33671     //Roo.log(config);
33672     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33673
33674     this.tree = tree;
33675
33676     tree.on('beforeclick', this.beforeNodeClick, this);
33677     tree.getTreeEl().on('mousedown', this.hide, this);
33678     this.on('complete', this.updateNode, this);
33679     this.on('beforestartedit', this.fitToTree, this);
33680     this.on('startedit', this.bindScroll, this, {delay:10});
33681     this.on('specialkey', this.onSpecialKey, this);
33682 };
33683
33684 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33685     /**
33686      * @cfg {String} alignment
33687      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33688      */
33689     alignment: "l-l",
33690     // inherit
33691     autoSize: false,
33692     /**
33693      * @cfg {Boolean} hideEl
33694      * True to hide the bound element while the editor is displayed (defaults to false)
33695      */
33696     hideEl : false,
33697     /**
33698      * @cfg {String} cls
33699      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33700      */
33701     cls: "x-small-editor x-tree-editor",
33702     /**
33703      * @cfg {Boolean} shim
33704      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33705      */
33706     shim:false,
33707     // inherit
33708     shadow:"frame",
33709     /**
33710      * @cfg {Number} maxWidth
33711      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33712      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33713      * scroll and client offsets into account prior to each edit.
33714      */
33715     maxWidth: 250,
33716
33717     editDelay : 350,
33718
33719     // private
33720     fitToTree : function(ed, el){
33721         var td = this.tree.getTreeEl().dom, nd = el.dom;
33722         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33723             td.scrollLeft = nd.offsetLeft;
33724         }
33725         var w = Math.min(
33726                 this.maxWidth,
33727                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33728         this.setSize(w, '');
33729         
33730         return this.fireEvent('beforenodeedit', this, this.editNode);
33731         
33732     },
33733
33734     // private
33735     triggerEdit : function(node){
33736         this.completeEdit();
33737         this.editNode = node;
33738         this.startEdit(node.ui.textNode, node.text);
33739     },
33740
33741     // private
33742     bindScroll : function(){
33743         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33744     },
33745
33746     // private
33747     beforeNodeClick : function(node, e){
33748         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33749         this.lastClick = new Date();
33750         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33751             e.stopEvent();
33752             this.triggerEdit(node);
33753             return false;
33754         }
33755         return true;
33756     },
33757
33758     // private
33759     updateNode : function(ed, value){
33760         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33761         this.editNode.setText(value);
33762     },
33763
33764     // private
33765     onHide : function(){
33766         Roo.tree.TreeEditor.superclass.onHide.call(this);
33767         if(this.editNode){
33768             this.editNode.ui.focus();
33769         }
33770     },
33771
33772     // private
33773     onSpecialKey : function(field, e){
33774         var k = e.getKey();
33775         if(k == e.ESC){
33776             e.stopEvent();
33777             this.cancelEdit();
33778         }else if(k == e.ENTER && !e.hasModifier()){
33779             e.stopEvent();
33780             this.completeEdit();
33781         }
33782     }
33783 });//<Script type="text/javascript">
33784 /*
33785  * Based on:
33786  * Ext JS Library 1.1.1
33787  * Copyright(c) 2006-2007, Ext JS, LLC.
33788  *
33789  * Originally Released Under LGPL - original licence link has changed is not relivant.
33790  *
33791  * Fork - LGPL
33792  * <script type="text/javascript">
33793  */
33794  
33795 /**
33796  * Not documented??? - probably should be...
33797  */
33798
33799 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33800     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33801     
33802     renderElements : function(n, a, targetNode, bulkRender){
33803         //consel.log("renderElements?");
33804         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33805
33806         var t = n.getOwnerTree();
33807         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33808         
33809         var cols = t.columns;
33810         var bw = t.borderWidth;
33811         var c = cols[0];
33812         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33813          var cb = typeof a.checked == "boolean";
33814         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33815         var colcls = 'x-t-' + tid + '-c0';
33816         var buf = [
33817             '<li class="x-tree-node">',
33818             
33819                 
33820                 '<div class="x-tree-node-el ', a.cls,'">',
33821                     // extran...
33822                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33823                 
33824                 
33825                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33826                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33827                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33828                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33829                            (a.iconCls ? ' '+a.iconCls : ''),
33830                            '" unselectable="on" />',
33831                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33832                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33833                              
33834                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33835                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33836                             '<span unselectable="on" qtip="' + tx + '">',
33837                              tx,
33838                              '</span></a>' ,
33839                     '</div>',
33840                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33841                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33842                  ];
33843         for(var i = 1, len = cols.length; i < len; i++){
33844             c = cols[i];
33845             colcls = 'x-t-' + tid + '-c' +i;
33846             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33847             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33848                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33849                       "</div>");
33850          }
33851          
33852          buf.push(
33853             '</a>',
33854             '<div class="x-clear"></div></div>',
33855             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33856             "</li>");
33857         
33858         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33859             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33860                                 n.nextSibling.ui.getEl(), buf.join(""));
33861         }else{
33862             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33863         }
33864         var el = this.wrap.firstChild;
33865         this.elRow = el;
33866         this.elNode = el.firstChild;
33867         this.ranchor = el.childNodes[1];
33868         this.ctNode = this.wrap.childNodes[1];
33869         var cs = el.firstChild.childNodes;
33870         this.indentNode = cs[0];
33871         this.ecNode = cs[1];
33872         this.iconNode = cs[2];
33873         var index = 3;
33874         if(cb){
33875             this.checkbox = cs[3];
33876             index++;
33877         }
33878         this.anchor = cs[index];
33879         
33880         this.textNode = cs[index].firstChild;
33881         
33882         //el.on("click", this.onClick, this);
33883         //el.on("dblclick", this.onDblClick, this);
33884         
33885         
33886        // console.log(this);
33887     },
33888     initEvents : function(){
33889         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33890         
33891             
33892         var a = this.ranchor;
33893
33894         var el = Roo.get(a);
33895
33896         if(Roo.isOpera){ // opera render bug ignores the CSS
33897             el.setStyle("text-decoration", "none");
33898         }
33899
33900         el.on("click", this.onClick, this);
33901         el.on("dblclick", this.onDblClick, this);
33902         el.on("contextmenu", this.onContextMenu, this);
33903         
33904     },
33905     
33906     /*onSelectedChange : function(state){
33907         if(state){
33908             this.focus();
33909             this.addClass("x-tree-selected");
33910         }else{
33911             //this.blur();
33912             this.removeClass("x-tree-selected");
33913         }
33914     },*/
33915     addClass : function(cls){
33916         if(this.elRow){
33917             Roo.fly(this.elRow).addClass(cls);
33918         }
33919         
33920     },
33921     
33922     
33923     removeClass : function(cls){
33924         if(this.elRow){
33925             Roo.fly(this.elRow).removeClass(cls);
33926         }
33927     }
33928
33929     
33930     
33931 });//<Script type="text/javascript">
33932
33933 /*
33934  * Based on:
33935  * Ext JS Library 1.1.1
33936  * Copyright(c) 2006-2007, Ext JS, LLC.
33937  *
33938  * Originally Released Under LGPL - original licence link has changed is not relivant.
33939  *
33940  * Fork - LGPL
33941  * <script type="text/javascript">
33942  */
33943  
33944
33945 /**
33946  * @class Roo.tree.ColumnTree
33947  * @extends Roo.data.TreePanel
33948  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33949  * @cfg {int} borderWidth  compined right/left border allowance
33950  * @constructor
33951  * @param {String/HTMLElement/Element} el The container element
33952  * @param {Object} config
33953  */
33954 Roo.tree.ColumnTree =  function(el, config)
33955 {
33956    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33957    this.addEvents({
33958         /**
33959         * @event resize
33960         * Fire this event on a container when it resizes
33961         * @param {int} w Width
33962         * @param {int} h Height
33963         */
33964        "resize" : true
33965     });
33966     this.on('resize', this.onResize, this);
33967 };
33968
33969 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33970     //lines:false,
33971     
33972     
33973     borderWidth: Roo.isBorderBox ? 0 : 2, 
33974     headEls : false,
33975     
33976     render : function(){
33977         // add the header.....
33978        
33979         Roo.tree.ColumnTree.superclass.render.apply(this);
33980         
33981         this.el.addClass('x-column-tree');
33982         
33983         this.headers = this.el.createChild(
33984             {cls:'x-tree-headers'},this.innerCt.dom);
33985    
33986         var cols = this.columns, c;
33987         var totalWidth = 0;
33988         this.headEls = [];
33989         var  len = cols.length;
33990         for(var i = 0; i < len; i++){
33991              c = cols[i];
33992              totalWidth += c.width;
33993             this.headEls.push(this.headers.createChild({
33994                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33995                  cn: {
33996                      cls:'x-tree-hd-text',
33997                      html: c.header
33998                  },
33999                  style:'width:'+(c.width-this.borderWidth)+'px;'
34000              }));
34001         }
34002         this.headers.createChild({cls:'x-clear'});
34003         // prevent floats from wrapping when clipped
34004         this.headers.setWidth(totalWidth);
34005         //this.innerCt.setWidth(totalWidth);
34006         this.innerCt.setStyle({ overflow: 'auto' });
34007         this.onResize(this.width, this.height);
34008              
34009         
34010     },
34011     onResize : function(w,h)
34012     {
34013         this.height = h;
34014         this.width = w;
34015         // resize cols..
34016         this.innerCt.setWidth(this.width);
34017         this.innerCt.setHeight(this.height-20);
34018         
34019         // headers...
34020         var cols = this.columns, c;
34021         var totalWidth = 0;
34022         var expEl = false;
34023         var len = cols.length;
34024         for(var i = 0; i < len; i++){
34025             c = cols[i];
34026             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34027                 // it's the expander..
34028                 expEl  = this.headEls[i];
34029                 continue;
34030             }
34031             totalWidth += c.width;
34032             
34033         }
34034         if (expEl) {
34035             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34036         }
34037         this.headers.setWidth(w-20);
34038
34039         
34040         
34041         
34042     }
34043 });
34044 /*
34045  * Based on:
34046  * Ext JS Library 1.1.1
34047  * Copyright(c) 2006-2007, Ext JS, LLC.
34048  *
34049  * Originally Released Under LGPL - original licence link has changed is not relivant.
34050  *
34051  * Fork - LGPL
34052  * <script type="text/javascript">
34053  */
34054  
34055 /**
34056  * @class Roo.menu.Menu
34057  * @extends Roo.util.Observable
34058  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34059  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34060  * @constructor
34061  * Creates a new Menu
34062  * @param {Object} config Configuration options
34063  */
34064 Roo.menu.Menu = function(config){
34065     Roo.apply(this, config);
34066     this.id = this.id || Roo.id();
34067     this.addEvents({
34068         /**
34069          * @event beforeshow
34070          * Fires before this menu is displayed
34071          * @param {Roo.menu.Menu} this
34072          */
34073         beforeshow : true,
34074         /**
34075          * @event beforehide
34076          * Fires before this menu is hidden
34077          * @param {Roo.menu.Menu} this
34078          */
34079         beforehide : true,
34080         /**
34081          * @event show
34082          * Fires after this menu is displayed
34083          * @param {Roo.menu.Menu} this
34084          */
34085         show : true,
34086         /**
34087          * @event hide
34088          * Fires after this menu is hidden
34089          * @param {Roo.menu.Menu} this
34090          */
34091         hide : true,
34092         /**
34093          * @event click
34094          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34095          * @param {Roo.menu.Menu} this
34096          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34097          * @param {Roo.EventObject} e
34098          */
34099         click : true,
34100         /**
34101          * @event mouseover
34102          * Fires when the mouse is hovering over this menu
34103          * @param {Roo.menu.Menu} this
34104          * @param {Roo.EventObject} e
34105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34106          */
34107         mouseover : true,
34108         /**
34109          * @event mouseout
34110          * Fires when the mouse exits this menu
34111          * @param {Roo.menu.Menu} this
34112          * @param {Roo.EventObject} e
34113          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34114          */
34115         mouseout : true,
34116         /**
34117          * @event itemclick
34118          * Fires when a menu item contained in this menu is clicked
34119          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34120          * @param {Roo.EventObject} e
34121          */
34122         itemclick: true
34123     });
34124     if (this.registerMenu) {
34125         Roo.menu.MenuMgr.register(this);
34126     }
34127     
34128     var mis = this.items;
34129     this.items = new Roo.util.MixedCollection();
34130     if(mis){
34131         this.add.apply(this, mis);
34132     }
34133 };
34134
34135 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34136     /**
34137      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34138      */
34139     minWidth : 120,
34140     /**
34141      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34142      * for bottom-right shadow (defaults to "sides")
34143      */
34144     shadow : "sides",
34145     /**
34146      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34147      * this menu (defaults to "tl-tr?")
34148      */
34149     subMenuAlign : "tl-tr?",
34150     /**
34151      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34152      * relative to its element of origin (defaults to "tl-bl?")
34153      */
34154     defaultAlign : "tl-bl?",
34155     /**
34156      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34157      */
34158     allowOtherMenus : false,
34159     /**
34160      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34161      */
34162     registerMenu : true,
34163
34164     hidden:true,
34165
34166     // private
34167     render : function(){
34168         if(this.el){
34169             return;
34170         }
34171         var el = this.el = new Roo.Layer({
34172             cls: "x-menu",
34173             shadow:this.shadow,
34174             constrain: false,
34175             parentEl: this.parentEl || document.body,
34176             zindex:15000
34177         });
34178
34179         this.keyNav = new Roo.menu.MenuNav(this);
34180
34181         if(this.plain){
34182             el.addClass("x-menu-plain");
34183         }
34184         if(this.cls){
34185             el.addClass(this.cls);
34186         }
34187         // generic focus element
34188         this.focusEl = el.createChild({
34189             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34190         });
34191         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34192         ul.on("click", this.onClick, this);
34193         ul.on("mouseover", this.onMouseOver, this);
34194         ul.on("mouseout", this.onMouseOut, this);
34195         this.items.each(function(item){
34196             var li = document.createElement("li");
34197             li.className = "x-menu-list-item";
34198             ul.dom.appendChild(li);
34199             item.render(li, this);
34200         }, this);
34201         this.ul = ul;
34202         this.autoWidth();
34203     },
34204
34205     // private
34206     autoWidth : function(){
34207         var el = this.el, ul = this.ul;
34208         if(!el){
34209             return;
34210         }
34211         var w = this.width;
34212         if(w){
34213             el.setWidth(w);
34214         }else if(Roo.isIE){
34215             el.setWidth(this.minWidth);
34216             var t = el.dom.offsetWidth; // force recalc
34217             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34218         }
34219     },
34220
34221     // private
34222     delayAutoWidth : function(){
34223         if(this.rendered){
34224             if(!this.awTask){
34225                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34226             }
34227             this.awTask.delay(20);
34228         }
34229     },
34230
34231     // private
34232     findTargetItem : function(e){
34233         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34234         if(t && t.menuItemId){
34235             return this.items.get(t.menuItemId);
34236         }
34237     },
34238
34239     // private
34240     onClick : function(e){
34241         var t;
34242         if(t = this.findTargetItem(e)){
34243             t.onClick(e);
34244             this.fireEvent("click", this, t, e);
34245         }
34246     },
34247
34248     // private
34249     setActiveItem : function(item, autoExpand){
34250         if(item != this.activeItem){
34251             if(this.activeItem){
34252                 this.activeItem.deactivate();
34253             }
34254             this.activeItem = item;
34255             item.activate(autoExpand);
34256         }else if(autoExpand){
34257             item.expandMenu();
34258         }
34259     },
34260
34261     // private
34262     tryActivate : function(start, step){
34263         var items = this.items;
34264         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34265             var item = items.get(i);
34266             if(!item.disabled && item.canActivate){
34267                 this.setActiveItem(item, false);
34268                 return item;
34269             }
34270         }
34271         return false;
34272     },
34273
34274     // private
34275     onMouseOver : function(e){
34276         var t;
34277         if(t = this.findTargetItem(e)){
34278             if(t.canActivate && !t.disabled){
34279                 this.setActiveItem(t, true);
34280             }
34281         }
34282         this.fireEvent("mouseover", this, e, t);
34283     },
34284
34285     // private
34286     onMouseOut : function(e){
34287         var t;
34288         if(t = this.findTargetItem(e)){
34289             if(t == this.activeItem && t.shouldDeactivate(e)){
34290                 this.activeItem.deactivate();
34291                 delete this.activeItem;
34292             }
34293         }
34294         this.fireEvent("mouseout", this, e, t);
34295     },
34296
34297     /**
34298      * Read-only.  Returns true if the menu is currently displayed, else false.
34299      * @type Boolean
34300      */
34301     isVisible : function(){
34302         return this.el && !this.hidden;
34303     },
34304
34305     /**
34306      * Displays this menu relative to another element
34307      * @param {String/HTMLElement/Roo.Element} element The element to align to
34308      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34309      * the element (defaults to this.defaultAlign)
34310      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34311      */
34312     show : function(el, pos, parentMenu){
34313         this.parentMenu = parentMenu;
34314         if(!this.el){
34315             this.render();
34316         }
34317         this.fireEvent("beforeshow", this);
34318         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34319     },
34320
34321     /**
34322      * Displays this menu at a specific xy position
34323      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34324      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34325      */
34326     showAt : function(xy, parentMenu, /* private: */_e){
34327         this.parentMenu = parentMenu;
34328         if(!this.el){
34329             this.render();
34330         }
34331         if(_e !== false){
34332             this.fireEvent("beforeshow", this);
34333             xy = this.el.adjustForConstraints(xy);
34334         }
34335         this.el.setXY(xy);
34336         this.el.show();
34337         this.hidden = false;
34338         this.focus();
34339         this.fireEvent("show", this);
34340     },
34341
34342     focus : function(){
34343         if(!this.hidden){
34344             this.doFocus.defer(50, this);
34345         }
34346     },
34347
34348     doFocus : function(){
34349         if(!this.hidden){
34350             this.focusEl.focus();
34351         }
34352     },
34353
34354     /**
34355      * Hides this menu and optionally all parent menus
34356      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34357      */
34358     hide : function(deep){
34359         if(this.el && this.isVisible()){
34360             this.fireEvent("beforehide", this);
34361             if(this.activeItem){
34362                 this.activeItem.deactivate();
34363                 this.activeItem = null;
34364             }
34365             this.el.hide();
34366             this.hidden = true;
34367             this.fireEvent("hide", this);
34368         }
34369         if(deep === true && this.parentMenu){
34370             this.parentMenu.hide(true);
34371         }
34372     },
34373
34374     /**
34375      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34376      * Any of the following are valid:
34377      * <ul>
34378      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34379      * <li>An HTMLElement object which will be converted to a menu item</li>
34380      * <li>A menu item config object that will be created as a new menu item</li>
34381      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34382      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34383      * </ul>
34384      * Usage:
34385      * <pre><code>
34386 // Create the menu
34387 var menu = new Roo.menu.Menu();
34388
34389 // Create a menu item to add by reference
34390 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34391
34392 // Add a bunch of items at once using different methods.
34393 // Only the last item added will be returned.
34394 var item = menu.add(
34395     menuItem,                // add existing item by ref
34396     'Dynamic Item',          // new TextItem
34397     '-',                     // new separator
34398     { text: 'Config Item' }  // new item by config
34399 );
34400 </code></pre>
34401      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34402      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34403      */
34404     add : function(){
34405         var a = arguments, l = a.length, item;
34406         for(var i = 0; i < l; i++){
34407             var el = a[i];
34408             if ((typeof(el) == "object") && el.xtype && el.xns) {
34409                 el = Roo.factory(el, Roo.menu);
34410             }
34411             
34412             if(el.render){ // some kind of Item
34413                 item = this.addItem(el);
34414             }else if(typeof el == "string"){ // string
34415                 if(el == "separator" || el == "-"){
34416                     item = this.addSeparator();
34417                 }else{
34418                     item = this.addText(el);
34419                 }
34420             }else if(el.tagName || el.el){ // element
34421                 item = this.addElement(el);
34422             }else if(typeof el == "object"){ // must be menu item config?
34423                 item = this.addMenuItem(el);
34424             }
34425         }
34426         return item;
34427     },
34428
34429     /**
34430      * Returns this menu's underlying {@link Roo.Element} object
34431      * @return {Roo.Element} The element
34432      */
34433     getEl : function(){
34434         if(!this.el){
34435             this.render();
34436         }
34437         return this.el;
34438     },
34439
34440     /**
34441      * Adds a separator bar to the menu
34442      * @return {Roo.menu.Item} The menu item that was added
34443      */
34444     addSeparator : function(){
34445         return this.addItem(new Roo.menu.Separator());
34446     },
34447
34448     /**
34449      * Adds an {@link Roo.Element} object to the menu
34450      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34451      * @return {Roo.menu.Item} The menu item that was added
34452      */
34453     addElement : function(el){
34454         return this.addItem(new Roo.menu.BaseItem(el));
34455     },
34456
34457     /**
34458      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34459      * @param {Roo.menu.Item} item The menu item to add
34460      * @return {Roo.menu.Item} The menu item that was added
34461      */
34462     addItem : function(item){
34463         this.items.add(item);
34464         if(this.ul){
34465             var li = document.createElement("li");
34466             li.className = "x-menu-list-item";
34467             this.ul.dom.appendChild(li);
34468             item.render(li, this);
34469             this.delayAutoWidth();
34470         }
34471         return item;
34472     },
34473
34474     /**
34475      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34476      * @param {Object} config A MenuItem config object
34477      * @return {Roo.menu.Item} The menu item that was added
34478      */
34479     addMenuItem : function(config){
34480         if(!(config instanceof Roo.menu.Item)){
34481             if(typeof config.checked == "boolean"){ // must be check menu item config?
34482                 config = new Roo.menu.CheckItem(config);
34483             }else{
34484                 config = new Roo.menu.Item(config);
34485             }
34486         }
34487         return this.addItem(config);
34488     },
34489
34490     /**
34491      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34492      * @param {String} text The text to display in the menu item
34493      * @return {Roo.menu.Item} The menu item that was added
34494      */
34495     addText : function(text){
34496         return this.addItem(new Roo.menu.TextItem({ text : text }));
34497     },
34498
34499     /**
34500      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34501      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34502      * @param {Roo.menu.Item} item The menu item to add
34503      * @return {Roo.menu.Item} The menu item that was added
34504      */
34505     insert : function(index, item){
34506         this.items.insert(index, item);
34507         if(this.ul){
34508             var li = document.createElement("li");
34509             li.className = "x-menu-list-item";
34510             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34511             item.render(li, this);
34512             this.delayAutoWidth();
34513         }
34514         return item;
34515     },
34516
34517     /**
34518      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34519      * @param {Roo.menu.Item} item The menu item to remove
34520      */
34521     remove : function(item){
34522         this.items.removeKey(item.id);
34523         item.destroy();
34524     },
34525
34526     /**
34527      * Removes and destroys all items in the menu
34528      */
34529     removeAll : function(){
34530         var f;
34531         while(f = this.items.first()){
34532             this.remove(f);
34533         }
34534     }
34535 });
34536
34537 // MenuNav is a private utility class used internally by the Menu
34538 Roo.menu.MenuNav = function(menu){
34539     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34540     this.scope = this.menu = menu;
34541 };
34542
34543 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34544     doRelay : function(e, h){
34545         var k = e.getKey();
34546         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34547             this.menu.tryActivate(0, 1);
34548             return false;
34549         }
34550         return h.call(this.scope || this, e, this.menu);
34551     },
34552
34553     up : function(e, m){
34554         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34555             m.tryActivate(m.items.length-1, -1);
34556         }
34557     },
34558
34559     down : function(e, m){
34560         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34561             m.tryActivate(0, 1);
34562         }
34563     },
34564
34565     right : function(e, m){
34566         if(m.activeItem){
34567             m.activeItem.expandMenu(true);
34568         }
34569     },
34570
34571     left : function(e, m){
34572         m.hide();
34573         if(m.parentMenu && m.parentMenu.activeItem){
34574             m.parentMenu.activeItem.activate();
34575         }
34576     },
34577
34578     enter : function(e, m){
34579         if(m.activeItem){
34580             e.stopPropagation();
34581             m.activeItem.onClick(e);
34582             m.fireEvent("click", this, m.activeItem);
34583             return true;
34584         }
34585     }
34586 });/*
34587  * Based on:
34588  * Ext JS Library 1.1.1
34589  * Copyright(c) 2006-2007, Ext JS, LLC.
34590  *
34591  * Originally Released Under LGPL - original licence link has changed is not relivant.
34592  *
34593  * Fork - LGPL
34594  * <script type="text/javascript">
34595  */
34596  
34597 /**
34598  * @class Roo.menu.MenuMgr
34599  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34600  * @singleton
34601  */
34602 Roo.menu.MenuMgr = function(){
34603    var menus, active, groups = {}, attached = false, lastShow = new Date();
34604
34605    // private - called when first menu is created
34606    function init(){
34607        menus = {};
34608        active = new Roo.util.MixedCollection();
34609        Roo.get(document).addKeyListener(27, function(){
34610            if(active.length > 0){
34611                hideAll();
34612            }
34613        });
34614    }
34615
34616    // private
34617    function hideAll(){
34618        if(active && active.length > 0){
34619            var c = active.clone();
34620            c.each(function(m){
34621                m.hide();
34622            });
34623        }
34624    }
34625
34626    // private
34627    function onHide(m){
34628        active.remove(m);
34629        if(active.length < 1){
34630            Roo.get(document).un("mousedown", onMouseDown);
34631            attached = false;
34632        }
34633    }
34634
34635    // private
34636    function onShow(m){
34637        var last = active.last();
34638        lastShow = new Date();
34639        active.add(m);
34640        if(!attached){
34641            Roo.get(document).on("mousedown", onMouseDown);
34642            attached = true;
34643        }
34644        if(m.parentMenu){
34645           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34646           m.parentMenu.activeChild = m;
34647        }else if(last && last.isVisible()){
34648           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34649        }
34650    }
34651
34652    // private
34653    function onBeforeHide(m){
34654        if(m.activeChild){
34655            m.activeChild.hide();
34656        }
34657        if(m.autoHideTimer){
34658            clearTimeout(m.autoHideTimer);
34659            delete m.autoHideTimer;
34660        }
34661    }
34662
34663    // private
34664    function onBeforeShow(m){
34665        var pm = m.parentMenu;
34666        if(!pm && !m.allowOtherMenus){
34667            hideAll();
34668        }else if(pm && pm.activeChild && active != m){
34669            pm.activeChild.hide();
34670        }
34671    }
34672
34673    // private
34674    function onMouseDown(e){
34675        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34676            hideAll();
34677        }
34678    }
34679
34680    // private
34681    function onBeforeCheck(mi, state){
34682        if(state){
34683            var g = groups[mi.group];
34684            for(var i = 0, l = g.length; i < l; i++){
34685                if(g[i] != mi){
34686                    g[i].setChecked(false);
34687                }
34688            }
34689        }
34690    }
34691
34692    return {
34693
34694        /**
34695         * Hides all menus that are currently visible
34696         */
34697        hideAll : function(){
34698             hideAll();  
34699        },
34700
34701        // private
34702        register : function(menu){
34703            if(!menus){
34704                init();
34705            }
34706            menus[menu.id] = menu;
34707            menu.on("beforehide", onBeforeHide);
34708            menu.on("hide", onHide);
34709            menu.on("beforeshow", onBeforeShow);
34710            menu.on("show", onShow);
34711            var g = menu.group;
34712            if(g && menu.events["checkchange"]){
34713                if(!groups[g]){
34714                    groups[g] = [];
34715                }
34716                groups[g].push(menu);
34717                menu.on("checkchange", onCheck);
34718            }
34719        },
34720
34721         /**
34722          * Returns a {@link Roo.menu.Menu} object
34723          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34724          * be used to generate and return a new Menu instance.
34725          */
34726        get : function(menu){
34727            if(typeof menu == "string"){ // menu id
34728                return menus[menu];
34729            }else if(menu.events){  // menu instance
34730                return menu;
34731            }else if(typeof menu.length == 'number'){ // array of menu items?
34732                return new Roo.menu.Menu({items:menu});
34733            }else{ // otherwise, must be a config
34734                return new Roo.menu.Menu(menu);
34735            }
34736        },
34737
34738        // private
34739        unregister : function(menu){
34740            delete menus[menu.id];
34741            menu.un("beforehide", onBeforeHide);
34742            menu.un("hide", onHide);
34743            menu.un("beforeshow", onBeforeShow);
34744            menu.un("show", onShow);
34745            var g = menu.group;
34746            if(g && menu.events["checkchange"]){
34747                groups[g].remove(menu);
34748                menu.un("checkchange", onCheck);
34749            }
34750        },
34751
34752        // private
34753        registerCheckable : function(menuItem){
34754            var g = menuItem.group;
34755            if(g){
34756                if(!groups[g]){
34757                    groups[g] = [];
34758                }
34759                groups[g].push(menuItem);
34760                menuItem.on("beforecheckchange", onBeforeCheck);
34761            }
34762        },
34763
34764        // private
34765        unregisterCheckable : function(menuItem){
34766            var g = menuItem.group;
34767            if(g){
34768                groups[g].remove(menuItem);
34769                menuItem.un("beforecheckchange", onBeforeCheck);
34770            }
34771        }
34772    };
34773 }();/*
34774  * Based on:
34775  * Ext JS Library 1.1.1
34776  * Copyright(c) 2006-2007, Ext JS, LLC.
34777  *
34778  * Originally Released Under LGPL - original licence link has changed is not relivant.
34779  *
34780  * Fork - LGPL
34781  * <script type="text/javascript">
34782  */
34783  
34784
34785 /**
34786  * @class Roo.menu.BaseItem
34787  * @extends Roo.Component
34788  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34789  * management and base configuration options shared by all menu components.
34790  * @constructor
34791  * Creates a new BaseItem
34792  * @param {Object} config Configuration options
34793  */
34794 Roo.menu.BaseItem = function(config){
34795     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34796
34797     this.addEvents({
34798         /**
34799          * @event click
34800          * Fires when this item is clicked
34801          * @param {Roo.menu.BaseItem} this
34802          * @param {Roo.EventObject} e
34803          */
34804         click: true,
34805         /**
34806          * @event activate
34807          * Fires when this item is activated
34808          * @param {Roo.menu.BaseItem} this
34809          */
34810         activate : true,
34811         /**
34812          * @event deactivate
34813          * Fires when this item is deactivated
34814          * @param {Roo.menu.BaseItem} this
34815          */
34816         deactivate : true
34817     });
34818
34819     if(this.handler){
34820         this.on("click", this.handler, this.scope, true);
34821     }
34822 };
34823
34824 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34825     /**
34826      * @cfg {Function} handler
34827      * A function that will handle the click event of this menu item (defaults to undefined)
34828      */
34829     /**
34830      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34831      */
34832     canActivate : false,
34833     /**
34834      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34835      */
34836     activeClass : "x-menu-item-active",
34837     /**
34838      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34839      */
34840     hideOnClick : true,
34841     /**
34842      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34843      */
34844     hideDelay : 100,
34845
34846     // private
34847     ctype: "Roo.menu.BaseItem",
34848
34849     // private
34850     actionMode : "container",
34851
34852     // private
34853     render : function(container, parentMenu){
34854         this.parentMenu = parentMenu;
34855         Roo.menu.BaseItem.superclass.render.call(this, container);
34856         this.container.menuItemId = this.id;
34857     },
34858
34859     // private
34860     onRender : function(container, position){
34861         this.el = Roo.get(this.el);
34862         container.dom.appendChild(this.el.dom);
34863     },
34864
34865     // private
34866     onClick : function(e){
34867         if(!this.disabled && this.fireEvent("click", this, e) !== false
34868                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34869             this.handleClick(e);
34870         }else{
34871             e.stopEvent();
34872         }
34873     },
34874
34875     // private
34876     activate : function(){
34877         if(this.disabled){
34878             return false;
34879         }
34880         var li = this.container;
34881         li.addClass(this.activeClass);
34882         this.region = li.getRegion().adjust(2, 2, -2, -2);
34883         this.fireEvent("activate", this);
34884         return true;
34885     },
34886
34887     // private
34888     deactivate : function(){
34889         this.container.removeClass(this.activeClass);
34890         this.fireEvent("deactivate", this);
34891     },
34892
34893     // private
34894     shouldDeactivate : function(e){
34895         return !this.region || !this.region.contains(e.getPoint());
34896     },
34897
34898     // private
34899     handleClick : function(e){
34900         if(this.hideOnClick){
34901             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34902         }
34903     },
34904
34905     // private
34906     expandMenu : function(autoActivate){
34907         // do nothing
34908     },
34909
34910     // private
34911     hideMenu : function(){
34912         // do nothing
34913     }
34914 });/*
34915  * Based on:
34916  * Ext JS Library 1.1.1
34917  * Copyright(c) 2006-2007, Ext JS, LLC.
34918  *
34919  * Originally Released Under LGPL - original licence link has changed is not relivant.
34920  *
34921  * Fork - LGPL
34922  * <script type="text/javascript">
34923  */
34924  
34925 /**
34926  * @class Roo.menu.Adapter
34927  * @extends Roo.menu.BaseItem
34928  * 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.
34929  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34930  * @constructor
34931  * Creates a new Adapter
34932  * @param {Object} config Configuration options
34933  */
34934 Roo.menu.Adapter = function(component, config){
34935     Roo.menu.Adapter.superclass.constructor.call(this, config);
34936     this.component = component;
34937 };
34938 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34939     // private
34940     canActivate : true,
34941
34942     // private
34943     onRender : function(container, position){
34944         this.component.render(container);
34945         this.el = this.component.getEl();
34946     },
34947
34948     // private
34949     activate : function(){
34950         if(this.disabled){
34951             return false;
34952         }
34953         this.component.focus();
34954         this.fireEvent("activate", this);
34955         return true;
34956     },
34957
34958     // private
34959     deactivate : function(){
34960         this.fireEvent("deactivate", this);
34961     },
34962
34963     // private
34964     disable : function(){
34965         this.component.disable();
34966         Roo.menu.Adapter.superclass.disable.call(this);
34967     },
34968
34969     // private
34970     enable : function(){
34971         this.component.enable();
34972         Roo.menu.Adapter.superclass.enable.call(this);
34973     }
34974 });/*
34975  * Based on:
34976  * Ext JS Library 1.1.1
34977  * Copyright(c) 2006-2007, Ext JS, LLC.
34978  *
34979  * Originally Released Under LGPL - original licence link has changed is not relivant.
34980  *
34981  * Fork - LGPL
34982  * <script type="text/javascript">
34983  */
34984
34985 /**
34986  * @class Roo.menu.TextItem
34987  * @extends Roo.menu.BaseItem
34988  * Adds a static text string to a menu, usually used as either a heading or group separator.
34989  * Note: old style constructor with text is still supported.
34990  * 
34991  * @constructor
34992  * Creates a new TextItem
34993  * @param {Object} cfg Configuration
34994  */
34995 Roo.menu.TextItem = function(cfg){
34996     if (typeof(cfg) == 'string') {
34997         this.text = cfg;
34998     } else {
34999         Roo.apply(this,cfg);
35000     }
35001     
35002     Roo.menu.TextItem.superclass.constructor.call(this);
35003 };
35004
35005 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35006     /**
35007      * @cfg {Boolean} text Text to show on item.
35008      */
35009     text : '',
35010     
35011     /**
35012      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35013      */
35014     hideOnClick : false,
35015     /**
35016      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35017      */
35018     itemCls : "x-menu-text",
35019
35020     // private
35021     onRender : function(){
35022         var s = document.createElement("span");
35023         s.className = this.itemCls;
35024         s.innerHTML = this.text;
35025         this.el = s;
35026         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35027     }
35028 });/*
35029  * Based on:
35030  * Ext JS Library 1.1.1
35031  * Copyright(c) 2006-2007, Ext JS, LLC.
35032  *
35033  * Originally Released Under LGPL - original licence link has changed is not relivant.
35034  *
35035  * Fork - LGPL
35036  * <script type="text/javascript">
35037  */
35038
35039 /**
35040  * @class Roo.menu.Separator
35041  * @extends Roo.menu.BaseItem
35042  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35043  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35044  * @constructor
35045  * @param {Object} config Configuration options
35046  */
35047 Roo.menu.Separator = function(config){
35048     Roo.menu.Separator.superclass.constructor.call(this, config);
35049 };
35050
35051 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35052     /**
35053      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35054      */
35055     itemCls : "x-menu-sep",
35056     /**
35057      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35058      */
35059     hideOnClick : false,
35060
35061     // private
35062     onRender : function(li){
35063         var s = document.createElement("span");
35064         s.className = this.itemCls;
35065         s.innerHTML = "&#160;";
35066         this.el = s;
35067         li.addClass("x-menu-sep-li");
35068         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35069     }
35070 });/*
35071  * Based on:
35072  * Ext JS Library 1.1.1
35073  * Copyright(c) 2006-2007, Ext JS, LLC.
35074  *
35075  * Originally Released Under LGPL - original licence link has changed is not relivant.
35076  *
35077  * Fork - LGPL
35078  * <script type="text/javascript">
35079  */
35080 /**
35081  * @class Roo.menu.Item
35082  * @extends Roo.menu.BaseItem
35083  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35084  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35085  * activation and click handling.
35086  * @constructor
35087  * Creates a new Item
35088  * @param {Object} config Configuration options
35089  */
35090 Roo.menu.Item = function(config){
35091     Roo.menu.Item.superclass.constructor.call(this, config);
35092     if(this.menu){
35093         this.menu = Roo.menu.MenuMgr.get(this.menu);
35094     }
35095 };
35096 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35097     
35098     /**
35099      * @cfg {String} text
35100      * The text to show on the menu item.
35101      */
35102     text: '',
35103      /**
35104      * @cfg {String} HTML to render in menu
35105      * The text to show on the menu item (HTML version).
35106      */
35107     html: '',
35108     /**
35109      * @cfg {String} icon
35110      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35111      */
35112     icon: undefined,
35113     /**
35114      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35115      */
35116     itemCls : "x-menu-item",
35117     /**
35118      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35119      */
35120     canActivate : true,
35121     /**
35122      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35123      */
35124     showDelay: 200,
35125     // doc'd in BaseItem
35126     hideDelay: 200,
35127
35128     // private
35129     ctype: "Roo.menu.Item",
35130     
35131     // private
35132     onRender : function(container, position){
35133         var el = document.createElement("a");
35134         el.hideFocus = true;
35135         el.unselectable = "on";
35136         el.href = this.href || "#";
35137         if(this.hrefTarget){
35138             el.target = this.hrefTarget;
35139         }
35140         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35141         
35142         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35143         
35144         el.innerHTML = String.format(
35145                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35146                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35147         this.el = el;
35148         Roo.menu.Item.superclass.onRender.call(this, container, position);
35149     },
35150
35151     /**
35152      * Sets the text to display in this menu item
35153      * @param {String} text The text to display
35154      * @param {Boolean} isHTML true to indicate text is pure html.
35155      */
35156     setText : function(text, isHTML){
35157         if (isHTML) {
35158             this.html = text;
35159         } else {
35160             this.text = text;
35161             this.html = '';
35162         }
35163         if(this.rendered){
35164             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35165      
35166             this.el.update(String.format(
35167                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35168                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35169             this.parentMenu.autoWidth();
35170         }
35171     },
35172
35173     // private
35174     handleClick : function(e){
35175         if(!this.href){ // if no link defined, stop the event automatically
35176             e.stopEvent();
35177         }
35178         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35179     },
35180
35181     // private
35182     activate : function(autoExpand){
35183         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35184             this.focus();
35185             if(autoExpand){
35186                 this.expandMenu();
35187             }
35188         }
35189         return true;
35190     },
35191
35192     // private
35193     shouldDeactivate : function(e){
35194         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35195             if(this.menu && this.menu.isVisible()){
35196                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35197             }
35198             return true;
35199         }
35200         return false;
35201     },
35202
35203     // private
35204     deactivate : function(){
35205         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35206         this.hideMenu();
35207     },
35208
35209     // private
35210     expandMenu : function(autoActivate){
35211         if(!this.disabled && this.menu){
35212             clearTimeout(this.hideTimer);
35213             delete this.hideTimer;
35214             if(!this.menu.isVisible() && !this.showTimer){
35215                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35216             }else if (this.menu.isVisible() && autoActivate){
35217                 this.menu.tryActivate(0, 1);
35218             }
35219         }
35220     },
35221
35222     // private
35223     deferExpand : function(autoActivate){
35224         delete this.showTimer;
35225         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35226         if(autoActivate){
35227             this.menu.tryActivate(0, 1);
35228         }
35229     },
35230
35231     // private
35232     hideMenu : function(){
35233         clearTimeout(this.showTimer);
35234         delete this.showTimer;
35235         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35236             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35237         }
35238     },
35239
35240     // private
35241     deferHide : function(){
35242         delete this.hideTimer;
35243         this.menu.hide();
35244     }
35245 });/*
35246  * Based on:
35247  * Ext JS Library 1.1.1
35248  * Copyright(c) 2006-2007, Ext JS, LLC.
35249  *
35250  * Originally Released Under LGPL - original licence link has changed is not relivant.
35251  *
35252  * Fork - LGPL
35253  * <script type="text/javascript">
35254  */
35255  
35256 /**
35257  * @class Roo.menu.CheckItem
35258  * @extends Roo.menu.Item
35259  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35260  * @constructor
35261  * Creates a new CheckItem
35262  * @param {Object} config Configuration options
35263  */
35264 Roo.menu.CheckItem = function(config){
35265     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35266     this.addEvents({
35267         /**
35268          * @event beforecheckchange
35269          * Fires before the checked value is set, providing an opportunity to cancel if needed
35270          * @param {Roo.menu.CheckItem} this
35271          * @param {Boolean} checked The new checked value that will be set
35272          */
35273         "beforecheckchange" : true,
35274         /**
35275          * @event checkchange
35276          * Fires after the checked value has been set
35277          * @param {Roo.menu.CheckItem} this
35278          * @param {Boolean} checked The checked value that was set
35279          */
35280         "checkchange" : true
35281     });
35282     if(this.checkHandler){
35283         this.on('checkchange', this.checkHandler, this.scope);
35284     }
35285 };
35286 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35287     /**
35288      * @cfg {String} group
35289      * All check items with the same group name will automatically be grouped into a single-select
35290      * radio button group (defaults to '')
35291      */
35292     /**
35293      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35294      */
35295     itemCls : "x-menu-item x-menu-check-item",
35296     /**
35297      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35298      */
35299     groupClass : "x-menu-group-item",
35300
35301     /**
35302      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35303      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35304      * initialized with checked = true will be rendered as checked.
35305      */
35306     checked: false,
35307
35308     // private
35309     ctype: "Roo.menu.CheckItem",
35310
35311     // private
35312     onRender : function(c){
35313         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35314         if(this.group){
35315             this.el.addClass(this.groupClass);
35316         }
35317         Roo.menu.MenuMgr.registerCheckable(this);
35318         if(this.checked){
35319             this.checked = false;
35320             this.setChecked(true, true);
35321         }
35322     },
35323
35324     // private
35325     destroy : function(){
35326         if(this.rendered){
35327             Roo.menu.MenuMgr.unregisterCheckable(this);
35328         }
35329         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35330     },
35331
35332     /**
35333      * Set the checked state of this item
35334      * @param {Boolean} checked The new checked value
35335      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35336      */
35337     setChecked : function(state, suppressEvent){
35338         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35339             if(this.container){
35340                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35341             }
35342             this.checked = state;
35343             if(suppressEvent !== true){
35344                 this.fireEvent("checkchange", this, state);
35345             }
35346         }
35347     },
35348
35349     // private
35350     handleClick : function(e){
35351        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35352            this.setChecked(!this.checked);
35353        }
35354        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35355     }
35356 });/*
35357  * Based on:
35358  * Ext JS Library 1.1.1
35359  * Copyright(c) 2006-2007, Ext JS, LLC.
35360  *
35361  * Originally Released Under LGPL - original licence link has changed is not relivant.
35362  *
35363  * Fork - LGPL
35364  * <script type="text/javascript">
35365  */
35366  
35367 /**
35368  * @class Roo.menu.DateItem
35369  * @extends Roo.menu.Adapter
35370  * A menu item that wraps the {@link Roo.DatPicker} component.
35371  * @constructor
35372  * Creates a new DateItem
35373  * @param {Object} config Configuration options
35374  */
35375 Roo.menu.DateItem = function(config){
35376     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35377     /** The Roo.DatePicker object @type Roo.DatePicker */
35378     this.picker = this.component;
35379     this.addEvents({select: true});
35380     
35381     this.picker.on("render", function(picker){
35382         picker.getEl().swallowEvent("click");
35383         picker.container.addClass("x-menu-date-item");
35384     });
35385
35386     this.picker.on("select", this.onSelect, this);
35387 };
35388
35389 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35390     // private
35391     onSelect : function(picker, date){
35392         this.fireEvent("select", this, date, picker);
35393         Roo.menu.DateItem.superclass.handleClick.call(this);
35394     }
35395 });/*
35396  * Based on:
35397  * Ext JS Library 1.1.1
35398  * Copyright(c) 2006-2007, Ext JS, LLC.
35399  *
35400  * Originally Released Under LGPL - original licence link has changed is not relivant.
35401  *
35402  * Fork - LGPL
35403  * <script type="text/javascript">
35404  */
35405  
35406 /**
35407  * @class Roo.menu.ColorItem
35408  * @extends Roo.menu.Adapter
35409  * A menu item that wraps the {@link Roo.ColorPalette} component.
35410  * @constructor
35411  * Creates a new ColorItem
35412  * @param {Object} config Configuration options
35413  */
35414 Roo.menu.ColorItem = function(config){
35415     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35416     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35417     this.palette = this.component;
35418     this.relayEvents(this.palette, ["select"]);
35419     if(this.selectHandler){
35420         this.on('select', this.selectHandler, this.scope);
35421     }
35422 };
35423 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35424  * Based on:
35425  * Ext JS Library 1.1.1
35426  * Copyright(c) 2006-2007, Ext JS, LLC.
35427  *
35428  * Originally Released Under LGPL - original licence link has changed is not relivant.
35429  *
35430  * Fork - LGPL
35431  * <script type="text/javascript">
35432  */
35433  
35434
35435 /**
35436  * @class Roo.menu.DateMenu
35437  * @extends Roo.menu.Menu
35438  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35439  * @constructor
35440  * Creates a new DateMenu
35441  * @param {Object} config Configuration options
35442  */
35443 Roo.menu.DateMenu = function(config){
35444     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35445     this.plain = true;
35446     var di = new Roo.menu.DateItem(config);
35447     this.add(di);
35448     /**
35449      * The {@link Roo.DatePicker} instance for this DateMenu
35450      * @type DatePicker
35451      */
35452     this.picker = di.picker;
35453     /**
35454      * @event select
35455      * @param {DatePicker} picker
35456      * @param {Date} date
35457      */
35458     this.relayEvents(di, ["select"]);
35459
35460     this.on('beforeshow', function(){
35461         if(this.picker){
35462             this.picker.hideMonthPicker(true);
35463         }
35464     }, this);
35465 };
35466 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35467     cls:'x-date-menu'
35468 });/*
35469  * Based on:
35470  * Ext JS Library 1.1.1
35471  * Copyright(c) 2006-2007, Ext JS, LLC.
35472  *
35473  * Originally Released Under LGPL - original licence link has changed is not relivant.
35474  *
35475  * Fork - LGPL
35476  * <script type="text/javascript">
35477  */
35478  
35479
35480 /**
35481  * @class Roo.menu.ColorMenu
35482  * @extends Roo.menu.Menu
35483  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35484  * @constructor
35485  * Creates a new ColorMenu
35486  * @param {Object} config Configuration options
35487  */
35488 Roo.menu.ColorMenu = function(config){
35489     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35490     this.plain = true;
35491     var ci = new Roo.menu.ColorItem(config);
35492     this.add(ci);
35493     /**
35494      * The {@link Roo.ColorPalette} instance for this ColorMenu
35495      * @type ColorPalette
35496      */
35497     this.palette = ci.palette;
35498     /**
35499      * @event select
35500      * @param {ColorPalette} palette
35501      * @param {String} color
35502      */
35503     this.relayEvents(ci, ["select"]);
35504 };
35505 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35506  * Based on:
35507  * Ext JS Library 1.1.1
35508  * Copyright(c) 2006-2007, Ext JS, LLC.
35509  *
35510  * Originally Released Under LGPL - original licence link has changed is not relivant.
35511  *
35512  * Fork - LGPL
35513  * <script type="text/javascript">
35514  */
35515  
35516 /**
35517  * @class Roo.form.Field
35518  * @extends Roo.BoxComponent
35519  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35520  * @constructor
35521  * Creates a new Field
35522  * @param {Object} config Configuration options
35523  */
35524 Roo.form.Field = function(config){
35525     Roo.form.Field.superclass.constructor.call(this, config);
35526 };
35527
35528 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35529     /**
35530      * @cfg {String} fieldLabel Label to use when rendering a form.
35531      */
35532        /**
35533      * @cfg {String} qtip Mouse over tip
35534      */
35535      
35536     /**
35537      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35538      */
35539     invalidClass : "x-form-invalid",
35540     /**
35541      * @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")
35542      */
35543     invalidText : "The value in this field is invalid",
35544     /**
35545      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35546      */
35547     focusClass : "x-form-focus",
35548     /**
35549      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35550       automatic validation (defaults to "keyup").
35551      */
35552     validationEvent : "keyup",
35553     /**
35554      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35555      */
35556     validateOnBlur : true,
35557     /**
35558      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35559      */
35560     validationDelay : 250,
35561     /**
35562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35563      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35564      */
35565     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35566     /**
35567      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35568      */
35569     fieldClass : "x-form-field",
35570     /**
35571      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35572      *<pre>
35573 Value         Description
35574 -----------   ----------------------------------------------------------------------
35575 qtip          Display a quick tip when the user hovers over the field
35576 title         Display a default browser title attribute popup
35577 under         Add a block div beneath the field containing the error text
35578 side          Add an error icon to the right of the field with a popup on hover
35579 [element id]  Add the error text directly to the innerHTML of the specified element
35580 </pre>
35581      */
35582     msgTarget : 'qtip',
35583     /**
35584      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35585      */
35586     msgFx : 'normal',
35587
35588     /**
35589      * @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.
35590      */
35591     readOnly : false,
35592
35593     /**
35594      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35595      */
35596     disabled : false,
35597
35598     /**
35599      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35600      */
35601     inputType : undefined,
35602     
35603     /**
35604      * @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).
35605          */
35606         tabIndex : undefined,
35607         
35608     // private
35609     isFormField : true,
35610
35611     // private
35612     hasFocus : false,
35613     /**
35614      * @property {Roo.Element} fieldEl
35615      * Element Containing the rendered Field (with label etc.)
35616      */
35617     /**
35618      * @cfg {Mixed} value A value to initialize this field with.
35619      */
35620     value : undefined,
35621
35622     /**
35623      * @cfg {String} name The field's HTML name attribute.
35624      */
35625     /**
35626      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35627      */
35628
35629         // private ??
35630         initComponent : function(){
35631         Roo.form.Field.superclass.initComponent.call(this);
35632         this.addEvents({
35633             /**
35634              * @event focus
35635              * Fires when this field receives input focus.
35636              * @param {Roo.form.Field} this
35637              */
35638             focus : true,
35639             /**
35640              * @event blur
35641              * Fires when this field loses input focus.
35642              * @param {Roo.form.Field} this
35643              */
35644             blur : true,
35645             /**
35646              * @event specialkey
35647              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35648              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35649              * @param {Roo.form.Field} this
35650              * @param {Roo.EventObject} e The event object
35651              */
35652             specialkey : true,
35653             /**
35654              * @event change
35655              * Fires just before the field blurs if the field value has changed.
35656              * @param {Roo.form.Field} this
35657              * @param {Mixed} newValue The new value
35658              * @param {Mixed} oldValue The original value
35659              */
35660             change : true,
35661             /**
35662              * @event invalid
35663              * Fires after the field has been marked as invalid.
35664              * @param {Roo.form.Field} this
35665              * @param {String} msg The validation message
35666              */
35667             invalid : true,
35668             /**
35669              * @event valid
35670              * Fires after the field has been validated with no errors.
35671              * @param {Roo.form.Field} this
35672              */
35673             valid : true,
35674              /**
35675              * @event keyup
35676              * Fires after the key up
35677              * @param {Roo.form.Field} this
35678              * @param {Roo.EventObject}  e The event Object
35679              */
35680             keyup : true
35681         });
35682     },
35683
35684     /**
35685      * Returns the name attribute of the field if available
35686      * @return {String} name The field name
35687      */
35688     getName: function(){
35689          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35690     },
35691
35692     // private
35693     onRender : function(ct, position){
35694         Roo.form.Field.superclass.onRender.call(this, ct, position);
35695         if(!this.el){
35696             var cfg = this.getAutoCreate();
35697             if(!cfg.name){
35698                 cfg.name = this.name || this.id;
35699             }
35700             if(this.inputType){
35701                 cfg.type = this.inputType;
35702             }
35703             this.el = ct.createChild(cfg, position);
35704         }
35705         var type = this.el.dom.type;
35706         if(type){
35707             if(type == 'password'){
35708                 type = 'text';
35709             }
35710             this.el.addClass('x-form-'+type);
35711         }
35712         if(this.readOnly){
35713             this.el.dom.readOnly = true;
35714         }
35715         if(this.tabIndex !== undefined){
35716             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35717         }
35718
35719         this.el.addClass([this.fieldClass, this.cls]);
35720         this.initValue();
35721     },
35722
35723     /**
35724      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35725      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35726      * @return {Roo.form.Field} this
35727      */
35728     applyTo : function(target){
35729         this.allowDomMove = false;
35730         this.el = Roo.get(target);
35731         this.render(this.el.dom.parentNode);
35732         return this;
35733     },
35734
35735     // private
35736     initValue : function(){
35737         if(this.value !== undefined){
35738             this.setValue(this.value);
35739         }else if(this.el.dom.value.length > 0){
35740             this.setValue(this.el.dom.value);
35741         }
35742     },
35743
35744     /**
35745      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35746      */
35747     isDirty : function() {
35748         if(this.disabled) {
35749             return false;
35750         }
35751         return String(this.getValue()) !== String(this.originalValue);
35752     },
35753
35754     // private
35755     afterRender : function(){
35756         Roo.form.Field.superclass.afterRender.call(this);
35757         this.initEvents();
35758     },
35759
35760     // private
35761     fireKey : function(e){
35762         //Roo.log('field ' + e.getKey());
35763         if(e.isNavKeyPress()){
35764             this.fireEvent("specialkey", this, e);
35765         }
35766     },
35767
35768     /**
35769      * Resets the current field value to the originally loaded value and clears any validation messages
35770      */
35771     reset : function(){
35772         this.setValue(this.originalValue);
35773         this.clearInvalid();
35774     },
35775
35776     // private
35777     initEvents : function(){
35778         // safari killled keypress - so keydown is now used..
35779         this.el.on("keydown" , this.fireKey,  this);
35780         this.el.on("focus", this.onFocus,  this);
35781         this.el.on("blur", this.onBlur,  this);
35782         this.el.relayEvent('keyup', this);
35783
35784         // reference to original value for reset
35785         this.originalValue = this.getValue();
35786     },
35787
35788     // private
35789     onFocus : function(){
35790         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35791             this.el.addClass(this.focusClass);
35792         }
35793         if(!this.hasFocus){
35794             this.hasFocus = true;
35795             this.startValue = this.getValue();
35796             this.fireEvent("focus", this);
35797         }
35798     },
35799
35800     beforeBlur : Roo.emptyFn,
35801
35802     // private
35803     onBlur : function(){
35804         this.beforeBlur();
35805         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35806             this.el.removeClass(this.focusClass);
35807         }
35808         this.hasFocus = false;
35809         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35810             this.validate();
35811         }
35812         var v = this.getValue();
35813         if(String(v) !== String(this.startValue)){
35814             this.fireEvent('change', this, v, this.startValue);
35815         }
35816         this.fireEvent("blur", this);
35817     },
35818
35819     /**
35820      * Returns whether or not the field value is currently valid
35821      * @param {Boolean} preventMark True to disable marking the field invalid
35822      * @return {Boolean} True if the value is valid, else false
35823      */
35824     isValid : function(preventMark){
35825         if(this.disabled){
35826             return true;
35827         }
35828         var restore = this.preventMark;
35829         this.preventMark = preventMark === true;
35830         var v = this.validateValue(this.processValue(this.getRawValue()));
35831         this.preventMark = restore;
35832         return v;
35833     },
35834
35835     /**
35836      * Validates the field value
35837      * @return {Boolean} True if the value is valid, else false
35838      */
35839     validate : function(){
35840         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35841             this.clearInvalid();
35842             return true;
35843         }
35844         return false;
35845     },
35846
35847     processValue : function(value){
35848         return value;
35849     },
35850
35851     // private
35852     // Subclasses should provide the validation implementation by overriding this
35853     validateValue : function(value){
35854         return true;
35855     },
35856
35857     /**
35858      * Mark this field as invalid
35859      * @param {String} msg The validation message
35860      */
35861     markInvalid : function(msg){
35862         if(!this.rendered || this.preventMark){ // not rendered
35863             return;
35864         }
35865         this.el.addClass(this.invalidClass);
35866         msg = msg || this.invalidText;
35867         switch(this.msgTarget){
35868             case 'qtip':
35869                 this.el.dom.qtip = msg;
35870                 this.el.dom.qclass = 'x-form-invalid-tip';
35871                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35872                     Roo.QuickTips.enable();
35873                 }
35874                 break;
35875             case 'title':
35876                 this.el.dom.title = msg;
35877                 break;
35878             case 'under':
35879                 if(!this.errorEl){
35880                     var elp = this.el.findParent('.x-form-element', 5, true);
35881                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35882                     this.errorEl.setWidth(elp.getWidth(true)-20);
35883                 }
35884                 this.errorEl.update(msg);
35885                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35886                 break;
35887             case 'side':
35888                 if(!this.errorIcon){
35889                     var elp = this.el.findParent('.x-form-element', 5, true);
35890                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35891                 }
35892                 this.alignErrorIcon();
35893                 this.errorIcon.dom.qtip = msg;
35894                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35895                 this.errorIcon.show();
35896                 this.on('resize', this.alignErrorIcon, this);
35897                 break;
35898             default:
35899                 var t = Roo.getDom(this.msgTarget);
35900                 t.innerHTML = msg;
35901                 t.style.display = this.msgDisplay;
35902                 break;
35903         }
35904         this.fireEvent('invalid', this, msg);
35905     },
35906
35907     // private
35908     alignErrorIcon : function(){
35909         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35910     },
35911
35912     /**
35913      * Clear any invalid styles/messages for this field
35914      */
35915     clearInvalid : function(){
35916         if(!this.rendered || this.preventMark){ // not rendered
35917             return;
35918         }
35919         this.el.removeClass(this.invalidClass);
35920         switch(this.msgTarget){
35921             case 'qtip':
35922                 this.el.dom.qtip = '';
35923                 break;
35924             case 'title':
35925                 this.el.dom.title = '';
35926                 break;
35927             case 'under':
35928                 if(this.errorEl){
35929                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35930                 }
35931                 break;
35932             case 'side':
35933                 if(this.errorIcon){
35934                     this.errorIcon.dom.qtip = '';
35935                     this.errorIcon.hide();
35936                     this.un('resize', this.alignErrorIcon, this);
35937                 }
35938                 break;
35939             default:
35940                 var t = Roo.getDom(this.msgTarget);
35941                 t.innerHTML = '';
35942                 t.style.display = 'none';
35943                 break;
35944         }
35945         this.fireEvent('valid', this);
35946     },
35947
35948     /**
35949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35950      * @return {Mixed} value The field value
35951      */
35952     getRawValue : function(){
35953         var v = this.el.getValue();
35954         if(v === this.emptyText){
35955             v = '';
35956         }
35957         return v;
35958     },
35959
35960     /**
35961      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35962      * @return {Mixed} value The field value
35963      */
35964     getValue : function(){
35965         var v = this.el.getValue();
35966         if(v === this.emptyText || v === undefined){
35967             v = '';
35968         }
35969         return v;
35970     },
35971
35972     /**
35973      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35974      * @param {Mixed} value The value to set
35975      */
35976     setRawValue : function(v){
35977         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35978     },
35979
35980     /**
35981      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35982      * @param {Mixed} value The value to set
35983      */
35984     setValue : function(v){
35985         this.value = v;
35986         if(this.rendered){
35987             this.el.dom.value = (v === null || v === undefined ? '' : v);
35988              this.validate();
35989         }
35990     },
35991
35992     adjustSize : function(w, h){
35993         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35994         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35995         return s;
35996     },
35997
35998     adjustWidth : function(tag, w){
35999         tag = tag.toLowerCase();
36000         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36001             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36002                 if(tag == 'input'){
36003                     return w + 2;
36004                 }
36005                 if(tag = 'textarea'){
36006                     return w-2;
36007                 }
36008             }else if(Roo.isOpera){
36009                 if(tag == 'input'){
36010                     return w + 2;
36011                 }
36012                 if(tag = 'textarea'){
36013                     return w-2;
36014                 }
36015             }
36016         }
36017         return w;
36018     }
36019 });
36020
36021
36022 // anything other than normal should be considered experimental
36023 Roo.form.Field.msgFx = {
36024     normal : {
36025         show: function(msgEl, f){
36026             msgEl.setDisplayed('block');
36027         },
36028
36029         hide : function(msgEl, f){
36030             msgEl.setDisplayed(false).update('');
36031         }
36032     },
36033
36034     slide : {
36035         show: function(msgEl, f){
36036             msgEl.slideIn('t', {stopFx:true});
36037         },
36038
36039         hide : function(msgEl, f){
36040             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36041         }
36042     },
36043
36044     slideRight : {
36045         show: function(msgEl, f){
36046             msgEl.fixDisplay();
36047             msgEl.alignTo(f.el, 'tl-tr');
36048             msgEl.slideIn('l', {stopFx:true});
36049         },
36050
36051         hide : function(msgEl, f){
36052             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36053         }
36054     }
36055 };/*
36056  * Based on:
36057  * Ext JS Library 1.1.1
36058  * Copyright(c) 2006-2007, Ext JS, LLC.
36059  *
36060  * Originally Released Under LGPL - original licence link has changed is not relivant.
36061  *
36062  * Fork - LGPL
36063  * <script type="text/javascript">
36064  */
36065  
36066
36067 /**
36068  * @class Roo.form.TextField
36069  * @extends Roo.form.Field
36070  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36071  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36072  * @constructor
36073  * Creates a new TextField
36074  * @param {Object} config Configuration options
36075  */
36076 Roo.form.TextField = function(config){
36077     Roo.form.TextField.superclass.constructor.call(this, config);
36078     this.addEvents({
36079         /**
36080          * @event autosize
36081          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36082          * according to the default logic, but this event provides a hook for the developer to apply additional
36083          * logic at runtime to resize the field if needed.
36084              * @param {Roo.form.Field} this This text field
36085              * @param {Number} width The new field width
36086              */
36087         autosize : true
36088     });
36089 };
36090
36091 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36092     /**
36093      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36094      */
36095     grow : false,
36096     /**
36097      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36098      */
36099     growMin : 30,
36100     /**
36101      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36102      */
36103     growMax : 800,
36104     /**
36105      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36106      */
36107     vtype : null,
36108     /**
36109      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36110      */
36111     maskRe : null,
36112     /**
36113      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36114      */
36115     disableKeyFilter : false,
36116     /**
36117      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36118      */
36119     allowBlank : true,
36120     /**
36121      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36122      */
36123     minLength : 0,
36124     /**
36125      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36126      */
36127     maxLength : Number.MAX_VALUE,
36128     /**
36129      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36130      */
36131     minLengthText : "The minimum length for this field is {0}",
36132     /**
36133      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36134      */
36135     maxLengthText : "The maximum length for this field is {0}",
36136     /**
36137      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36138      */
36139     selectOnFocus : false,
36140     /**
36141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36142      */
36143     blankText : "This field is required",
36144     /**
36145      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36146      * If available, this function will be called only after the basic validators all return true, and will be passed the
36147      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36148      */
36149     validator : null,
36150     /**
36151      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36152      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36153      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36154      */
36155     regex : null,
36156     /**
36157      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36158      */
36159     regexText : "",
36160     /**
36161      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36162      */
36163     emptyText : null,
36164     /**
36165      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36166      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36167      */
36168     emptyClass : 'x-form-empty-field',
36169
36170     // private
36171     initEvents : function(){
36172         Roo.form.TextField.superclass.initEvents.call(this);
36173         if(this.validationEvent == 'keyup'){
36174             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36175             this.el.on('keyup', this.filterValidation, this);
36176         }
36177         else if(this.validationEvent !== false){
36178             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36179         }
36180         if(this.selectOnFocus || this.emptyText){
36181             this.on("focus", this.preFocus, this);
36182             if(this.emptyText){
36183                 this.on('blur', this.postBlur, this);
36184                 this.applyEmptyText();
36185             }
36186         }
36187         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36188             this.el.on("keypress", this.filterKeys, this);
36189         }
36190         if(this.grow){
36191             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36192             this.el.on("click", this.autoSize,  this);
36193         }
36194     },
36195
36196     processValue : function(value){
36197         if(this.stripCharsRe){
36198             var newValue = value.replace(this.stripCharsRe, '');
36199             if(newValue !== value){
36200                 this.setRawValue(newValue);
36201                 return newValue;
36202             }
36203         }
36204         return value;
36205     },
36206
36207     filterValidation : function(e){
36208         if(!e.isNavKeyPress()){
36209             this.validationTask.delay(this.validationDelay);
36210         }
36211     },
36212
36213     // private
36214     onKeyUp : function(e){
36215         if(!e.isNavKeyPress()){
36216             this.autoSize();
36217         }
36218     },
36219
36220     /**
36221      * Resets the current field value to the originally-loaded value and clears any validation messages.
36222      * Also adds emptyText and emptyClass if the original value was blank.
36223      */
36224     reset : function(){
36225         Roo.form.TextField.superclass.reset.call(this);
36226         this.applyEmptyText();
36227     },
36228
36229     applyEmptyText : function(){
36230         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36231             this.setRawValue(this.emptyText);
36232             this.el.addClass(this.emptyClass);
36233         }
36234     },
36235
36236     // private
36237     preFocus : function(){
36238         if(this.emptyText){
36239             if(this.el.dom.value == this.emptyText){
36240                 this.setRawValue('');
36241             }
36242             this.el.removeClass(this.emptyClass);
36243         }
36244         if(this.selectOnFocus){
36245             this.el.dom.select();
36246         }
36247     },
36248
36249     // private
36250     postBlur : function(){
36251         this.applyEmptyText();
36252     },
36253
36254     // private
36255     filterKeys : function(e){
36256         var k = e.getKey();
36257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36258             return;
36259         }
36260         var c = e.getCharCode(), cc = String.fromCharCode(c);
36261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36262             return;
36263         }
36264         if(!this.maskRe.test(cc)){
36265             e.stopEvent();
36266         }
36267     },
36268
36269     setValue : function(v){
36270         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36271             this.el.removeClass(this.emptyClass);
36272         }
36273         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36274         this.applyEmptyText();
36275         this.autoSize();
36276     },
36277
36278     /**
36279      * Validates a value according to the field's validation rules and marks the field as invalid
36280      * if the validation fails
36281      * @param {Mixed} value The value to validate
36282      * @return {Boolean} True if the value is valid, else false
36283      */
36284     validateValue : function(value){
36285         if(value.length < 1 || value === this.emptyText){ // if it's blank
36286              if(this.allowBlank){
36287                 this.clearInvalid();
36288                 return true;
36289              }else{
36290                 this.markInvalid(this.blankText);
36291                 return false;
36292              }
36293         }
36294         if(value.length < this.minLength){
36295             this.markInvalid(String.format(this.minLengthText, this.minLength));
36296             return false;
36297         }
36298         if(value.length > this.maxLength){
36299             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36300             return false;
36301         }
36302         if(this.vtype){
36303             var vt = Roo.form.VTypes;
36304             if(!vt[this.vtype](value, this)){
36305                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36306                 return false;
36307             }
36308         }
36309         if(typeof this.validator == "function"){
36310             var msg = this.validator(value);
36311             if(msg !== true){
36312                 this.markInvalid(msg);
36313                 return false;
36314             }
36315         }
36316         if(this.regex && !this.regex.test(value)){
36317             this.markInvalid(this.regexText);
36318             return false;
36319         }
36320         return true;
36321     },
36322
36323     /**
36324      * Selects text in this field
36325      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36326      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36327      */
36328     selectText : function(start, end){
36329         var v = this.getRawValue();
36330         if(v.length > 0){
36331             start = start === undefined ? 0 : start;
36332             end = end === undefined ? v.length : end;
36333             var d = this.el.dom;
36334             if(d.setSelectionRange){
36335                 d.setSelectionRange(start, end);
36336             }else if(d.createTextRange){
36337                 var range = d.createTextRange();
36338                 range.moveStart("character", start);
36339                 range.moveEnd("character", v.length-end);
36340                 range.select();
36341             }
36342         }
36343     },
36344
36345     /**
36346      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36347      * This only takes effect if grow = true, and fires the autosize event.
36348      */
36349     autoSize : function(){
36350         if(!this.grow || !this.rendered){
36351             return;
36352         }
36353         if(!this.metrics){
36354             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36355         }
36356         var el = this.el;
36357         var v = el.dom.value;
36358         var d = document.createElement('div');
36359         d.appendChild(document.createTextNode(v));
36360         v = d.innerHTML;
36361         d = null;
36362         v += "&#160;";
36363         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36364         this.el.setWidth(w);
36365         this.fireEvent("autosize", this, w);
36366     }
36367 });/*
36368  * Based on:
36369  * Ext JS Library 1.1.1
36370  * Copyright(c) 2006-2007, Ext JS, LLC.
36371  *
36372  * Originally Released Under LGPL - original licence link has changed is not relivant.
36373  *
36374  * Fork - LGPL
36375  * <script type="text/javascript">
36376  */
36377  
36378 /**
36379  * @class Roo.form.Hidden
36380  * @extends Roo.form.TextField
36381  * Simple Hidden element used on forms 
36382  * 
36383  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36384  * 
36385  * @constructor
36386  * Creates a new Hidden form element.
36387  * @param {Object} config Configuration options
36388  */
36389
36390
36391
36392 // easy hidden field...
36393 Roo.form.Hidden = function(config){
36394     Roo.form.Hidden.superclass.constructor.call(this, config);
36395 };
36396   
36397 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36398     fieldLabel:      '',
36399     inputType:      'hidden',
36400     width:          50,
36401     allowBlank:     true,
36402     labelSeparator: '',
36403     hidden:         true,
36404     itemCls :       'x-form-item-display-none'
36405
36406
36407 });
36408
36409
36410 /*
36411  * Based on:
36412  * Ext JS Library 1.1.1
36413  * Copyright(c) 2006-2007, Ext JS, LLC.
36414  *
36415  * Originally Released Under LGPL - original licence link has changed is not relivant.
36416  *
36417  * Fork - LGPL
36418  * <script type="text/javascript">
36419  */
36420  
36421 /**
36422  * @class Roo.form.TriggerField
36423  * @extends Roo.form.TextField
36424  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36425  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36426  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36427  * for which you can provide a custom implementation.  For example:
36428  * <pre><code>
36429 var trigger = new Roo.form.TriggerField();
36430 trigger.onTriggerClick = myTriggerFn;
36431 trigger.applyTo('my-field');
36432 </code></pre>
36433  *
36434  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36435  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36436  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36437  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36438  * @constructor
36439  * Create a new TriggerField.
36440  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36441  * to the base TextField)
36442  */
36443 Roo.form.TriggerField = function(config){
36444     this.mimicing = false;
36445     Roo.form.TriggerField.superclass.constructor.call(this, config);
36446 };
36447
36448 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36449     /**
36450      * @cfg {String} triggerClass A CSS class to apply to the trigger
36451      */
36452     /**
36453      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36454      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36455      */
36456     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36457     /**
36458      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36459      */
36460     hideTrigger:false,
36461
36462     /** @cfg {Boolean} grow @hide */
36463     /** @cfg {Number} growMin @hide */
36464     /** @cfg {Number} growMax @hide */
36465
36466     /**
36467      * @hide 
36468      * @method
36469      */
36470     autoSize: Roo.emptyFn,
36471     // private
36472     monitorTab : true,
36473     // private
36474     deferHeight : true,
36475
36476     
36477     actionMode : 'wrap',
36478     // private
36479     onResize : function(w, h){
36480         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36481         if(typeof w == 'number'){
36482             var x = w - this.trigger.getWidth();
36483             this.el.setWidth(this.adjustWidth('input', x));
36484             this.trigger.setStyle('left', x+'px');
36485         }
36486     },
36487
36488     // private
36489     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36490
36491     // private
36492     getResizeEl : function(){
36493         return this.wrap;
36494     },
36495
36496     // private
36497     getPositionEl : function(){
36498         return this.wrap;
36499     },
36500
36501     // private
36502     alignErrorIcon : function(){
36503         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36504     },
36505
36506     // private
36507     onRender : function(ct, position){
36508         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36509         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36510         this.trigger = this.wrap.createChild(this.triggerConfig ||
36511                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36512         if(this.hideTrigger){
36513             this.trigger.setDisplayed(false);
36514         }
36515         this.initTrigger();
36516         if(!this.width){
36517             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36518         }
36519     },
36520
36521     // private
36522     initTrigger : function(){
36523         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36524         this.trigger.addClassOnOver('x-form-trigger-over');
36525         this.trigger.addClassOnClick('x-form-trigger-click');
36526     },
36527
36528     // private
36529     onDestroy : function(){
36530         if(this.trigger){
36531             this.trigger.removeAllListeners();
36532             this.trigger.remove();
36533         }
36534         if(this.wrap){
36535             this.wrap.remove();
36536         }
36537         Roo.form.TriggerField.superclass.onDestroy.call(this);
36538     },
36539
36540     // private
36541     onFocus : function(){
36542         Roo.form.TriggerField.superclass.onFocus.call(this);
36543         if(!this.mimicing){
36544             this.wrap.addClass('x-trigger-wrap-focus');
36545             this.mimicing = true;
36546             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36547             if(this.monitorTab){
36548                 this.el.on("keydown", this.checkTab, this);
36549             }
36550         }
36551     },
36552
36553     // private
36554     checkTab : function(e){
36555         if(e.getKey() == e.TAB){
36556             this.triggerBlur();
36557         }
36558     },
36559
36560     // private
36561     onBlur : function(){
36562         // do nothing
36563     },
36564
36565     // private
36566     mimicBlur : function(e, t){
36567         if(!this.wrap.contains(t) && this.validateBlur()){
36568             this.triggerBlur();
36569         }
36570     },
36571
36572     // private
36573     triggerBlur : function(){
36574         this.mimicing = false;
36575         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36576         if(this.monitorTab){
36577             this.el.un("keydown", this.checkTab, this);
36578         }
36579         this.wrap.removeClass('x-trigger-wrap-focus');
36580         Roo.form.TriggerField.superclass.onBlur.call(this);
36581     },
36582
36583     // private
36584     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36585     validateBlur : function(e, t){
36586         return true;
36587     },
36588
36589     // private
36590     onDisable : function(){
36591         Roo.form.TriggerField.superclass.onDisable.call(this);
36592         if(this.wrap){
36593             this.wrap.addClass('x-item-disabled');
36594         }
36595     },
36596
36597     // private
36598     onEnable : function(){
36599         Roo.form.TriggerField.superclass.onEnable.call(this);
36600         if(this.wrap){
36601             this.wrap.removeClass('x-item-disabled');
36602         }
36603     },
36604
36605     // private
36606     onShow : function(){
36607         var ae = this.getActionEl();
36608         
36609         if(ae){
36610             ae.dom.style.display = '';
36611             ae.dom.style.visibility = 'visible';
36612         }
36613     },
36614
36615     // private
36616     
36617     onHide : function(){
36618         var ae = this.getActionEl();
36619         ae.dom.style.display = 'none';
36620     },
36621
36622     /**
36623      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36624      * by an implementing function.
36625      * @method
36626      * @param {EventObject} e
36627      */
36628     onTriggerClick : Roo.emptyFn
36629 });
36630
36631 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36632 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36633 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36634 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36635     initComponent : function(){
36636         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36637
36638         this.triggerConfig = {
36639             tag:'span', cls:'x-form-twin-triggers', cn:[
36640             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36641             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36642         ]};
36643     },
36644
36645     getTrigger : function(index){
36646         return this.triggers[index];
36647     },
36648
36649     initTrigger : function(){
36650         var ts = this.trigger.select('.x-form-trigger', true);
36651         this.wrap.setStyle('overflow', 'hidden');
36652         var triggerField = this;
36653         ts.each(function(t, all, index){
36654             t.hide = function(){
36655                 var w = triggerField.wrap.getWidth();
36656                 this.dom.style.display = 'none';
36657                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36658             };
36659             t.show = function(){
36660                 var w = triggerField.wrap.getWidth();
36661                 this.dom.style.display = '';
36662                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36663             };
36664             var triggerIndex = 'Trigger'+(index+1);
36665
36666             if(this['hide'+triggerIndex]){
36667                 t.dom.style.display = 'none';
36668             }
36669             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36670             t.addClassOnOver('x-form-trigger-over');
36671             t.addClassOnClick('x-form-trigger-click');
36672         }, this);
36673         this.triggers = ts.elements;
36674     },
36675
36676     onTrigger1Click : Roo.emptyFn,
36677     onTrigger2Click : Roo.emptyFn
36678 });/*
36679  * Based on:
36680  * Ext JS Library 1.1.1
36681  * Copyright(c) 2006-2007, Ext JS, LLC.
36682  *
36683  * Originally Released Under LGPL - original licence link has changed is not relivant.
36684  *
36685  * Fork - LGPL
36686  * <script type="text/javascript">
36687  */
36688  
36689 /**
36690  * @class Roo.form.TextArea
36691  * @extends Roo.form.TextField
36692  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36693  * support for auto-sizing.
36694  * @constructor
36695  * Creates a new TextArea
36696  * @param {Object} config Configuration options
36697  */
36698 Roo.form.TextArea = function(config){
36699     Roo.form.TextArea.superclass.constructor.call(this, config);
36700     // these are provided exchanges for backwards compat
36701     // minHeight/maxHeight were replaced by growMin/growMax to be
36702     // compatible with TextField growing config values
36703     if(this.minHeight !== undefined){
36704         this.growMin = this.minHeight;
36705     }
36706     if(this.maxHeight !== undefined){
36707         this.growMax = this.maxHeight;
36708     }
36709 };
36710
36711 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36712     /**
36713      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36714      */
36715     growMin : 60,
36716     /**
36717      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36718      */
36719     growMax: 1000,
36720     /**
36721      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36722      * in the field (equivalent to setting overflow: hidden, defaults to false)
36723      */
36724     preventScrollbars: false,
36725     /**
36726      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36727      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36728      */
36729
36730     // private
36731     onRender : function(ct, position){
36732         if(!this.el){
36733             this.defaultAutoCreate = {
36734                 tag: "textarea",
36735                 style:"width:300px;height:60px;",
36736                 autocomplete: "off"
36737             };
36738         }
36739         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36740         if(this.grow){
36741             this.textSizeEl = Roo.DomHelper.append(document.body, {
36742                 tag: "pre", cls: "x-form-grow-sizer"
36743             });
36744             if(this.preventScrollbars){
36745                 this.el.setStyle("overflow", "hidden");
36746             }
36747             this.el.setHeight(this.growMin);
36748         }
36749     },
36750
36751     onDestroy : function(){
36752         if(this.textSizeEl){
36753             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36754         }
36755         Roo.form.TextArea.superclass.onDestroy.call(this);
36756     },
36757
36758     // private
36759     onKeyUp : function(e){
36760         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36761             this.autoSize();
36762         }
36763     },
36764
36765     /**
36766      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36767      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36768      */
36769     autoSize : function(){
36770         if(!this.grow || !this.textSizeEl){
36771             return;
36772         }
36773         var el = this.el;
36774         var v = el.dom.value;
36775         var ts = this.textSizeEl;
36776
36777         ts.innerHTML = '';
36778         ts.appendChild(document.createTextNode(v));
36779         v = ts.innerHTML;
36780
36781         Roo.fly(ts).setWidth(this.el.getWidth());
36782         if(v.length < 1){
36783             v = "&#160;&#160;";
36784         }else{
36785             if(Roo.isIE){
36786                 v = v.replace(/\n/g, '<p>&#160;</p>');
36787             }
36788             v += "&#160;\n&#160;";
36789         }
36790         ts.innerHTML = v;
36791         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36792         if(h != this.lastHeight){
36793             this.lastHeight = h;
36794             this.el.setHeight(h);
36795             this.fireEvent("autosize", this, h);
36796         }
36797     }
36798 });/*
36799  * Based on:
36800  * Ext JS Library 1.1.1
36801  * Copyright(c) 2006-2007, Ext JS, LLC.
36802  *
36803  * Originally Released Under LGPL - original licence link has changed is not relivant.
36804  *
36805  * Fork - LGPL
36806  * <script type="text/javascript">
36807  */
36808  
36809
36810 /**
36811  * @class Roo.form.NumberField
36812  * @extends Roo.form.TextField
36813  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36814  * @constructor
36815  * Creates a new NumberField
36816  * @param {Object} config Configuration options
36817  */
36818 Roo.form.NumberField = function(config){
36819     Roo.form.NumberField.superclass.constructor.call(this, config);
36820 };
36821
36822 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36823     /**
36824      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36825      */
36826     fieldClass: "x-form-field x-form-num-field",
36827     /**
36828      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36829      */
36830     allowDecimals : true,
36831     /**
36832      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36833      */
36834     decimalSeparator : ".",
36835     /**
36836      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36837      */
36838     decimalPrecision : 2,
36839     /**
36840      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36841      */
36842     allowNegative : true,
36843     /**
36844      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36845      */
36846     minValue : Number.NEGATIVE_INFINITY,
36847     /**
36848      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36849      */
36850     maxValue : Number.MAX_VALUE,
36851     /**
36852      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36853      */
36854     minText : "The minimum value for this field is {0}",
36855     /**
36856      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36857      */
36858     maxText : "The maximum value for this field is {0}",
36859     /**
36860      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36861      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36862      */
36863     nanText : "{0} is not a valid number",
36864
36865     // private
36866     initEvents : function(){
36867         Roo.form.NumberField.superclass.initEvents.call(this);
36868         var allowed = "0123456789";
36869         if(this.allowDecimals){
36870             allowed += this.decimalSeparator;
36871         }
36872         if(this.allowNegative){
36873             allowed += "-";
36874         }
36875         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36876         var keyPress = function(e){
36877             var k = e.getKey();
36878             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36879                 return;
36880             }
36881             var c = e.getCharCode();
36882             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36883                 e.stopEvent();
36884             }
36885         };
36886         this.el.on("keypress", keyPress, this);
36887     },
36888
36889     // private
36890     validateValue : function(value){
36891         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36892             return false;
36893         }
36894         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36895              return true;
36896         }
36897         var num = this.parseValue(value);
36898         if(isNaN(num)){
36899             this.markInvalid(String.format(this.nanText, value));
36900             return false;
36901         }
36902         if(num < this.minValue){
36903             this.markInvalid(String.format(this.minText, this.minValue));
36904             return false;
36905         }
36906         if(num > this.maxValue){
36907             this.markInvalid(String.format(this.maxText, this.maxValue));
36908             return false;
36909         }
36910         return true;
36911     },
36912
36913     getValue : function(){
36914         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36915     },
36916
36917     // private
36918     parseValue : function(value){
36919         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36920         return isNaN(value) ? '' : value;
36921     },
36922
36923     // private
36924     fixPrecision : function(value){
36925         var nan = isNaN(value);
36926         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36927             return nan ? '' : value;
36928         }
36929         return parseFloat(value).toFixed(this.decimalPrecision);
36930     },
36931
36932     setValue : function(v){
36933         v = this.fixPrecision(v);
36934         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36935     },
36936
36937     // private
36938     decimalPrecisionFcn : function(v){
36939         return Math.floor(v);
36940     },
36941
36942     beforeBlur : function(){
36943         var v = this.parseValue(this.getRawValue());
36944         if(v){
36945             this.setValue(v);
36946         }
36947     }
36948 });/*
36949  * Based on:
36950  * Ext JS Library 1.1.1
36951  * Copyright(c) 2006-2007, Ext JS, LLC.
36952  *
36953  * Originally Released Under LGPL - original licence link has changed is not relivant.
36954  *
36955  * Fork - LGPL
36956  * <script type="text/javascript">
36957  */
36958  
36959 /**
36960  * @class Roo.form.DateField
36961  * @extends Roo.form.TriggerField
36962  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36963 * @constructor
36964 * Create a new DateField
36965 * @param {Object} config
36966  */
36967 Roo.form.DateField = function(config){
36968     Roo.form.DateField.superclass.constructor.call(this, config);
36969     
36970       this.addEvents({
36971          
36972         /**
36973          * @event select
36974          * Fires when a date is selected
36975              * @param {Roo.form.DateField} combo This combo box
36976              * @param {Date} date The date selected
36977              */
36978         'select' : true
36979          
36980     });
36981     
36982     
36983     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36984     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36985     this.ddMatch = null;
36986     if(this.disabledDates){
36987         var dd = this.disabledDates;
36988         var re = "(?:";
36989         for(var i = 0; i < dd.length; i++){
36990             re += dd[i];
36991             if(i != dd.length-1) re += "|";
36992         }
36993         this.ddMatch = new RegExp(re + ")");
36994     }
36995 };
36996
36997 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36998     /**
36999      * @cfg {String} format
37000      * The default date format string which can be overriden for localization support.  The format must be
37001      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37002      */
37003     format : "m/d/y",
37004     /**
37005      * @cfg {String} altFormats
37006      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37007      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37008      */
37009     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37010     /**
37011      * @cfg {Array} disabledDays
37012      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37013      */
37014     disabledDays : null,
37015     /**
37016      * @cfg {String} disabledDaysText
37017      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37018      */
37019     disabledDaysText : "Disabled",
37020     /**
37021      * @cfg {Array} disabledDates
37022      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37023      * expression so they are very powerful. Some examples:
37024      * <ul>
37025      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37026      * <li>["03/08", "09/16"] would disable those days for every year</li>
37027      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37028      * <li>["03/../2006"] would disable every day in March 2006</li>
37029      * <li>["^03"] would disable every day in every March</li>
37030      * </ul>
37031      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37032      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37033      */
37034     disabledDates : null,
37035     /**
37036      * @cfg {String} disabledDatesText
37037      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37038      */
37039     disabledDatesText : "Disabled",
37040     /**
37041      * @cfg {Date/String} minValue
37042      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37043      * valid format (defaults to null).
37044      */
37045     minValue : null,
37046     /**
37047      * @cfg {Date/String} maxValue
37048      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37049      * valid format (defaults to null).
37050      */
37051     maxValue : null,
37052     /**
37053      * @cfg {String} minText
37054      * The error text to display when the date in the cell is before minValue (defaults to
37055      * 'The date in this field must be after {minValue}').
37056      */
37057     minText : "The date in this field must be equal to or after {0}",
37058     /**
37059      * @cfg {String} maxText
37060      * The error text to display when the date in the cell is after maxValue (defaults to
37061      * 'The date in this field must be before {maxValue}').
37062      */
37063     maxText : "The date in this field must be equal to or before {0}",
37064     /**
37065      * @cfg {String} invalidText
37066      * The error text to display when the date in the field is invalid (defaults to
37067      * '{value} is not a valid date - it must be in the format {format}').
37068      */
37069     invalidText : "{0} is not a valid date - it must be in the format {1}",
37070     /**
37071      * @cfg {String} triggerClass
37072      * An additional CSS class used to style the trigger button.  The trigger will always get the
37073      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37074      * which displays a calendar icon).
37075      */
37076     triggerClass : 'x-form-date-trigger',
37077     
37078
37079     /**
37080      * @cfg {bool} useIso
37081      * if enabled, then the date field will use a hidden field to store the 
37082      * real value as iso formated date. default (false)
37083      */ 
37084     useIso : false,
37085     /**
37086      * @cfg {String/Object} autoCreate
37087      * A DomHelper element spec, or true for a default element spec (defaults to
37088      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37089      */ 
37090     // private
37091     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37092     
37093     // private
37094     hiddenField: false,
37095     
37096     onRender : function(ct, position)
37097     {
37098         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37099         if (this.useIso) {
37100             this.el.dom.removeAttribute('name'); 
37101             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37102                     'before', true);
37103             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37104             // prevent input submission
37105             this.hiddenName = this.name;
37106         }
37107             
37108             
37109     },
37110     
37111     // private
37112     validateValue : function(value)
37113     {
37114         value = this.formatDate(value);
37115         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37116             return false;
37117         }
37118         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37119              return true;
37120         }
37121         var svalue = value;
37122         value = this.parseDate(value);
37123         if(!value){
37124             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37125             return false;
37126         }
37127         var time = value.getTime();
37128         if(this.minValue && time < this.minValue.getTime()){
37129             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37130             return false;
37131         }
37132         if(this.maxValue && time > this.maxValue.getTime()){
37133             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37134             return false;
37135         }
37136         if(this.disabledDays){
37137             var day = value.getDay();
37138             for(var i = 0; i < this.disabledDays.length; i++) {
37139                 if(day === this.disabledDays[i]){
37140                     this.markInvalid(this.disabledDaysText);
37141                     return false;
37142                 }
37143             }
37144         }
37145         var fvalue = this.formatDate(value);
37146         if(this.ddMatch && this.ddMatch.test(fvalue)){
37147             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37148             return false;
37149         }
37150         return true;
37151     },
37152
37153     // private
37154     // Provides logic to override the default TriggerField.validateBlur which just returns true
37155     validateBlur : function(){
37156         return !this.menu || !this.menu.isVisible();
37157     },
37158
37159     /**
37160      * Returns the current date value of the date field.
37161      * @return {Date} The date value
37162      */
37163     getValue : function(){
37164         
37165         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37166     },
37167
37168     /**
37169      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37170      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37171      * (the default format used is "m/d/y").
37172      * <br />Usage:
37173      * <pre><code>
37174 //All of these calls set the same date value (May 4, 2006)
37175
37176 //Pass a date object:
37177 var dt = new Date('5/4/06');
37178 dateField.setValue(dt);
37179
37180 //Pass a date string (default format):
37181 dateField.setValue('5/4/06');
37182
37183 //Pass a date string (custom format):
37184 dateField.format = 'Y-m-d';
37185 dateField.setValue('2006-5-4');
37186 </code></pre>
37187      * @param {String/Date} date The date or valid date string
37188      */
37189     setValue : function(date){
37190         if (this.hiddenField) {
37191             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37192         }
37193         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37194     },
37195
37196     // private
37197     parseDate : function(value){
37198         if(!value || value instanceof Date){
37199             return value;
37200         }
37201         var v = Date.parseDate(value, this.format);
37202         if(!v && this.altFormats){
37203             if(!this.altFormatsArray){
37204                 this.altFormatsArray = this.altFormats.split("|");
37205             }
37206             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37207                 v = Date.parseDate(value, this.altFormatsArray[i]);
37208             }
37209         }
37210         return v;
37211     },
37212
37213     // private
37214     formatDate : function(date, fmt){
37215         return (!date || !(date instanceof Date)) ?
37216                date : date.dateFormat(fmt || this.format);
37217     },
37218
37219     // private
37220     menuListeners : {
37221         select: function(m, d){
37222             this.setValue(d);
37223             this.fireEvent('select', this, d);
37224         },
37225         show : function(){ // retain focus styling
37226             this.onFocus();
37227         },
37228         hide : function(){
37229             this.focus.defer(10, this);
37230             var ml = this.menuListeners;
37231             this.menu.un("select", ml.select,  this);
37232             this.menu.un("show", ml.show,  this);
37233             this.menu.un("hide", ml.hide,  this);
37234         }
37235     },
37236
37237     // private
37238     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37239     onTriggerClick : function(){
37240         if(this.disabled){
37241             return;
37242         }
37243         if(this.menu == null){
37244             this.menu = new Roo.menu.DateMenu();
37245         }
37246         Roo.apply(this.menu.picker,  {
37247             showClear: this.allowBlank,
37248             minDate : this.minValue,
37249             maxDate : this.maxValue,
37250             disabledDatesRE : this.ddMatch,
37251             disabledDatesText : this.disabledDatesText,
37252             disabledDays : this.disabledDays,
37253             disabledDaysText : this.disabledDaysText,
37254             format : this.format,
37255             minText : String.format(this.minText, this.formatDate(this.minValue)),
37256             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37257         });
37258         this.menu.on(Roo.apply({}, this.menuListeners, {
37259             scope:this
37260         }));
37261         this.menu.picker.setValue(this.getValue() || new Date());
37262         this.menu.show(this.el, "tl-bl?");
37263     },
37264
37265     beforeBlur : function(){
37266         var v = this.parseDate(this.getRawValue());
37267         if(v){
37268             this.setValue(v);
37269         }
37270     }
37271
37272     /** @cfg {Boolean} grow @hide */
37273     /** @cfg {Number} growMin @hide */
37274     /** @cfg {Number} growMax @hide */
37275     /**
37276      * @hide
37277      * @method autoSize
37278      */
37279 });/*
37280  * Based on:
37281  * Ext JS Library 1.1.1
37282  * Copyright(c) 2006-2007, Ext JS, LLC.
37283  *
37284  * Originally Released Under LGPL - original licence link has changed is not relivant.
37285  *
37286  * Fork - LGPL
37287  * <script type="text/javascript">
37288  */
37289  
37290
37291 /**
37292  * @class Roo.form.ComboBox
37293  * @extends Roo.form.TriggerField
37294  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37295  * @constructor
37296  * Create a new ComboBox.
37297  * @param {Object} config Configuration options
37298  */
37299 Roo.form.ComboBox = function(config){
37300     Roo.form.ComboBox.superclass.constructor.call(this, config);
37301     this.addEvents({
37302         /**
37303          * @event expand
37304          * Fires when the dropdown list is expanded
37305              * @param {Roo.form.ComboBox} combo This combo box
37306              */
37307         'expand' : true,
37308         /**
37309          * @event collapse
37310          * Fires when the dropdown list is collapsed
37311              * @param {Roo.form.ComboBox} combo This combo box
37312              */
37313         'collapse' : true,
37314         /**
37315          * @event beforeselect
37316          * Fires before a list item is selected. Return false to cancel the selection.
37317              * @param {Roo.form.ComboBox} combo This combo box
37318              * @param {Roo.data.Record} record The data record returned from the underlying store
37319              * @param {Number} index The index of the selected item in the dropdown list
37320              */
37321         'beforeselect' : true,
37322         /**
37323          * @event select
37324          * Fires when a list item is selected
37325              * @param {Roo.form.ComboBox} combo This combo box
37326              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37327              * @param {Number} index The index of the selected item in the dropdown list
37328              */
37329         'select' : true,
37330         /**
37331          * @event beforequery
37332          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37333          * The event object passed has these properties:
37334              * @param {Roo.form.ComboBox} combo This combo box
37335              * @param {String} query The query
37336              * @param {Boolean} forceAll true to force "all" query
37337              * @param {Boolean} cancel true to cancel the query
37338              * @param {Object} e The query event object
37339              */
37340         'beforequery': true,
37341          /**
37342          * @event add
37343          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37344              * @param {Roo.form.ComboBox} combo This combo box
37345              */
37346         'add' : true,
37347         /**
37348          * @event edit
37349          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37350              * @param {Roo.form.ComboBox} combo This combo box
37351              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37352              */
37353         'edit' : true
37354         
37355         
37356     });
37357     if(this.transform){
37358         this.allowDomMove = false;
37359         var s = Roo.getDom(this.transform);
37360         if(!this.hiddenName){
37361             this.hiddenName = s.name;
37362         }
37363         if(!this.store){
37364             this.mode = 'local';
37365             var d = [], opts = s.options;
37366             for(var i = 0, len = opts.length;i < len; i++){
37367                 var o = opts[i];
37368                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37369                 if(o.selected) {
37370                     this.value = value;
37371                 }
37372                 d.push([value, o.text]);
37373             }
37374             this.store = new Roo.data.SimpleStore({
37375                 'id': 0,
37376                 fields: ['value', 'text'],
37377                 data : d
37378             });
37379             this.valueField = 'value';
37380             this.displayField = 'text';
37381         }
37382         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37383         if(!this.lazyRender){
37384             this.target = true;
37385             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37386             s.parentNode.removeChild(s); // remove it
37387             this.render(this.el.parentNode);
37388         }else{
37389             s.parentNode.removeChild(s); // remove it
37390         }
37391
37392     }
37393     if (this.store) {
37394         this.store = Roo.factory(this.store, Roo.data);
37395     }
37396     
37397     this.selectedIndex = -1;
37398     if(this.mode == 'local'){
37399         if(config.queryDelay === undefined){
37400             this.queryDelay = 10;
37401         }
37402         if(config.minChars === undefined){
37403             this.minChars = 0;
37404         }
37405     }
37406 };
37407
37408 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37409     /**
37410      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37411      */
37412     /**
37413      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37414      * rendering into an Roo.Editor, defaults to false)
37415      */
37416     /**
37417      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37418      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37419      */
37420     /**
37421      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37422      */
37423     /**
37424      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37425      * the dropdown list (defaults to undefined, with no header element)
37426      */
37427
37428      /**
37429      * @cfg {String/Roo.Template} tpl The template to use to render the output
37430      */
37431      
37432     // private
37433     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37434     /**
37435      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37436      */
37437     listWidth: undefined,
37438     /**
37439      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37440      * mode = 'remote' or 'text' if mode = 'local')
37441      */
37442     displayField: undefined,
37443     /**
37444      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37445      * mode = 'remote' or 'value' if mode = 'local'). 
37446      * Note: use of a valueField requires the user make a selection
37447      * in order for a value to be mapped.
37448      */
37449     valueField: undefined,
37450     
37451     
37452     /**
37453      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37454      * field's data value (defaults to the underlying DOM element's name)
37455      */
37456     hiddenName: undefined,
37457     /**
37458      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37459      */
37460     listClass: '',
37461     /**
37462      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37463      */
37464     selectedClass: 'x-combo-selected',
37465     /**
37466      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37467      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37468      * which displays a downward arrow icon).
37469      */
37470     triggerClass : 'x-form-arrow-trigger',
37471     /**
37472      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37473      */
37474     shadow:'sides',
37475     /**
37476      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37477      * anchor positions (defaults to 'tl-bl')
37478      */
37479     listAlign: 'tl-bl?',
37480     /**
37481      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37482      */
37483     maxHeight: 300,
37484     /**
37485      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37486      * query specified by the allQuery config option (defaults to 'query')
37487      */
37488     triggerAction: 'query',
37489     /**
37490      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37491      * (defaults to 4, does not apply if editable = false)
37492      */
37493     minChars : 4,
37494     /**
37495      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37496      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37497      */
37498     typeAhead: false,
37499     /**
37500      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37501      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37502      */
37503     queryDelay: 500,
37504     /**
37505      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37506      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37507      */
37508     pageSize: 0,
37509     /**
37510      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37511      * when editable = true (defaults to false)
37512      */
37513     selectOnFocus:false,
37514     /**
37515      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37516      */
37517     queryParam: 'query',
37518     /**
37519      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37520      * when mode = 'remote' (defaults to 'Loading...')
37521      */
37522     loadingText: 'Loading...',
37523     /**
37524      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37525      */
37526     resizable: false,
37527     /**
37528      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37529      */
37530     handleHeight : 8,
37531     /**
37532      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37533      * traditional select (defaults to true)
37534      */
37535     editable: true,
37536     /**
37537      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37538      */
37539     allQuery: '',
37540     /**
37541      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37542      */
37543     mode: 'remote',
37544     /**
37545      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37546      * listWidth has a higher value)
37547      */
37548     minListWidth : 70,
37549     /**
37550      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37551      * allow the user to set arbitrary text into the field (defaults to false)
37552      */
37553     forceSelection:false,
37554     /**
37555      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37556      * if typeAhead = true (defaults to 250)
37557      */
37558     typeAheadDelay : 250,
37559     /**
37560      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37561      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37562      */
37563     valueNotFoundText : undefined,
37564     /**
37565      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37566      */
37567     blockFocus : false,
37568     
37569     /**
37570      * @cfg {Boolean} disableClear Disable showing of clear button.
37571      */
37572     disableClear : false,
37573     /**
37574      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37575      */
37576     alwaysQuery : false,
37577     
37578     //private
37579     addicon : false,
37580     editicon: false,
37581     
37582     // element that contains real text value.. (when hidden is used..)
37583      
37584     // private
37585     onRender : function(ct, position){
37586         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37587         if(this.hiddenName){
37588             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37589                     'before', true);
37590             this.hiddenField.value =
37591                 this.hiddenValue !== undefined ? this.hiddenValue :
37592                 this.value !== undefined ? this.value : '';
37593
37594             // prevent input submission
37595             this.el.dom.removeAttribute('name');
37596              
37597              
37598         }
37599         if(Roo.isGecko){
37600             this.el.dom.setAttribute('autocomplete', 'off');
37601         }
37602
37603         var cls = 'x-combo-list';
37604
37605         this.list = new Roo.Layer({
37606             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37607         });
37608
37609         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37610         this.list.setWidth(lw);
37611         this.list.swallowEvent('mousewheel');
37612         this.assetHeight = 0;
37613
37614         if(this.title){
37615             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37616             this.assetHeight += this.header.getHeight();
37617         }
37618
37619         this.innerList = this.list.createChild({cls:cls+'-inner'});
37620         this.innerList.on('mouseover', this.onViewOver, this);
37621         this.innerList.on('mousemove', this.onViewMove, this);
37622         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37623         
37624         if(this.allowBlank && !this.pageSize && !this.disableClear){
37625             this.footer = this.list.createChild({cls:cls+'-ft'});
37626             this.pageTb = new Roo.Toolbar(this.footer);
37627            
37628         }
37629         if(this.pageSize){
37630             this.footer = this.list.createChild({cls:cls+'-ft'});
37631             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37632                     {pageSize: this.pageSize});
37633             
37634         }
37635         
37636         if (this.pageTb && this.allowBlank && !this.disableClear) {
37637             var _this = this;
37638             this.pageTb.add(new Roo.Toolbar.Fill(), {
37639                 cls: 'x-btn-icon x-btn-clear',
37640                 text: '&#160;',
37641                 handler: function()
37642                 {
37643                     _this.collapse();
37644                     _this.clearValue();
37645                     _this.onSelect(false, -1);
37646                 }
37647             });
37648         }
37649         if (this.footer) {
37650             this.assetHeight += this.footer.getHeight();
37651         }
37652         
37653
37654         if(!this.tpl){
37655             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37656         }
37657
37658         this.view = new Roo.View(this.innerList, this.tpl, {
37659             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37660         });
37661
37662         this.view.on('click', this.onViewClick, this);
37663
37664         this.store.on('beforeload', this.onBeforeLoad, this);
37665         this.store.on('load', this.onLoad, this);
37666         this.store.on('loadexception', this.onLoadException, this);
37667
37668         if(this.resizable){
37669             this.resizer = new Roo.Resizable(this.list,  {
37670                pinned:true, handles:'se'
37671             });
37672             this.resizer.on('resize', function(r, w, h){
37673                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37674                 this.listWidth = w;
37675                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37676                 this.restrictHeight();
37677             }, this);
37678             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37679         }
37680         if(!this.editable){
37681             this.editable = true;
37682             this.setEditable(false);
37683         }  
37684         
37685         
37686         if (typeof(this.events.add.listeners) != 'undefined') {
37687             
37688             this.addicon = this.wrap.createChild(
37689                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37690        
37691             this.addicon.on('click', function(e) {
37692                 this.fireEvent('add', this);
37693             }, this);
37694         }
37695         if (typeof(this.events.edit.listeners) != 'undefined') {
37696             
37697             this.editicon = this.wrap.createChild(
37698                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37699             if (this.addicon) {
37700                 this.editicon.setStyle('margin-left', '40px');
37701             }
37702             this.editicon.on('click', function(e) {
37703                 
37704                 // we fire even  if inothing is selected..
37705                 this.fireEvent('edit', this, this.lastData );
37706                 
37707             }, this);
37708         }
37709         
37710         
37711         
37712     },
37713
37714     // private
37715     initEvents : function(){
37716         Roo.form.ComboBox.superclass.initEvents.call(this);
37717
37718         this.keyNav = new Roo.KeyNav(this.el, {
37719             "up" : function(e){
37720                 this.inKeyMode = true;
37721                 this.selectPrev();
37722             },
37723
37724             "down" : function(e){
37725                 if(!this.isExpanded()){
37726                     this.onTriggerClick();
37727                 }else{
37728                     this.inKeyMode = true;
37729                     this.selectNext();
37730                 }
37731             },
37732
37733             "enter" : function(e){
37734                 this.onViewClick();
37735                 //return true;
37736             },
37737
37738             "esc" : function(e){
37739                 this.collapse();
37740             },
37741
37742             "tab" : function(e){
37743                 this.onViewClick(false);
37744                 this.fireEvent("specialkey", this, e);
37745                 return true;
37746             },
37747
37748             scope : this,
37749
37750             doRelay : function(foo, bar, hname){
37751                 if(hname == 'down' || this.scope.isExpanded()){
37752                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37753                 }
37754                 return true;
37755             },
37756
37757             forceKeyDown: true
37758         });
37759         this.queryDelay = Math.max(this.queryDelay || 10,
37760                 this.mode == 'local' ? 10 : 250);
37761         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37762         if(this.typeAhead){
37763             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37764         }
37765         if(this.editable !== false){
37766             this.el.on("keyup", this.onKeyUp, this);
37767         }
37768         if(this.forceSelection){
37769             this.on('blur', this.doForce, this);
37770         }
37771     },
37772
37773     onDestroy : function(){
37774         if(this.view){
37775             this.view.setStore(null);
37776             this.view.el.removeAllListeners();
37777             this.view.el.remove();
37778             this.view.purgeListeners();
37779         }
37780         if(this.list){
37781             this.list.destroy();
37782         }
37783         if(this.store){
37784             this.store.un('beforeload', this.onBeforeLoad, this);
37785             this.store.un('load', this.onLoad, this);
37786             this.store.un('loadexception', this.onLoadException, this);
37787         }
37788         Roo.form.ComboBox.superclass.onDestroy.call(this);
37789     },
37790
37791     // private
37792     fireKey : function(e){
37793         if(e.isNavKeyPress() && !this.list.isVisible()){
37794             this.fireEvent("specialkey", this, e);
37795         }
37796     },
37797
37798     // private
37799     onResize: function(w, h){
37800         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37801         
37802         if(typeof w != 'number'){
37803             // we do not handle it!?!?
37804             return;
37805         }
37806         var tw = this.trigger.getWidth();
37807         tw += this.addicon ? this.addicon.getWidth() : 0;
37808         tw += this.editicon ? this.editicon.getWidth() : 0;
37809         var x = w - tw;
37810         this.el.setWidth( this.adjustWidth('input', x));
37811             
37812         this.trigger.setStyle('left', x+'px');
37813         
37814         if(this.list && this.listWidth === undefined){
37815             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37816             this.list.setWidth(lw);
37817             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37818         }
37819         
37820     
37821         
37822     },
37823
37824     /**
37825      * Allow or prevent the user from directly editing the field text.  If false is passed,
37826      * the user will only be able to select from the items defined in the dropdown list.  This method
37827      * is the runtime equivalent of setting the 'editable' config option at config time.
37828      * @param {Boolean} value True to allow the user to directly edit the field text
37829      */
37830     setEditable : function(value){
37831         if(value == this.editable){
37832             return;
37833         }
37834         this.editable = value;
37835         if(!value){
37836             this.el.dom.setAttribute('readOnly', true);
37837             this.el.on('mousedown', this.onTriggerClick,  this);
37838             this.el.addClass('x-combo-noedit');
37839         }else{
37840             this.el.dom.setAttribute('readOnly', false);
37841             this.el.un('mousedown', this.onTriggerClick,  this);
37842             this.el.removeClass('x-combo-noedit');
37843         }
37844     },
37845
37846     // private
37847     onBeforeLoad : function(){
37848         if(!this.hasFocus){
37849             return;
37850         }
37851         this.innerList.update(this.loadingText ?
37852                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37853         this.restrictHeight();
37854         this.selectedIndex = -1;
37855     },
37856
37857     // private
37858     onLoad : function(){
37859         if(!this.hasFocus){
37860             return;
37861         }
37862         if(this.store.getCount() > 0){
37863             this.expand();
37864             this.restrictHeight();
37865             if(this.lastQuery == this.allQuery){
37866                 if(this.editable){
37867                     this.el.dom.select();
37868                 }
37869                 if(!this.selectByValue(this.value, true)){
37870                     this.select(0, true);
37871                 }
37872             }else{
37873                 this.selectNext();
37874                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37875                     this.taTask.delay(this.typeAheadDelay);
37876                 }
37877             }
37878         }else{
37879             this.onEmptyResults();
37880         }
37881         //this.el.focus();
37882     },
37883     // private
37884     onLoadException : function()
37885     {
37886         this.collapse();
37887         Roo.log(this.store.reader.jsonData);
37888         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37889             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37890         }
37891         
37892         
37893     },
37894     // private
37895     onTypeAhead : function(){
37896         if(this.store.getCount() > 0){
37897             var r = this.store.getAt(0);
37898             var newValue = r.data[this.displayField];
37899             var len = newValue.length;
37900             var selStart = this.getRawValue().length;
37901             if(selStart != len){
37902                 this.setRawValue(newValue);
37903                 this.selectText(selStart, newValue.length);
37904             }
37905         }
37906     },
37907
37908     // private
37909     onSelect : function(record, index){
37910         if(this.fireEvent('beforeselect', this, record, index) !== false){
37911             this.setFromData(index > -1 ? record.data : false);
37912             this.collapse();
37913             this.fireEvent('select', this, record, index);
37914         }
37915     },
37916
37917     /**
37918      * Returns the currently selected field value or empty string if no value is set.
37919      * @return {String} value The selected value
37920      */
37921     getValue : function(){
37922         if(this.valueField){
37923             return typeof this.value != 'undefined' ? this.value : '';
37924         }else{
37925             return Roo.form.ComboBox.superclass.getValue.call(this);
37926         }
37927     },
37928
37929     /**
37930      * Clears any text/value currently set in the field
37931      */
37932     clearValue : function(){
37933         if(this.hiddenField){
37934             this.hiddenField.value = '';
37935         }
37936         this.value = '';
37937         this.setRawValue('');
37938         this.lastSelectionText = '';
37939         this.applyEmptyText();
37940     },
37941
37942     /**
37943      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37944      * will be displayed in the field.  If the value does not match the data value of an existing item,
37945      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37946      * Otherwise the field will be blank (although the value will still be set).
37947      * @param {String} value The value to match
37948      */
37949     setValue : function(v){
37950         var text = v;
37951         if(this.valueField){
37952             var r = this.findRecord(this.valueField, v);
37953             if(r){
37954                 text = r.data[this.displayField];
37955             }else if(this.valueNotFoundText !== undefined){
37956                 text = this.valueNotFoundText;
37957             }
37958         }
37959         this.lastSelectionText = text;
37960         if(this.hiddenField){
37961             this.hiddenField.value = v;
37962         }
37963         Roo.form.ComboBox.superclass.setValue.call(this, text);
37964         this.value = v;
37965     },
37966     /**
37967      * @property {Object} the last set data for the element
37968      */
37969     
37970     lastData : false,
37971     /**
37972      * Sets the value of the field based on a object which is related to the record format for the store.
37973      * @param {Object} value the value to set as. or false on reset?
37974      */
37975     setFromData : function(o){
37976         var dv = ''; // display value
37977         var vv = ''; // value value..
37978         this.lastData = o;
37979         if (this.displayField) {
37980             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37981         } else {
37982             // this is an error condition!!!
37983             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37984         }
37985         
37986         if(this.valueField){
37987             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37988         }
37989         if(this.hiddenField){
37990             this.hiddenField.value = vv;
37991             
37992             this.lastSelectionText = dv;
37993             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37994             this.value = vv;
37995             return;
37996         }
37997         // no hidden field.. - we store the value in 'value', but still display
37998         // display field!!!!
37999         this.lastSelectionText = dv;
38000         Roo.form.ComboBox.superclass.setValue.call(this, dv);
38001         this.value = vv;
38002         
38003         
38004     },
38005     // private
38006     reset : function(){
38007         // overridden so that last data is reset..
38008         this.setValue(this.originalValue);
38009         this.clearInvalid();
38010         this.lastData = false;
38011     },
38012     // private
38013     findRecord : function(prop, value){
38014         var record;
38015         if(this.store.getCount() > 0){
38016             this.store.each(function(r){
38017                 if(r.data[prop] == value){
38018                     record = r;
38019                     return false;
38020                 }
38021                 return true;
38022             });
38023         }
38024         return record;
38025     },
38026     
38027     getName: function()
38028     {
38029         // returns hidden if it's set..
38030         if (!this.rendered) {return ''};
38031         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38032         
38033     },
38034     // private
38035     onViewMove : function(e, t){
38036         this.inKeyMode = false;
38037     },
38038
38039     // private
38040     onViewOver : function(e, t){
38041         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38042             return;
38043         }
38044         var item = this.view.findItemFromChild(t);
38045         if(item){
38046             var index = this.view.indexOf(item);
38047             this.select(index, false);
38048         }
38049     },
38050
38051     // private
38052     onViewClick : function(doFocus)
38053     {
38054         var index = this.view.getSelectedIndexes()[0];
38055         var r = this.store.getAt(index);
38056         if(r){
38057             this.onSelect(r, index);
38058         }
38059         if(doFocus !== false && !this.blockFocus){
38060             this.el.focus();
38061         }
38062     },
38063
38064     // private
38065     restrictHeight : function(){
38066         this.innerList.dom.style.height = '';
38067         var inner = this.innerList.dom;
38068         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38069         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38070         this.list.beginUpdate();
38071         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38072         this.list.alignTo(this.el, this.listAlign);
38073         this.list.endUpdate();
38074     },
38075
38076     // private
38077     onEmptyResults : function(){
38078         this.collapse();
38079     },
38080
38081     /**
38082      * Returns true if the dropdown list is expanded, else false.
38083      */
38084     isExpanded : function(){
38085         return this.list.isVisible();
38086     },
38087
38088     /**
38089      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38090      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38091      * @param {String} value The data value of the item to select
38092      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38093      * selected item if it is not currently in view (defaults to true)
38094      * @return {Boolean} True if the value matched an item in the list, else false
38095      */
38096     selectByValue : function(v, scrollIntoView){
38097         if(v !== undefined && v !== null){
38098             var r = this.findRecord(this.valueField || this.displayField, v);
38099             if(r){
38100                 this.select(this.store.indexOf(r), scrollIntoView);
38101                 return true;
38102             }
38103         }
38104         return false;
38105     },
38106
38107     /**
38108      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38109      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38110      * @param {Number} index The zero-based index of the list item to select
38111      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38112      * selected item if it is not currently in view (defaults to true)
38113      */
38114     select : function(index, scrollIntoView){
38115         this.selectedIndex = index;
38116         this.view.select(index);
38117         if(scrollIntoView !== false){
38118             var el = this.view.getNode(index);
38119             if(el){
38120                 this.innerList.scrollChildIntoView(el, false);
38121             }
38122         }
38123     },
38124
38125     // private
38126     selectNext : function(){
38127         var ct = this.store.getCount();
38128         if(ct > 0){
38129             if(this.selectedIndex == -1){
38130                 this.select(0);
38131             }else if(this.selectedIndex < ct-1){
38132                 this.select(this.selectedIndex+1);
38133             }
38134         }
38135     },
38136
38137     // private
38138     selectPrev : function(){
38139         var ct = this.store.getCount();
38140         if(ct > 0){
38141             if(this.selectedIndex == -1){
38142                 this.select(0);
38143             }else if(this.selectedIndex != 0){
38144                 this.select(this.selectedIndex-1);
38145             }
38146         }
38147     },
38148
38149     // private
38150     onKeyUp : function(e){
38151         if(this.editable !== false && !e.isSpecialKey()){
38152             this.lastKey = e.getKey();
38153             this.dqTask.delay(this.queryDelay);
38154         }
38155     },
38156
38157     // private
38158     validateBlur : function(){
38159         return !this.list || !this.list.isVisible();   
38160     },
38161
38162     // private
38163     initQuery : function(){
38164         this.doQuery(this.getRawValue());
38165     },
38166
38167     // private
38168     doForce : function(){
38169         if(this.el.dom.value.length > 0){
38170             this.el.dom.value =
38171                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38172             this.applyEmptyText();
38173         }
38174     },
38175
38176     /**
38177      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38178      * query allowing the query action to be canceled if needed.
38179      * @param {String} query The SQL query to execute
38180      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38181      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38182      * saved in the current store (defaults to false)
38183      */
38184     doQuery : function(q, forceAll){
38185         if(q === undefined || q === null){
38186             q = '';
38187         }
38188         var qe = {
38189             query: q,
38190             forceAll: forceAll,
38191             combo: this,
38192             cancel:false
38193         };
38194         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38195             return false;
38196         }
38197         q = qe.query;
38198         forceAll = qe.forceAll;
38199         if(forceAll === true || (q.length >= this.minChars)){
38200             if(this.lastQuery != q || this.alwaysQuery){
38201                 this.lastQuery = q;
38202                 if(this.mode == 'local'){
38203                     this.selectedIndex = -1;
38204                     if(forceAll){
38205                         this.store.clearFilter();
38206                     }else{
38207                         this.store.filter(this.displayField, q);
38208                     }
38209                     this.onLoad();
38210                 }else{
38211                     this.store.baseParams[this.queryParam] = q;
38212                     this.store.load({
38213                         params: this.getParams(q)
38214                     });
38215                     this.expand();
38216                 }
38217             }else{
38218                 this.selectedIndex = -1;
38219                 this.onLoad();   
38220             }
38221         }
38222     },
38223
38224     // private
38225     getParams : function(q){
38226         var p = {};
38227         //p[this.queryParam] = q;
38228         if(this.pageSize){
38229             p.start = 0;
38230             p.limit = this.pageSize;
38231         }
38232         return p;
38233     },
38234
38235     /**
38236      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38237      */
38238     collapse : function(){
38239         if(!this.isExpanded()){
38240             return;
38241         }
38242         this.list.hide();
38243         Roo.get(document).un('mousedown', this.collapseIf, this);
38244         Roo.get(document).un('mousewheel', this.collapseIf, this);
38245         if (!this.editable) {
38246             Roo.get(document).un('keydown', this.listKeyPress, this);
38247         }
38248         this.fireEvent('collapse', this);
38249     },
38250
38251     // private
38252     collapseIf : function(e){
38253         if(!e.within(this.wrap) && !e.within(this.list)){
38254             this.collapse();
38255         }
38256     },
38257
38258     /**
38259      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38260      */
38261     expand : function(){
38262         if(this.isExpanded() || !this.hasFocus){
38263             return;
38264         }
38265         this.list.alignTo(this.el, this.listAlign);
38266         this.list.show();
38267         Roo.get(document).on('mousedown', this.collapseIf, this);
38268         Roo.get(document).on('mousewheel', this.collapseIf, this);
38269         if (!this.editable) {
38270             Roo.get(document).on('keydown', this.listKeyPress, this);
38271         }
38272         
38273         this.fireEvent('expand', this);
38274     },
38275
38276     // private
38277     // Implements the default empty TriggerField.onTriggerClick function
38278     onTriggerClick : function(){
38279         if(this.disabled){
38280             return;
38281         }
38282         if(this.isExpanded()){
38283             this.collapse();
38284             if (!this.blockFocus) {
38285                 this.el.focus();
38286             }
38287             
38288         }else {
38289             this.hasFocus = true;
38290             if(this.triggerAction == 'all') {
38291                 this.doQuery(this.allQuery, true);
38292             } else {
38293                 this.doQuery(this.getRawValue());
38294             }
38295             if (!this.blockFocus) {
38296                 this.el.focus();
38297             }
38298         }
38299     },
38300     listKeyPress : function(e)
38301     {
38302         //Roo.log('listkeypress');
38303         // scroll to first matching element based on key pres..
38304         if (e.isSpecialKey()) {
38305             return false;
38306         }
38307         var k = String.fromCharCode(e.getKey()).toUpperCase();
38308         //Roo.log(k);
38309         var match  = false;
38310         var csel = this.view.getSelectedNodes();
38311         var cselitem = false;
38312         if (csel.length) {
38313             var ix = this.view.indexOf(csel[0]);
38314             cselitem  = this.store.getAt(ix);
38315             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38316                 cselitem = false;
38317             }
38318             
38319         }
38320         
38321         this.store.each(function(v) { 
38322             if (cselitem) {
38323                 // start at existing selection.
38324                 if (cselitem.id == v.id) {
38325                     cselitem = false;
38326                 }
38327                 return;
38328             }
38329                 
38330             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38331                 match = this.store.indexOf(v);
38332                 return false;
38333             }
38334         }, this);
38335         
38336         if (match === false) {
38337             return true; // no more action?
38338         }
38339         // scroll to?
38340         this.view.select(match);
38341         var sn = Roo.get(this.view.getSelectedNodes()[0])
38342         sn.scrollIntoView(sn.dom.parentNode, false);
38343     }
38344
38345     /** 
38346     * @cfg {Boolean} grow 
38347     * @hide 
38348     */
38349     /** 
38350     * @cfg {Number} growMin 
38351     * @hide 
38352     */
38353     /** 
38354     * @cfg {Number} growMax 
38355     * @hide 
38356     */
38357     /**
38358      * @hide
38359      * @method autoSize
38360      */
38361 });/*
38362  * Based on:
38363  * Ext JS Library 1.1.1
38364  * Copyright(c) 2006-2007, Ext JS, LLC.
38365  *
38366  * Originally Released Under LGPL - original licence link has changed is not relivant.
38367  *
38368  * Fork - LGPL
38369  * <script type="text/javascript">
38370  */
38371 /**
38372  * @class Roo.form.Checkbox
38373  * @extends Roo.form.Field
38374  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38375  * @constructor
38376  * Creates a new Checkbox
38377  * @param {Object} config Configuration options
38378  */
38379 Roo.form.Checkbox = function(config){
38380     Roo.form.Checkbox.superclass.constructor.call(this, config);
38381     this.addEvents({
38382         /**
38383          * @event check
38384          * Fires when the checkbox is checked or unchecked.
38385              * @param {Roo.form.Checkbox} this This checkbox
38386              * @param {Boolean} checked The new checked value
38387              */
38388         check : true
38389     });
38390 };
38391
38392 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38393     /**
38394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38395      */
38396     focusClass : undefined,
38397     /**
38398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38399      */
38400     fieldClass: "x-form-field",
38401     /**
38402      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38403      */
38404     checked: false,
38405     /**
38406      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38407      * {tag: "input", type: "checkbox", autocomplete: "off"})
38408      */
38409     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38410     /**
38411      * @cfg {String} boxLabel The text that appears beside the checkbox
38412      */
38413     boxLabel : "",
38414     /**
38415      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38416      */  
38417     inputValue : '1',
38418     /**
38419      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38420      */
38421      valueOff: '0', // value when not checked..
38422
38423     actionMode : 'viewEl', 
38424     //
38425     // private
38426     itemCls : 'x-menu-check-item x-form-item',
38427     groupClass : 'x-menu-group-item',
38428     inputType : 'hidden',
38429     
38430     
38431     inSetChecked: false, // check that we are not calling self...
38432     
38433     inputElement: false, // real input element?
38434     basedOn: false, // ????
38435     
38436     isFormField: true, // not sure where this is needed!!!!
38437
38438     onResize : function(){
38439         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38440         if(!this.boxLabel){
38441             this.el.alignTo(this.wrap, 'c-c');
38442         }
38443     },
38444
38445     initEvents : function(){
38446         Roo.form.Checkbox.superclass.initEvents.call(this);
38447         this.el.on("click", this.onClick,  this);
38448         this.el.on("change", this.onClick,  this);
38449     },
38450
38451
38452     getResizeEl : function(){
38453         return this.wrap;
38454     },
38455
38456     getPositionEl : function(){
38457         return this.wrap;
38458     },
38459
38460     // private
38461     onRender : function(ct, position){
38462         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38463         /*
38464         if(this.inputValue !== undefined){
38465             this.el.dom.value = this.inputValue;
38466         }
38467         */
38468         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38469         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38470         var viewEl = this.wrap.createChild({ 
38471             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38472         this.viewEl = viewEl;   
38473         this.wrap.on('click', this.onClick,  this); 
38474         
38475         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38476         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38477         
38478         
38479         
38480         if(this.boxLabel){
38481             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38482         //    viewEl.on('click', this.onClick,  this); 
38483         }
38484         //if(this.checked){
38485             this.setChecked(this.checked);
38486         //}else{
38487             //this.checked = this.el.dom;
38488         //}
38489
38490     },
38491
38492     // private
38493     initValue : Roo.emptyFn,
38494
38495     /**
38496      * Returns the checked state of the checkbox.
38497      * @return {Boolean} True if checked, else false
38498      */
38499     getValue : function(){
38500         if(this.el){
38501             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38502         }
38503         return this.valueOff;
38504         
38505     },
38506
38507         // private
38508     onClick : function(){ 
38509         this.setChecked(!this.checked);
38510
38511         //if(this.el.dom.checked != this.checked){
38512         //    this.setValue(this.el.dom.checked);
38513        // }
38514     },
38515
38516     /**
38517      * Sets the checked state of the checkbox.
38518      * On is always based on a string comparison between inputValue and the param.
38519      * @param {Boolean/String} value - the value to set 
38520      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38521      */
38522     setValue : function(v,suppressEvent){
38523         
38524         
38525         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38526         //if(this.el && this.el.dom){
38527         //    this.el.dom.checked = this.checked;
38528         //    this.el.dom.defaultChecked = this.checked;
38529         //}
38530         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38531         //this.fireEvent("check", this, this.checked);
38532     },
38533     // private..
38534     setChecked : function(state,suppressEvent)
38535     {
38536         if (this.inSetChecked) {
38537             this.checked = state;
38538             return;
38539         }
38540         
38541     
38542         if(this.wrap){
38543             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38544         }
38545         this.checked = state;
38546         if(suppressEvent !== true){
38547             this.fireEvent('check', this, state);
38548         }
38549         this.inSetChecked = true;
38550         this.el.dom.value = state ? this.inputValue : this.valueOff;
38551         this.inSetChecked = false;
38552         
38553     },
38554     // handle setting of hidden value by some other method!!?!?
38555     setFromHidden: function()
38556     {
38557         if(!this.el){
38558             return;
38559         }
38560         //console.log("SET FROM HIDDEN");
38561         //alert('setFrom hidden');
38562         this.setValue(this.el.dom.value);
38563     },
38564     
38565     onDestroy : function()
38566     {
38567         if(this.viewEl){
38568             Roo.get(this.viewEl).remove();
38569         }
38570          
38571         Roo.form.Checkbox.superclass.onDestroy.call(this);
38572     }
38573
38574 });/*
38575  * Based on:
38576  * Ext JS Library 1.1.1
38577  * Copyright(c) 2006-2007, Ext JS, LLC.
38578  *
38579  * Originally Released Under LGPL - original licence link has changed is not relivant.
38580  *
38581  * Fork - LGPL
38582  * <script type="text/javascript">
38583  */
38584  
38585 /**
38586  * @class Roo.form.Radio
38587  * @extends Roo.form.Checkbox
38588  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38589  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38590  * @constructor
38591  * Creates a new Radio
38592  * @param {Object} config Configuration options
38593  */
38594 Roo.form.Radio = function(){
38595     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38596 };
38597 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38598     inputType: 'radio',
38599
38600     /**
38601      * If this radio is part of a group, it will return the selected value
38602      * @return {String}
38603      */
38604     getGroupValue : function(){
38605         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38606     }
38607 });//<script type="text/javascript">
38608
38609 /*
38610  * Ext JS Library 1.1.1
38611  * Copyright(c) 2006-2007, Ext JS, LLC.
38612  * licensing@extjs.com
38613  * 
38614  * http://www.extjs.com/license
38615  */
38616  
38617  /*
38618   * 
38619   * Known bugs:
38620   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38621   * - IE ? - no idea how much works there.
38622   * 
38623   * 
38624   * 
38625   */
38626  
38627
38628 /**
38629  * @class Ext.form.HtmlEditor
38630  * @extends Ext.form.Field
38631  * Provides a lightweight HTML Editor component.
38632  *
38633  * This has been tested on Fireforx / Chrome.. IE may not be so great..
38634  * 
38635  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38636  * supported by this editor.</b><br/><br/>
38637  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38638  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38639  */
38640 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38641       /**
38642      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38643      */
38644     toolbars : false,
38645     /**
38646      * @cfg {String} createLinkText The default text for the create link prompt
38647      */
38648     createLinkText : 'Please enter the URL for the link:',
38649     /**
38650      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38651      */
38652     defaultLinkValue : 'http:/'+'/',
38653    
38654      /**
38655      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38656      *                        Roo.resizable.
38657      */
38658     resizable : false,
38659      /**
38660      * @cfg {Number} height (in pixels)
38661      */   
38662     height: 300,
38663    /**
38664      * @cfg {Number} width (in pixels)
38665      */   
38666     width: 500,
38667     
38668     /**
38669      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38670      * 
38671      */
38672     stylesheets: false,
38673     
38674     // id of frame..
38675     frameId: false,
38676     
38677     // private properties
38678     validationEvent : false,
38679     deferHeight: true,
38680     initialized : false,
38681     activated : false,
38682     sourceEditMode : false,
38683     onFocus : Roo.emptyFn,
38684     iframePad:3,
38685     hideMode:'offsets',
38686     
38687     defaultAutoCreate : { // modified by initCompnoent..
38688         tag: "textarea",
38689         style:"width:500px;height:300px;",
38690         autocomplete: "off"
38691     },
38692
38693     // private
38694     initComponent : function(){
38695         this.addEvents({
38696             /**
38697              * @event initialize
38698              * Fires when the editor is fully initialized (including the iframe)
38699              * @param {HtmlEditor} this
38700              */
38701             initialize: true,
38702             /**
38703              * @event activate
38704              * Fires when the editor is first receives the focus. Any insertion must wait
38705              * until after this event.
38706              * @param {HtmlEditor} this
38707              */
38708             activate: true,
38709              /**
38710              * @event beforesync
38711              * Fires before the textarea is updated with content from the editor iframe. Return false
38712              * to cancel the sync.
38713              * @param {HtmlEditor} this
38714              * @param {String} html
38715              */
38716             beforesync: true,
38717              /**
38718              * @event beforepush
38719              * Fires before the iframe editor is updated with content from the textarea. Return false
38720              * to cancel the push.
38721              * @param {HtmlEditor} this
38722              * @param {String} html
38723              */
38724             beforepush: true,
38725              /**
38726              * @event sync
38727              * Fires when the textarea is updated with content from the editor iframe.
38728              * @param {HtmlEditor} this
38729              * @param {String} html
38730              */
38731             sync: true,
38732              /**
38733              * @event push
38734              * Fires when the iframe editor is updated with content from the textarea.
38735              * @param {HtmlEditor} this
38736              * @param {String} html
38737              */
38738             push: true,
38739              /**
38740              * @event editmodechange
38741              * Fires when the editor switches edit modes
38742              * @param {HtmlEditor} this
38743              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38744              */
38745             editmodechange: true,
38746             /**
38747              * @event editorevent
38748              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38749              * @param {HtmlEditor} this
38750              */
38751             editorevent: true
38752         });
38753         this.defaultAutoCreate =  {
38754             tag: "textarea",
38755             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38756             autocomplete: "off"
38757         };
38758     },
38759
38760     /**
38761      * Protected method that will not generally be called directly. It
38762      * is called when the editor creates its toolbar. Override this method if you need to
38763      * add custom toolbar buttons.
38764      * @param {HtmlEditor} editor
38765      */
38766     createToolbar : function(editor){
38767         if (!editor.toolbars || !editor.toolbars.length) {
38768             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38769         }
38770         
38771         for (var i =0 ; i < editor.toolbars.length;i++) {
38772             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38773             editor.toolbars[i].init(editor);
38774         }
38775          
38776         
38777     },
38778
38779     /**
38780      * Protected method that will not generally be called directly. It
38781      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38782      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38783      */
38784     getDocMarkup : function(){
38785         // body styles..
38786         var st = '';
38787         if (this.stylesheets === false) {
38788             
38789             Roo.get(document.head).select('style').each(function(node) {
38790                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38791             });
38792             
38793             Roo.get(document.head).select('link').each(function(node) { 
38794                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38795             });
38796             
38797         } else if (!this.stylesheets.length) {
38798                 // simple..
38799                 st = '<style type="text/css">' +
38800                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38801                    '</style>';
38802         } else {
38803             Roo.each(this.stylesheets, function(s) {
38804                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38805             });
38806             
38807         }
38808         
38809         return '<html><head>' + st  +
38810             //<style type="text/css">' +
38811             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38812             //'</style>' +
38813             ' </head><body></body></html>';
38814     },
38815
38816     // private
38817     onRender : function(ct, position)
38818     {
38819         var _t = this;
38820         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38821         this.el.dom.style.border = '0 none';
38822         this.el.dom.setAttribute('tabIndex', -1);
38823         this.el.addClass('x-hidden');
38824         if(Roo.isIE){ // fix IE 1px bogus margin
38825             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38826         }
38827         this.wrap = this.el.wrap({
38828             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38829         });
38830         
38831         if (this.resizable) {
38832             this.resizeEl = new Roo.Resizable(this.wrap, {
38833                 pinned : true,
38834                 wrap: true,
38835                 dynamic : true,
38836                 minHeight : this.height,
38837                 height: this.height,
38838                 handles : this.resizable,
38839                 width: this.width,
38840                 listeners : {
38841                     resize : function(r, w, h) {
38842                         _t.onResize(w,h); // -something
38843                     }
38844                 }
38845             });
38846             
38847         }
38848
38849         this.frameId = Roo.id();
38850         
38851         this.createToolbar(this);
38852         
38853       
38854         
38855         var iframe = this.wrap.createChild({
38856             tag: 'iframe',
38857             id: this.frameId,
38858             name: this.frameId,
38859             frameBorder : 'no',
38860             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38861         }, this.el
38862         );
38863         
38864        // console.log(iframe);
38865         //this.wrap.dom.appendChild(iframe);
38866
38867         this.iframe = iframe.dom;
38868
38869          this.assignDocWin();
38870         
38871         this.doc.designMode = 'on';
38872        
38873         this.doc.open();
38874         this.doc.write(this.getDocMarkup());
38875         this.doc.close();
38876
38877         
38878         var task = { // must defer to wait for browser to be ready
38879             run : function(){
38880                 //console.log("run task?" + this.doc.readyState);
38881                 this.assignDocWin();
38882                 if(this.doc.body || this.doc.readyState == 'complete'){
38883                     try {
38884                         this.doc.designMode="on";
38885                     } catch (e) {
38886                         return;
38887                     }
38888                     Roo.TaskMgr.stop(task);
38889                     this.initEditor.defer(10, this);
38890                 }
38891             },
38892             interval : 10,
38893             duration:10000,
38894             scope: this
38895         };
38896         Roo.TaskMgr.start(task);
38897
38898         if(!this.width){
38899             this.setSize(this.wrap.getSize());
38900         }
38901         if (this.resizeEl) {
38902             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38903             // should trigger onReize..
38904         }
38905     },
38906
38907     // private
38908     onResize : function(w, h)
38909     {
38910         //Roo.log('resize: ' +w + ',' + h );
38911         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38912         if(this.el && this.iframe){
38913             if(typeof w == 'number'){
38914                 var aw = w - this.wrap.getFrameWidth('lr');
38915                 this.el.setWidth(this.adjustWidth('textarea', aw));
38916                 this.iframe.style.width = aw + 'px';
38917             }
38918             if(typeof h == 'number'){
38919                 var tbh = 0;
38920                 for (var i =0; i < this.toolbars.length;i++) {
38921                     // fixme - ask toolbars for heights?
38922                     tbh += this.toolbars[i].tb.el.getHeight();
38923                     if (this.toolbars[i].footer) {
38924                         tbh += this.toolbars[i].footer.el.getHeight();
38925                     }
38926                 }
38927                 
38928                 
38929                 
38930                 
38931                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38932                 ah -= 5; // knock a few pixes off for look..
38933                 this.el.setHeight(this.adjustWidth('textarea', ah));
38934                 this.iframe.style.height = ah + 'px';
38935                 if(this.doc){
38936                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38937                 }
38938             }
38939         }
38940     },
38941
38942     /**
38943      * Toggles the editor between standard and source edit mode.
38944      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38945      */
38946     toggleSourceEdit : function(sourceEditMode){
38947         
38948         this.sourceEditMode = sourceEditMode === true;
38949         
38950         if(this.sourceEditMode){
38951           
38952             this.syncValue();
38953             this.iframe.className = 'x-hidden';
38954             this.el.removeClass('x-hidden');
38955             this.el.dom.removeAttribute('tabIndex');
38956             this.el.focus();
38957         }else{
38958              
38959             this.pushValue();
38960             this.iframe.className = '';
38961             this.el.addClass('x-hidden');
38962             this.el.dom.setAttribute('tabIndex', -1);
38963             this.deferFocus();
38964         }
38965         this.setSize(this.wrap.getSize());
38966         this.fireEvent('editmodechange', this, this.sourceEditMode);
38967     },
38968
38969     // private used internally
38970     createLink : function(){
38971         var url = prompt(this.createLinkText, this.defaultLinkValue);
38972         if(url && url != 'http:/'+'/'){
38973             this.relayCmd('createlink', url);
38974         }
38975     },
38976
38977     // private (for BoxComponent)
38978     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38979
38980     // private (for BoxComponent)
38981     getResizeEl : function(){
38982         return this.wrap;
38983     },
38984
38985     // private (for BoxComponent)
38986     getPositionEl : function(){
38987         return this.wrap;
38988     },
38989
38990     // private
38991     initEvents : function(){
38992         this.originalValue = this.getValue();
38993     },
38994
38995     /**
38996      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38997      * @method
38998      */
38999     markInvalid : Roo.emptyFn,
39000     /**
39001      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
39002      * @method
39003      */
39004     clearInvalid : Roo.emptyFn,
39005
39006     setValue : function(v){
39007         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
39008         this.pushValue();
39009     },
39010
39011     /**
39012      * Protected method that will not generally be called directly. If you need/want
39013      * custom HTML cleanup, this is the method you should override.
39014      * @param {String} html The HTML to be cleaned
39015      * return {String} The cleaned HTML
39016      */
39017     cleanHtml : function(html){
39018         html = String(html);
39019         if(html.length > 5){
39020             if(Roo.isSafari){ // strip safari nonsense
39021                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
39022             }
39023         }
39024         if(html == '&nbsp;'){
39025             html = '';
39026         }
39027         return html;
39028     },
39029
39030     /**
39031      * Protected method that will not generally be called directly. Syncs the contents
39032      * of the editor iframe with the textarea.
39033      */
39034     syncValue : function(){
39035         if(this.initialized){
39036             var bd = (this.doc.body || this.doc.documentElement);
39037             //this.cleanUpPaste(); -- this is done else where and causes havoc..
39038             var html = bd.innerHTML;
39039             if(Roo.isSafari){
39040                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39041                 var m = bs.match(/text-align:(.*?);/i);
39042                 if(m && m[1]){
39043                     html = '<div style="'+m[0]+'">' + html + '</div>';
39044                 }
39045             }
39046             html = this.cleanHtml(html);
39047             // fix up the special chars..
39048             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
39049                 return "&#"+b.charCodeAt()+";" 
39050             });
39051             if(this.fireEvent('beforesync', this, html) !== false){
39052                 this.el.dom.value = html;
39053                 this.fireEvent('sync', this, html);
39054             }
39055         }
39056     },
39057
39058     /**
39059      * Protected method that will not generally be called directly. Pushes the value of the textarea
39060      * into the iframe editor.
39061      */
39062     pushValue : function(){
39063         if(this.initialized){
39064             var v = this.el.dom.value;
39065             if(v.length < 1){
39066                 v = '&#160;';
39067             }
39068             
39069             if(this.fireEvent('beforepush', this, v) !== false){
39070                 var d = (this.doc.body || this.doc.documentElement);
39071                 d.innerHTML = v;
39072                 this.cleanUpPaste();
39073                 this.el.dom.value = d.innerHTML;
39074                 this.fireEvent('push', this, v);
39075             }
39076         }
39077     },
39078
39079     // private
39080     deferFocus : function(){
39081         this.focus.defer(10, this);
39082     },
39083
39084     // doc'ed in Field
39085     focus : function(){
39086         if(this.win && !this.sourceEditMode){
39087             this.win.focus();
39088         }else{
39089             this.el.focus();
39090         }
39091     },
39092     
39093     assignDocWin: function()
39094     {
39095         var iframe = this.iframe;
39096         
39097          if(Roo.isIE){
39098             this.doc = iframe.contentWindow.document;
39099             this.win = iframe.contentWindow;
39100         } else {
39101             if (!Roo.get(this.frameId)) {
39102                 return;
39103             }
39104             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39105             this.win = Roo.get(this.frameId).dom.contentWindow;
39106         }
39107     },
39108     
39109     // private
39110     initEditor : function(){
39111         //console.log("INIT EDITOR");
39112         this.assignDocWin();
39113         
39114         
39115         
39116         this.doc.designMode="on";
39117         this.doc.open();
39118         this.doc.write(this.getDocMarkup());
39119         this.doc.close();
39120         
39121         var dbody = (this.doc.body || this.doc.documentElement);
39122         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39123         // this copies styles from the containing element into thsi one..
39124         // not sure why we need all of this..
39125         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39126         ss['background-attachment'] = 'fixed'; // w3c
39127         dbody.bgProperties = 'fixed'; // ie
39128         Roo.DomHelper.applyStyles(dbody, ss);
39129         Roo.EventManager.on(this.doc, {
39130             //'mousedown': this.onEditorEvent,
39131             'mouseup': this.onEditorEvent,
39132             'dblclick': this.onEditorEvent,
39133             'click': this.onEditorEvent,
39134             'keyup': this.onEditorEvent,
39135             buffer:100,
39136             scope: this
39137         });
39138         if(Roo.isGecko){
39139             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39140         }
39141         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39142             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39143         }
39144         this.initialized = true;
39145
39146         this.fireEvent('initialize', this);
39147         this.pushValue();
39148     },
39149
39150     // private
39151     onDestroy : function(){
39152         
39153         
39154         
39155         if(this.rendered){
39156             
39157             for (var i =0; i < this.toolbars.length;i++) {
39158                 // fixme - ask toolbars for heights?
39159                 this.toolbars[i].onDestroy();
39160             }
39161             
39162             this.wrap.dom.innerHTML = '';
39163             this.wrap.remove();
39164         }
39165     },
39166
39167     // private
39168     onFirstFocus : function(){
39169         
39170         this.assignDocWin();
39171         
39172         
39173         this.activated = true;
39174         for (var i =0; i < this.toolbars.length;i++) {
39175             this.toolbars[i].onFirstFocus();
39176         }
39177        
39178         if(Roo.isGecko){ // prevent silly gecko errors
39179             this.win.focus();
39180             var s = this.win.getSelection();
39181             if(!s.focusNode || s.focusNode.nodeType != 3){
39182                 var r = s.getRangeAt(0);
39183                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39184                 r.collapse(true);
39185                 this.deferFocus();
39186             }
39187             try{
39188                 this.execCmd('useCSS', true);
39189                 this.execCmd('styleWithCSS', false);
39190             }catch(e){}
39191         }
39192         this.fireEvent('activate', this);
39193     },
39194
39195     // private
39196     adjustFont: function(btn){
39197         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39198         //if(Roo.isSafari){ // safari
39199         //    adjust *= 2;
39200        // }
39201         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39202         if(Roo.isSafari){ // safari
39203             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39204             v =  (v < 10) ? 10 : v;
39205             v =  (v > 48) ? 48 : v;
39206             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39207             
39208         }
39209         
39210         
39211         v = Math.max(1, v+adjust);
39212         
39213         this.execCmd('FontSize', v  );
39214     },
39215
39216     onEditorEvent : function(e){
39217         this.fireEvent('editorevent', this, e);
39218       //  this.updateToolbar();
39219         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
39220     },
39221
39222     insertTag : function(tg)
39223     {
39224         // could be a bit smarter... -> wrap the current selected tRoo..
39225         
39226         this.execCmd("formatblock",   tg);
39227         
39228     },
39229     
39230     insertText : function(txt)
39231     {
39232         
39233         
39234         range = this.createRange();
39235         range.deleteContents();
39236                //alert(Sender.getAttribute('label'));
39237                
39238         range.insertNode(this.doc.createTextNode(txt));
39239     } ,
39240     
39241     // private
39242     relayBtnCmd : function(btn){
39243         this.relayCmd(btn.cmd);
39244     },
39245
39246     /**
39247      * Executes a Midas editor command on the editor document and performs necessary focus and
39248      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39249      * @param {String} cmd The Midas command
39250      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39251      */
39252     relayCmd : function(cmd, value){
39253         this.win.focus();
39254         this.execCmd(cmd, value);
39255         this.fireEvent('editorevent', this);
39256         //this.updateToolbar();
39257         this.deferFocus();
39258     },
39259
39260     /**
39261      * Executes a Midas editor command directly on the editor document.
39262      * For visual commands, you should use {@link #relayCmd} instead.
39263      * <b>This should only be called after the editor is initialized.</b>
39264      * @param {String} cmd The Midas command
39265      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39266      */
39267     execCmd : function(cmd, value){
39268         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39269         this.syncValue();
39270     },
39271  
39272  
39273    
39274     /**
39275      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39276      * to insert tRoo.
39277      * @param {String} text | dom node.. 
39278      */
39279     insertAtCursor : function(text)
39280     {
39281         
39282         
39283         
39284         if(!this.activated){
39285             return;
39286         }
39287         /*
39288         if(Roo.isIE){
39289             this.win.focus();
39290             var r = this.doc.selection.createRange();
39291             if(r){
39292                 r.collapse(true);
39293                 r.pasteHTML(text);
39294                 this.syncValue();
39295                 this.deferFocus();
39296             
39297             }
39298             return;
39299         }
39300         */
39301         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39302             this.win.focus();
39303             
39304             
39305             // from jquery ui (MIT licenced)
39306             var range, node;
39307             var win = this.win;
39308             
39309             if (win.getSelection && win.getSelection().getRangeAt) {
39310                 range = win.getSelection().getRangeAt(0);
39311                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
39312                 range.insertNode(node);
39313             } else if (win.document.selection && win.document.selection.createRange) {
39314                 // no firefox support
39315                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39316                 win.document.selection.createRange().pasteHTML(txt);
39317             } else {
39318                 // no firefox support
39319                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
39320                 this.execCmd('InsertHTML', txt);
39321             } 
39322             
39323             this.syncValue();
39324             
39325             this.deferFocus();
39326         }
39327     },
39328  // private
39329     mozKeyPress : function(e){
39330         if(e.ctrlKey){
39331             var c = e.getCharCode(), cmd;
39332           
39333             if(c > 0){
39334                 c = String.fromCharCode(c).toLowerCase();
39335                 switch(c){
39336                     case 'b':
39337                         cmd = 'bold';
39338                         break;
39339                     case 'i':
39340                         cmd = 'italic';
39341                         break;
39342                     
39343                     case 'u':
39344                         cmd = 'underline';
39345                         break;
39346                     
39347                     case 'v':
39348                         this.cleanUpPaste.defer(100, this);
39349                         return;
39350                         
39351                 }
39352                 if(cmd){
39353                     this.win.focus();
39354                     this.execCmd(cmd);
39355                     this.deferFocus();
39356                     e.preventDefault();
39357                 }
39358                 
39359             }
39360         }
39361     },
39362
39363     // private
39364     fixKeys : function(){ // load time branching for fastest keydown performance
39365         if(Roo.isIE){
39366             return function(e){
39367                 var k = e.getKey(), r;
39368                 if(k == e.TAB){
39369                     e.stopEvent();
39370                     r = this.doc.selection.createRange();
39371                     if(r){
39372                         r.collapse(true);
39373                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39374                         this.deferFocus();
39375                     }
39376                     return;
39377                 }
39378                 
39379                 if(k == e.ENTER){
39380                     r = this.doc.selection.createRange();
39381                     if(r){
39382                         var target = r.parentElement();
39383                         if(!target || target.tagName.toLowerCase() != 'li'){
39384                             e.stopEvent();
39385                             r.pasteHTML('<br />');
39386                             r.collapse(false);
39387                             r.select();
39388                         }
39389                     }
39390                 }
39391                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39392                     this.cleanUpPaste.defer(100, this);
39393                     return;
39394                 }
39395                 
39396                 
39397             };
39398         }else if(Roo.isOpera){
39399             return function(e){
39400                 var k = e.getKey();
39401                 if(k == e.TAB){
39402                     e.stopEvent();
39403                     this.win.focus();
39404                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39405                     this.deferFocus();
39406                 }
39407                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39408                     this.cleanUpPaste.defer(100, this);
39409                     return;
39410                 }
39411                 
39412             };
39413         }else if(Roo.isSafari){
39414             return function(e){
39415                 var k = e.getKey();
39416                 
39417                 if(k == e.TAB){
39418                     e.stopEvent();
39419                     this.execCmd('InsertText','\t');
39420                     this.deferFocus();
39421                     return;
39422                 }
39423                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39424                     this.cleanUpPaste.defer(100, this);
39425                     return;
39426                 }
39427                 
39428              };
39429         }
39430     }(),
39431     
39432     getAllAncestors: function()
39433     {
39434         var p = this.getSelectedNode();
39435         var a = [];
39436         if (!p) {
39437             a.push(p); // push blank onto stack..
39438             p = this.getParentElement();
39439         }
39440         
39441         
39442         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39443             a.push(p);
39444             p = p.parentNode;
39445         }
39446         a.push(this.doc.body);
39447         return a;
39448     },
39449     lastSel : false,
39450     lastSelNode : false,
39451     
39452     
39453     getSelection : function() 
39454     {
39455         this.assignDocWin();
39456         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39457     },
39458     
39459     getSelectedNode: function() 
39460     {
39461         // this may only work on Gecko!!!
39462         
39463         // should we cache this!!!!
39464         
39465         
39466         
39467          
39468         var range = this.createRange(this.getSelection()).cloneRange();
39469         
39470         if (Roo.isIE) {
39471             var parent = range.parentElement();
39472             while (true) {
39473                 var testRange = range.duplicate();
39474                 testRange.moveToElementText(parent);
39475                 if (testRange.inRange(range)) {
39476                     break;
39477                 }
39478                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39479                     break;
39480                 }
39481                 parent = parent.parentElement;
39482             }
39483             return parent;
39484         }
39485         
39486         // is ancestor a text element.
39487         var ac =  range.commonAncestorContainer;
39488         if (ac.nodeType == 3) {
39489             ac = ac.parentNode;
39490         }
39491         
39492         var ar = ac.childNodes;
39493          
39494         var nodes = [];
39495         var other_nodes = [];
39496         var has_other_nodes = false;
39497         for (var i=0;i<ar.length;i++) {
39498             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39499                 continue;
39500             }
39501             // fullly contained node.
39502             
39503             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39504                 nodes.push(ar[i]);
39505                 continue;
39506             }
39507             
39508             // probably selected..
39509             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39510                 other_nodes.push(ar[i]);
39511                 continue;
39512             }
39513             // outer..
39514             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39515                 continue;
39516             }
39517             
39518             
39519             has_other_nodes = true;
39520         }
39521         if (!nodes.length && other_nodes.length) {
39522             nodes= other_nodes;
39523         }
39524         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39525             return false;
39526         }
39527         
39528         return nodes[0];
39529     },
39530     createRange: function(sel)
39531     {
39532         // this has strange effects when using with 
39533         // top toolbar - not sure if it's a great idea.
39534         //this.editor.contentWindow.focus();
39535         if (typeof sel != "undefined") {
39536             try {
39537                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39538             } catch(e) {
39539                 return this.doc.createRange();
39540             }
39541         } else {
39542             return this.doc.createRange();
39543         }
39544     },
39545     getParentElement: function()
39546     {
39547         
39548         this.assignDocWin();
39549         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39550         
39551         var range = this.createRange(sel);
39552          
39553         try {
39554             var p = range.commonAncestorContainer;
39555             while (p.nodeType == 3) { // text node
39556                 p = p.parentNode;
39557             }
39558             return p;
39559         } catch (e) {
39560             return null;
39561         }
39562     
39563     },
39564     /***
39565      *
39566      * Range intersection.. the hard stuff...
39567      *  '-1' = before
39568      *  '0' = hits..
39569      *  '1' = after.
39570      *         [ -- selected range --- ]
39571      *   [fail]                        [fail]
39572      *
39573      *    basically..
39574      *      if end is before start or  hits it. fail.
39575      *      if start is after end or hits it fail.
39576      *
39577      *   if either hits (but other is outside. - then it's not 
39578      *   
39579      *    
39580      **/
39581     
39582     
39583     // @see http://www.thismuchiknow.co.uk/?p=64.
39584     rangeIntersectsNode : function(range, node)
39585     {
39586         var nodeRange = node.ownerDocument.createRange();
39587         try {
39588             nodeRange.selectNode(node);
39589         } catch (e) {
39590             nodeRange.selectNodeContents(node);
39591         }
39592     
39593         var rangeStartRange = range.cloneRange();
39594         rangeStartRange.collapse(true);
39595     
39596         var rangeEndRange = range.cloneRange();
39597         rangeEndRange.collapse(false);
39598     
39599         var nodeStartRange = nodeRange.cloneRange();
39600         nodeStartRange.collapse(true);
39601     
39602         var nodeEndRange = nodeRange.cloneRange();
39603         nodeEndRange.collapse(false);
39604     
39605         return rangeStartRange.compareBoundaryPoints(
39606                  Range.START_TO_START, nodeEndRange) == -1 &&
39607                rangeEndRange.compareBoundaryPoints(
39608                  Range.START_TO_START, nodeStartRange) == 1;
39609         
39610          
39611     },
39612     rangeCompareNode : function(range, node)
39613     {
39614         var nodeRange = node.ownerDocument.createRange();
39615         try {
39616             nodeRange.selectNode(node);
39617         } catch (e) {
39618             nodeRange.selectNodeContents(node);
39619         }
39620         
39621         
39622         range.collapse(true);
39623     
39624         nodeRange.collapse(true);
39625      
39626         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39627         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39628          
39629         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39630         
39631         var nodeIsBefore   =  ss == 1;
39632         var nodeIsAfter    = ee == -1;
39633         
39634         if (nodeIsBefore && nodeIsAfter)
39635             return 0; // outer
39636         if (!nodeIsBefore && nodeIsAfter)
39637             return 1; //right trailed.
39638         
39639         if (nodeIsBefore && !nodeIsAfter)
39640             return 2;  // left trailed.
39641         // fully contined.
39642         return 3;
39643     },
39644
39645     // private? - in a new class?
39646     cleanUpPaste :  function()
39647     {
39648         // cleans up the whole document..
39649          Roo.log('cleanuppaste');
39650         this.cleanUpChildren(this.doc.body);
39651         var clean = this.cleanWordChars(this.doc.body.innerHTML);
39652         if (clean != this.doc.body.innerHTML) {
39653             this.doc.body.innerHTML = clean;
39654         }
39655         
39656     },
39657     
39658     cleanWordChars : function(input) {
39659         var he = Roo.form.HtmlEditor;
39660     
39661         var output = input;
39662         Roo.each(he.swapCodes, function(sw) { 
39663         
39664             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
39665             output = output.replace(swapper, sw[1]);
39666         });
39667         return output;
39668     },
39669     
39670     
39671     cleanUpChildren : function (n)
39672     {
39673         if (!n.childNodes.length) {
39674             return;
39675         }
39676         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39677            this.cleanUpChild(n.childNodes[i]);
39678         }
39679     },
39680     
39681     
39682         
39683     
39684     cleanUpChild : function (node)
39685     {
39686         //console.log(node);
39687         if (node.nodeName == "#text") {
39688             // clean up silly Windows -- stuff?
39689             return; 
39690         }
39691         if (node.nodeName == "#comment") {
39692             node.parentNode.removeChild(node);
39693             // clean up silly Windows -- stuff?
39694             return; 
39695         }
39696         
39697         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39698             // remove node.
39699             node.parentNode.removeChild(node);
39700             return;
39701             
39702         }
39703         
39704         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
39705         
39706         // remove <a name=....> as rendering on yahoo mailer is bored with this.
39707         
39708         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
39709             remove_keep_children = true;
39710         }
39711         
39712         if (remove_keep_children) {
39713             this.cleanUpChildren(node);
39714             // inserts everything just before this node...
39715             while (node.childNodes.length) {
39716                 var cn = node.childNodes[0];
39717                 node.removeChild(cn);
39718                 node.parentNode.insertBefore(cn, node);
39719             }
39720             node.parentNode.removeChild(node);
39721             return;
39722         }
39723         
39724         if (!node.attributes || !node.attributes.length) {
39725             this.cleanUpChildren(node);
39726             return;
39727         }
39728         
39729         function cleanAttr(n,v)
39730         {
39731             
39732             if (v.match(/^\./) || v.match(/^\//)) {
39733                 return;
39734             }
39735             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39736                 return;
39737             }
39738             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39739             node.removeAttribute(n);
39740             
39741         }
39742         
39743         function cleanStyle(n,v)
39744         {
39745             if (v.match(/expression/)) { //XSS?? should we even bother..
39746                 node.removeAttribute(n);
39747                 return;
39748             }
39749             
39750             
39751             var parts = v.split(/;/);
39752             Roo.each(parts, function(p) {
39753                 p = p.replace(/\s+/g,'');
39754                 if (!p.length) {
39755                     return true;
39756                 }
39757                 var l = p.split(':').shift().replace(/\s+/g,'');
39758                 
39759                 // only allow 'c whitelisted system attributes'
39760                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39761                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39762                     node.removeAttribute(n);
39763                     return false;
39764                 }
39765                 return true;
39766             });
39767             
39768             
39769         }
39770         
39771         
39772         for (var i = node.attributes.length-1; i > -1 ; i--) {
39773             var a = node.attributes[i];
39774             //console.log(a);
39775             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39776                 node.removeAttribute(a.name);
39777                 return;
39778             }
39779             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39780                 cleanAttr(a.name,a.value); // fixme..
39781                 return;
39782             }
39783             if (a.name == 'style') {
39784                 cleanStyle(a.name,a.value);
39785             }
39786             /// clean up MS crap..
39787             // tecnically this should be a list of valid class'es..
39788             
39789             
39790             if (a.name == 'class') {
39791                 if (a.value.match(/^Mso/)) {
39792                     node.className = '';
39793                 }
39794                 
39795                 if (a.value.match(/body/)) {
39796                     node.className = '';
39797                 }
39798             }
39799             
39800             // style cleanup!?
39801             // class cleanup?
39802             
39803         }
39804         
39805         
39806         this.cleanUpChildren(node);
39807         
39808         
39809     }
39810     
39811     
39812     // hide stuff that is not compatible
39813     /**
39814      * @event blur
39815      * @hide
39816      */
39817     /**
39818      * @event change
39819      * @hide
39820      */
39821     /**
39822      * @event focus
39823      * @hide
39824      */
39825     /**
39826      * @event specialkey
39827      * @hide
39828      */
39829     /**
39830      * @cfg {String} fieldClass @hide
39831      */
39832     /**
39833      * @cfg {String} focusClass @hide
39834      */
39835     /**
39836      * @cfg {String} autoCreate @hide
39837      */
39838     /**
39839      * @cfg {String} inputType @hide
39840      */
39841     /**
39842      * @cfg {String} invalidClass @hide
39843      */
39844     /**
39845      * @cfg {String} invalidText @hide
39846      */
39847     /**
39848      * @cfg {String} msgFx @hide
39849      */
39850     /**
39851      * @cfg {String} validateOnBlur @hide
39852      */
39853 });
39854
39855 Roo.form.HtmlEditor.white = [
39856         'area', 'br', 'img', 'input', 'hr', 'wbr',
39857         
39858        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39859        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39860        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39861        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39862        'table',   'ul',         'xmp', 
39863        
39864        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39865       'thead',   'tr', 
39866      
39867       'dir', 'menu', 'ol', 'ul', 'dl',
39868        
39869       'embed',  'object'
39870 ];
39871
39872
39873 Roo.form.HtmlEditor.black = [
39874     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39875         'applet', // 
39876         'base',   'basefont', 'bgsound', 'blink',  'body', 
39877         'frame',  'frameset', 'head',    'html',   'ilayer', 
39878         'iframe', 'layer',  'link',     'meta',    'object',   
39879         'script', 'style' ,'title',  'xml' // clean later..
39880 ];
39881 Roo.form.HtmlEditor.clean = [
39882     'script', 'style', 'title', 'xml'
39883 ];
39884 Roo.form.HtmlEditor.remove = [
39885     'font'
39886 ];
39887 // attributes..
39888
39889 Roo.form.HtmlEditor.ablack = [
39890     'on'
39891 ];
39892     
39893 Roo.form.HtmlEditor.aclean = [ 
39894     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39895 ];
39896
39897 // protocols..
39898 Roo.form.HtmlEditor.pwhite= [
39899         'http',  'https',  'mailto'
39900 ];
39901
39902 // white listed style attributes.
39903 Roo.form.HtmlEditor.cwhite= [
39904         'text-align',
39905         'font-size'
39906 ];
39907
39908
39909 Roo.form.HtmlEditor.swapCodes   =[ 
39910     [    8211, "--" ], 
39911     [    8212, "--" ], 
39912     [    8216,  "'" ],  
39913     [    8217, "'" ],  
39914     [    8220, '"' ],  
39915     [    8221, '"' ],  
39916     [    8226, "*" ],  
39917     [    8230, "..." ]
39918 ]; 
39919
39920     // <script type="text/javascript">
39921 /*
39922  * Based on
39923  * Ext JS Library 1.1.1
39924  * Copyright(c) 2006-2007, Ext JS, LLC.
39925  *  
39926  
39927  */
39928
39929 /**
39930  * @class Roo.form.HtmlEditorToolbar1
39931  * Basic Toolbar
39932  * 
39933  * Usage:
39934  *
39935  new Roo.form.HtmlEditor({
39936     ....
39937     toolbars : [
39938         new Roo.form.HtmlEditorToolbar1({
39939             disable : { fonts: 1 , format: 1, ..., ... , ...],
39940             btns : [ .... ]
39941         })
39942     }
39943      
39944  * 
39945  * @cfg {Object} disable List of elements to disable..
39946  * @cfg {Array} btns List of additional buttons.
39947  * 
39948  * 
39949  * NEEDS Extra CSS? 
39950  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39951  */
39952  
39953 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39954 {
39955     
39956     Roo.apply(this, config);
39957     
39958     // default disabled, based on 'good practice'..
39959     this.disable = this.disable || {};
39960     Roo.applyIf(this.disable, {
39961         fontSize : true,
39962         colors : true,
39963         specialElements : true
39964     });
39965     
39966     
39967     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39968     // dont call parent... till later.
39969 }
39970
39971 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39972     
39973     tb: false,
39974     
39975     rendered: false,
39976     
39977     editor : false,
39978     /**
39979      * @cfg {Object} disable  List of toolbar elements to disable
39980          
39981      */
39982     disable : false,
39983       /**
39984      * @cfg {Array} fontFamilies An array of available font families
39985      */
39986     fontFamilies : [
39987         'Arial',
39988         'Courier New',
39989         'Tahoma',
39990         'Times New Roman',
39991         'Verdana'
39992     ],
39993     
39994     specialChars : [
39995            "&#169;",
39996           "&#174;",     
39997           "&#8482;",    
39998           "&#163;" ,    
39999          // "&#8212;",    
40000           "&#8230;",    
40001           "&#247;" ,    
40002         //  "&#225;" ,     ?? a acute?
40003            "&#8364;"    , //Euro
40004        //   "&#8220;"    ,
40005         //  "&#8221;"    ,
40006         //  "&#8226;"    ,
40007           "&#176;"  //   , // degrees
40008
40009          // "&#233;"     , // e ecute
40010          // "&#250;"     , // u ecute?
40011     ],
40012     
40013     specialElements : [
40014         {
40015             text: "Insert Table",
40016             xtype: 'MenuItem',
40017             xns : Roo.Menu,
40018             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
40019                 
40020         },
40021         {    
40022             text: "Insert Image",
40023             xtype: 'MenuItem',
40024             xns : Roo.Menu,
40025             ihtml : '<img src="about:blank"/>'
40026             
40027         }
40028         
40029          
40030     ],
40031     
40032     
40033     inputElements : [ 
40034             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
40035             "input:submit", "input:button", "select", "textarea", "label" ],
40036     formats : [
40037         ["p"] ,  
40038         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
40039         ["pre"],[ "code"], 
40040         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
40041     ],
40042      /**
40043      * @cfg {String} defaultFont default font to use.
40044      */
40045     defaultFont: 'tahoma',
40046    
40047     fontSelect : false,
40048     
40049     
40050     formatCombo : false,
40051     
40052     init : function(editor)
40053     {
40054         this.editor = editor;
40055         
40056         
40057         var fid = editor.frameId;
40058         var etb = this;
40059         function btn(id, toggle, handler){
40060             var xid = fid + '-'+ id ;
40061             return {
40062                 id : xid,
40063                 cmd : id,
40064                 cls : 'x-btn-icon x-edit-'+id,
40065                 enableToggle:toggle !== false,
40066                 scope: editor, // was editor...
40067                 handler:handler||editor.relayBtnCmd,
40068                 clickEvent:'mousedown',
40069                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40070                 tabIndex:-1
40071             };
40072         }
40073         
40074         
40075         
40076         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
40077         this.tb = tb;
40078          // stop form submits
40079         tb.el.on('click', function(e){
40080             e.preventDefault(); // what does this do?
40081         });
40082
40083         if(!this.disable.font && !Roo.isSafari){
40084             /* why no safari for fonts
40085             editor.fontSelect = tb.el.createChild({
40086                 tag:'select',
40087                 tabIndex: -1,
40088                 cls:'x-font-select',
40089                 html: editor.createFontOptions()
40090             });
40091             editor.fontSelect.on('change', function(){
40092                 var font = editor.fontSelect.dom.value;
40093                 editor.relayCmd('fontname', font);
40094                 editor.deferFocus();
40095             }, editor);
40096             tb.add(
40097                 editor.fontSelect.dom,
40098                 '-'
40099             );
40100             */
40101         };
40102         if(!this.disable.formats){
40103             this.formatCombo = new Roo.form.ComboBox({
40104                 store: new Roo.data.SimpleStore({
40105                     id : 'tag',
40106                     fields: ['tag'],
40107                     data : this.formats // from states.js
40108                 }),
40109                 blockFocus : true,
40110                 //autoCreate : {tag: "div",  size: "20"},
40111                 displayField:'tag',
40112                 typeAhead: false,
40113                 mode: 'local',
40114                 editable : false,
40115                 triggerAction: 'all',
40116                 emptyText:'Add tag',
40117                 selectOnFocus:true,
40118                 width:135,
40119                 listeners : {
40120                     'select': function(c, r, i) {
40121                         editor.insertTag(r.get('tag'));
40122                         editor.focus();
40123                     }
40124                 }
40125
40126             });
40127             tb.addField(this.formatCombo);
40128             
40129         }
40130         
40131         if(!this.disable.format){
40132             tb.add(
40133                 btn('bold'),
40134                 btn('italic'),
40135                 btn('underline')
40136             );
40137         };
40138         if(!this.disable.fontSize){
40139             tb.add(
40140                 '-',
40141                 
40142                 
40143                 btn('increasefontsize', false, editor.adjustFont),
40144                 btn('decreasefontsize', false, editor.adjustFont)
40145             );
40146         };
40147         
40148         
40149         if(!this.disable.colors){
40150             tb.add(
40151                 '-', {
40152                     id:editor.frameId +'-forecolor',
40153                     cls:'x-btn-icon x-edit-forecolor',
40154                     clickEvent:'mousedown',
40155                     tooltip: this.buttonTips['forecolor'] || undefined,
40156                     tabIndex:-1,
40157                     menu : new Roo.menu.ColorMenu({
40158                         allowReselect: true,
40159                         focus: Roo.emptyFn,
40160                         value:'000000',
40161                         plain:true,
40162                         selectHandler: function(cp, color){
40163                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40164                             editor.deferFocus();
40165                         },
40166                         scope: editor,
40167                         clickEvent:'mousedown'
40168                     })
40169                 }, {
40170                     id:editor.frameId +'backcolor',
40171                     cls:'x-btn-icon x-edit-backcolor',
40172                     clickEvent:'mousedown',
40173                     tooltip: this.buttonTips['backcolor'] || undefined,
40174                     tabIndex:-1,
40175                     menu : new Roo.menu.ColorMenu({
40176                         focus: Roo.emptyFn,
40177                         value:'FFFFFF',
40178                         plain:true,
40179                         allowReselect: true,
40180                         selectHandler: function(cp, color){
40181                             if(Roo.isGecko){
40182                                 editor.execCmd('useCSS', false);
40183                                 editor.execCmd('hilitecolor', color);
40184                                 editor.execCmd('useCSS', true);
40185                                 editor.deferFocus();
40186                             }else{
40187                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40188                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40189                                 editor.deferFocus();
40190                             }
40191                         },
40192                         scope:editor,
40193                         clickEvent:'mousedown'
40194                     })
40195                 }
40196             );
40197         };
40198         // now add all the items...
40199         
40200
40201         if(!this.disable.alignments){
40202             tb.add(
40203                 '-',
40204                 btn('justifyleft'),
40205                 btn('justifycenter'),
40206                 btn('justifyright')
40207             );
40208         };
40209
40210         //if(!Roo.isSafari){
40211             if(!this.disable.links){
40212                 tb.add(
40213                     '-',
40214                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40215                 );
40216             };
40217
40218             if(!this.disable.lists){
40219                 tb.add(
40220                     '-',
40221                     btn('insertorderedlist'),
40222                     btn('insertunorderedlist')
40223                 );
40224             }
40225             if(!this.disable.sourceEdit){
40226                 tb.add(
40227                     '-',
40228                     btn('sourceedit', true, function(btn){
40229                         this.toggleSourceEdit(btn.pressed);
40230                     })
40231                 );
40232             }
40233         //}
40234         
40235         var smenu = { };
40236         // special menu.. - needs to be tidied up..
40237         if (!this.disable.special) {
40238             smenu = {
40239                 text: "&#169;",
40240                 cls: 'x-edit-none',
40241                 
40242                 menu : {
40243                     items : []
40244                 }
40245             };
40246             for (var i =0; i < this.specialChars.length; i++) {
40247                 smenu.menu.items.push({
40248                     
40249                     html: this.specialChars[i],
40250                     handler: function(a,b) {
40251                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40252                         //editor.insertAtCursor(a.html);
40253                         
40254                     },
40255                     tabIndex:-1
40256                 });
40257             }
40258             
40259             
40260             tb.add(smenu);
40261             
40262             
40263         }
40264          
40265         if (!this.disable.specialElements) {
40266             var semenu = {
40267                 text: "Other;",
40268                 cls: 'x-edit-none',
40269                 menu : {
40270                     items : []
40271                 }
40272             };
40273             for (var i =0; i < this.specialElements.length; i++) {
40274                 semenu.menu.items.push(
40275                     Roo.apply({ 
40276                         handler: function(a,b) {
40277                             editor.insertAtCursor(this.ihtml);
40278                         }
40279                     }, this.specialElements[i])
40280                 );
40281                     
40282             }
40283             
40284             tb.add(semenu);
40285             
40286             
40287         }
40288          
40289         
40290         if (this.btns) {
40291             for(var i =0; i< this.btns.length;i++) {
40292                 var b = this.btns[i];
40293                 b.cls =  'x-edit-none';
40294                 b.scope = editor;
40295                 tb.add(b);
40296             }
40297         
40298         }
40299         
40300         
40301         
40302         // disable everything...
40303         
40304         this.tb.items.each(function(item){
40305            if(item.id != editor.frameId+ '-sourceedit'){
40306                 item.disable();
40307             }
40308         });
40309         this.rendered = true;
40310         
40311         // the all the btns;
40312         editor.on('editorevent', this.updateToolbar, this);
40313         // other toolbars need to implement this..
40314         //editor.on('editmodechange', this.updateToolbar, this);
40315     },
40316     
40317     
40318     
40319     /**
40320      * Protected method that will not generally be called directly. It triggers
40321      * a toolbar update by reading the markup state of the current selection in the editor.
40322      */
40323     updateToolbar: function(){
40324
40325         if(!this.editor.activated){
40326             this.editor.onFirstFocus();
40327             return;
40328         }
40329
40330         var btns = this.tb.items.map, 
40331             doc = this.editor.doc,
40332             frameId = this.editor.frameId;
40333
40334         if(!this.disable.font && !Roo.isSafari){
40335             /*
40336             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40337             if(name != this.fontSelect.dom.value){
40338                 this.fontSelect.dom.value = name;
40339             }
40340             */
40341         }
40342         if(!this.disable.format){
40343             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40344             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40345             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40346         }
40347         if(!this.disable.alignments){
40348             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40349             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40350             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40351         }
40352         if(!Roo.isSafari && !this.disable.lists){
40353             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40354             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40355         }
40356         
40357         var ans = this.editor.getAllAncestors();
40358         if (this.formatCombo) {
40359             
40360             
40361             var store = this.formatCombo.store;
40362             this.formatCombo.setValue("");
40363             for (var i =0; i < ans.length;i++) {
40364                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40365                     // select it..
40366                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40367                     break;
40368                 }
40369             }
40370         }
40371         
40372         
40373         
40374         // hides menus... - so this cant be on a menu...
40375         Roo.menu.MenuMgr.hideAll();
40376
40377         //this.editorsyncValue();
40378     },
40379    
40380     
40381     createFontOptions : function(){
40382         var buf = [], fs = this.fontFamilies, ff, lc;
40383         for(var i = 0, len = fs.length; i< len; i++){
40384             ff = fs[i];
40385             lc = ff.toLowerCase();
40386             buf.push(
40387                 '<option value="',lc,'" style="font-family:',ff,';"',
40388                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40389                     ff,
40390                 '</option>'
40391             );
40392         }
40393         return buf.join('');
40394     },
40395     
40396     toggleSourceEdit : function(sourceEditMode){
40397         if(sourceEditMode === undefined){
40398             sourceEditMode = !this.sourceEditMode;
40399         }
40400         this.sourceEditMode = sourceEditMode === true;
40401         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40402         // just toggle the button?
40403         if(btn.pressed !== this.editor.sourceEditMode){
40404             btn.toggle(this.editor.sourceEditMode);
40405             return;
40406         }
40407         
40408         if(this.sourceEditMode){
40409             this.tb.items.each(function(item){
40410                 if(item.cmd != 'sourceedit'){
40411                     item.disable();
40412                 }
40413             });
40414           
40415         }else{
40416             if(this.initialized){
40417                 this.tb.items.each(function(item){
40418                     item.enable();
40419                 });
40420             }
40421             
40422         }
40423         // tell the editor that it's been pressed..
40424         this.editor.toggleSourceEdit(sourceEditMode);
40425        
40426     },
40427      /**
40428      * Object collection of toolbar tooltips for the buttons in the editor. The key
40429      * is the command id associated with that button and the value is a valid QuickTips object.
40430      * For example:
40431 <pre><code>
40432 {
40433     bold : {
40434         title: 'Bold (Ctrl+B)',
40435         text: 'Make the selected text bold.',
40436         cls: 'x-html-editor-tip'
40437     },
40438     italic : {
40439         title: 'Italic (Ctrl+I)',
40440         text: 'Make the selected text italic.',
40441         cls: 'x-html-editor-tip'
40442     },
40443     ...
40444 </code></pre>
40445     * @type Object
40446      */
40447     buttonTips : {
40448         bold : {
40449             title: 'Bold (Ctrl+B)',
40450             text: 'Make the selected text bold.',
40451             cls: 'x-html-editor-tip'
40452         },
40453         italic : {
40454             title: 'Italic (Ctrl+I)',
40455             text: 'Make the selected text italic.',
40456             cls: 'x-html-editor-tip'
40457         },
40458         underline : {
40459             title: 'Underline (Ctrl+U)',
40460             text: 'Underline the selected text.',
40461             cls: 'x-html-editor-tip'
40462         },
40463         increasefontsize : {
40464             title: 'Grow Text',
40465             text: 'Increase the font size.',
40466             cls: 'x-html-editor-tip'
40467         },
40468         decreasefontsize : {
40469             title: 'Shrink Text',
40470             text: 'Decrease the font size.',
40471             cls: 'x-html-editor-tip'
40472         },
40473         backcolor : {
40474             title: 'Text Highlight Color',
40475             text: 'Change the background color of the selected text.',
40476             cls: 'x-html-editor-tip'
40477         },
40478         forecolor : {
40479             title: 'Font Color',
40480             text: 'Change the color of the selected text.',
40481             cls: 'x-html-editor-tip'
40482         },
40483         justifyleft : {
40484             title: 'Align Text Left',
40485             text: 'Align text to the left.',
40486             cls: 'x-html-editor-tip'
40487         },
40488         justifycenter : {
40489             title: 'Center Text',
40490             text: 'Center text in the editor.',
40491             cls: 'x-html-editor-tip'
40492         },
40493         justifyright : {
40494             title: 'Align Text Right',
40495             text: 'Align text to the right.',
40496             cls: 'x-html-editor-tip'
40497         },
40498         insertunorderedlist : {
40499             title: 'Bullet List',
40500             text: 'Start a bulleted list.',
40501             cls: 'x-html-editor-tip'
40502         },
40503         insertorderedlist : {
40504             title: 'Numbered List',
40505             text: 'Start a numbered list.',
40506             cls: 'x-html-editor-tip'
40507         },
40508         createlink : {
40509             title: 'Hyperlink',
40510             text: 'Make the selected text a hyperlink.',
40511             cls: 'x-html-editor-tip'
40512         },
40513         sourceedit : {
40514             title: 'Source Edit',
40515             text: 'Switch to source editing mode.',
40516             cls: 'x-html-editor-tip'
40517         }
40518     },
40519     // private
40520     onDestroy : function(){
40521         if(this.rendered){
40522             
40523             this.tb.items.each(function(item){
40524                 if(item.menu){
40525                     item.menu.removeAll();
40526                     if(item.menu.el){
40527                         item.menu.el.destroy();
40528                     }
40529                 }
40530                 item.destroy();
40531             });
40532              
40533         }
40534     },
40535     onFirstFocus: function() {
40536         this.tb.items.each(function(item){
40537            item.enable();
40538         });
40539     }
40540 });
40541
40542
40543
40544
40545 // <script type="text/javascript">
40546 /*
40547  * Based on
40548  * Ext JS Library 1.1.1
40549  * Copyright(c) 2006-2007, Ext JS, LLC.
40550  *  
40551  
40552  */
40553
40554  
40555 /**
40556  * @class Roo.form.HtmlEditor.ToolbarContext
40557  * Context Toolbar
40558  * 
40559  * Usage:
40560  *
40561  new Roo.form.HtmlEditor({
40562     ....
40563     toolbars : [
40564         { xtype: 'ToolbarStandard', styles : {} }
40565         { xtype: 'ToolbarContext', disable : {} }
40566     ]
40567 })
40568
40569      
40570  * 
40571  * @config : {Object} disable List of elements to disable.. (not done yet.)
40572  * @config : {Object} styles  Map of styles available.
40573  * 
40574  */
40575
40576 Roo.form.HtmlEditor.ToolbarContext = function(config)
40577 {
40578     
40579     Roo.apply(this, config);
40580     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40581     // dont call parent... till later.
40582     this.styles = this.styles || {};
40583 }
40584 Roo.form.HtmlEditor.ToolbarContext.types = {
40585     'IMG' : {
40586         width : {
40587             title: "Width",
40588             width: 40
40589         },
40590         height:  {
40591             title: "Height",
40592             width: 40
40593         },
40594         align: {
40595             title: "Align",
40596             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40597             width : 80
40598             
40599         },
40600         border: {
40601             title: "Border",
40602             width: 40
40603         },
40604         alt: {
40605             title: "Alt",
40606             width: 120
40607         },
40608         src : {
40609             title: "Src",
40610             width: 220
40611         }
40612         
40613     },
40614     'A' : {
40615         name : {
40616             title: "Name",
40617             width: 50
40618         },
40619         href:  {
40620             title: "Href",
40621             width: 220
40622         } // border?
40623         
40624     },
40625     'TABLE' : {
40626         rows : {
40627             title: "Rows",
40628             width: 20
40629         },
40630         cols : {
40631             title: "Cols",
40632             width: 20
40633         },
40634         width : {
40635             title: "Width",
40636             width: 40
40637         },
40638         height : {
40639             title: "Height",
40640             width: 40
40641         },
40642         border : {
40643             title: "Border",
40644             width: 20
40645         }
40646     },
40647     'TD' : {
40648         width : {
40649             title: "Width",
40650             width: 40
40651         },
40652         height : {
40653             title: "Height",
40654             width: 40
40655         },   
40656         align: {
40657             title: "Align",
40658             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40659             width: 80
40660         },
40661         valign: {
40662             title: "Valign",
40663             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40664             width: 80
40665         },
40666         colspan: {
40667             title: "Colspan",
40668             width: 20
40669             
40670         }
40671     },
40672     'INPUT' : {
40673         name : {
40674             title: "name",
40675             width: 120
40676         },
40677         value : {
40678             title: "Value",
40679             width: 120
40680         },
40681         width : {
40682             title: "Width",
40683             width: 40
40684         }
40685     },
40686     'LABEL' : {
40687         'for' : {
40688             title: "For",
40689             width: 120
40690         }
40691     },
40692     'TEXTAREA' : {
40693           name : {
40694             title: "name",
40695             width: 120
40696         },
40697         rows : {
40698             title: "Rows",
40699             width: 20
40700         },
40701         cols : {
40702             title: "Cols",
40703             width: 20
40704         }
40705     },
40706     'SELECT' : {
40707         name : {
40708             title: "name",
40709             width: 120
40710         },
40711         selectoptions : {
40712             title: "Options",
40713             width: 200
40714         }
40715     },
40716     
40717     // should we really allow this??
40718     // should this just be 
40719     'BODY' : {
40720         title : {
40721             title: "title",
40722             width: 200,
40723             disabled : true
40724         }
40725     },
40726     '*' : {
40727         // empty..
40728     }
40729 };
40730
40731
40732
40733 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40734     
40735     tb: false,
40736     
40737     rendered: false,
40738     
40739     editor : false,
40740     /**
40741      * @cfg {Object} disable  List of toolbar elements to disable
40742          
40743      */
40744     disable : false,
40745     /**
40746      * @cfg {Object} styles List of styles 
40747      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40748      *
40749      * These must be defined in the page, so they get rendered correctly..
40750      * .headline { }
40751      * TD.underline { }
40752      * 
40753      */
40754     styles : false,
40755     
40756     
40757     
40758     toolbars : false,
40759     
40760     init : function(editor)
40761     {
40762         this.editor = editor;
40763         
40764         
40765         var fid = editor.frameId;
40766         var etb = this;
40767         function btn(id, toggle, handler){
40768             var xid = fid + '-'+ id ;
40769             return {
40770                 id : xid,
40771                 cmd : id,
40772                 cls : 'x-btn-icon x-edit-'+id,
40773                 enableToggle:toggle !== false,
40774                 scope: editor, // was editor...
40775                 handler:handler||editor.relayBtnCmd,
40776                 clickEvent:'mousedown',
40777                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40778                 tabIndex:-1
40779             };
40780         }
40781         // create a new element.
40782         var wdiv = editor.wrap.createChild({
40783                 tag: 'div'
40784             }, editor.wrap.dom.firstChild.nextSibling, true);
40785         
40786         // can we do this more than once??
40787         
40788          // stop form submits
40789       
40790  
40791         // disable everything...
40792         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40793         this.toolbars = {};
40794            
40795         for (var i in  ty) {
40796           
40797             this.toolbars[i] = this.buildToolbar(ty[i],i);
40798         }
40799         this.tb = this.toolbars.BODY;
40800         this.tb.el.show();
40801         this.buildFooter();
40802         this.footer.show();
40803          
40804         this.rendered = true;
40805         
40806         // the all the btns;
40807         editor.on('editorevent', this.updateToolbar, this);
40808         // other toolbars need to implement this..
40809         //editor.on('editmodechange', this.updateToolbar, this);
40810     },
40811     
40812     
40813     
40814     /**
40815      * Protected method that will not generally be called directly. It triggers
40816      * a toolbar update by reading the markup state of the current selection in the editor.
40817      */
40818     updateToolbar: function(ignore_a,ignore_b,sel){
40819
40820         
40821         if(!this.editor.activated){
40822              this.editor.onFirstFocus();
40823             return;
40824         }
40825         var updateFooter = sel ? false : true;
40826         
40827         
40828         var ans = this.editor.getAllAncestors();
40829         
40830         // pick
40831         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40832         
40833         if (!sel) { 
40834             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40835             sel = sel ? sel : this.editor.doc.body;
40836             sel = sel.tagName.length ? sel : this.editor.doc.body;
40837             
40838         }
40839         // pick a menu that exists..
40840         var tn = sel.tagName.toUpperCase();
40841         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40842         
40843         tn = sel.tagName.toUpperCase();
40844         
40845         var lastSel = this.tb.selectedNode
40846         
40847         this.tb.selectedNode = sel;
40848         
40849         // if current menu does not match..
40850         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40851                 
40852             this.tb.el.hide();
40853             ///console.log("show: " + tn);
40854             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40855             this.tb.el.show();
40856             // update name
40857             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40858             
40859             
40860             // update attributes
40861             if (this.tb.fields) {
40862                 this.tb.fields.each(function(e) {
40863                    e.setValue(sel.getAttribute(e.name));
40864                 });
40865             }
40866             
40867             // update styles
40868             var st = this.tb.fields.item(0);
40869             st.store.removeAll();
40870             var cn = sel.className.split(/\s+/);
40871             
40872             var avs = [];
40873             if (this.styles['*']) {
40874                 
40875                 Roo.each(this.styles['*'], function(v) {
40876                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40877                 });
40878             }
40879             if (this.styles[tn]) { 
40880                 Roo.each(this.styles[tn], function(v) {
40881                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40882                 });
40883             }
40884             
40885             st.store.loadData(avs);
40886             st.collapse();
40887             st.setValue(cn);
40888             
40889             // flag our selected Node.
40890             this.tb.selectedNode = sel;
40891            
40892            
40893             Roo.menu.MenuMgr.hideAll();
40894
40895         }
40896         
40897         if (!updateFooter) {
40898             return;
40899         }
40900         // update the footer
40901         //
40902         var html = '';
40903         
40904         this.footerEls = ans.reverse();
40905         Roo.each(this.footerEls, function(a,i) {
40906             if (!a) { return; }
40907             html += html.length ? ' &gt; '  :  '';
40908             
40909             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40910             
40911         });
40912        
40913         // 
40914         var sz = this.footDisp.up('td').getSize();
40915         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40916         this.footDisp.dom.style.marginLeft = '5px';
40917         
40918         this.footDisp.dom.style.overflow = 'hidden';
40919         
40920         this.footDisp.dom.innerHTML = html;
40921             
40922         //this.editorsyncValue();
40923     },
40924    
40925        
40926     // private
40927     onDestroy : function(){
40928         if(this.rendered){
40929             
40930             this.tb.items.each(function(item){
40931                 if(item.menu){
40932                     item.menu.removeAll();
40933                     if(item.menu.el){
40934                         item.menu.el.destroy();
40935                     }
40936                 }
40937                 item.destroy();
40938             });
40939              
40940         }
40941     },
40942     onFirstFocus: function() {
40943         // need to do this for all the toolbars..
40944         this.tb.items.each(function(item){
40945            item.enable();
40946         });
40947     },
40948     buildToolbar: function(tlist, nm)
40949     {
40950         var editor = this.editor;
40951          // create a new element.
40952         var wdiv = editor.wrap.createChild({
40953                 tag: 'div'
40954             }, editor.wrap.dom.firstChild.nextSibling, true);
40955         
40956        
40957         var tb = new Roo.Toolbar(wdiv);
40958         // add the name..
40959         
40960         tb.add(nm+ ":&nbsp;");
40961         
40962         // styles...
40963         if (this.styles) {
40964             
40965             // this needs a multi-select checkbox...
40966             tb.addField( new Roo.form.ComboBox({
40967                 store: new Roo.data.SimpleStore({
40968                     id : 'val',
40969                     fields: ['val', 'selected'],
40970                     data : [] 
40971                 }),
40972                 name : 'className',
40973                 displayField:'val',
40974                 typeAhead: false,
40975                 mode: 'local',
40976                 editable : false,
40977                 triggerAction: 'all',
40978                 emptyText:'Select Style',
40979                 selectOnFocus:true,
40980                 width: 130,
40981                 listeners : {
40982                     'select': function(c, r, i) {
40983                         // initial support only for on class per el..
40984                         tb.selectedNode.className =  r ? r.get('val') : '';
40985                     }
40986                 }
40987     
40988             }));
40989         }
40990             
40991         
40992         
40993         for (var i in tlist) {
40994             
40995             var item = tlist[i];
40996             tb.add(item.title + ":&nbsp;");
40997             
40998             
40999             
41000             
41001             if (item.opts) {
41002                 // opts == pulldown..
41003                 tb.addField( new Roo.form.ComboBox({
41004                     store: new Roo.data.SimpleStore({
41005                         id : 'val',
41006                         fields: ['val'],
41007                         data : item.opts  
41008                     }),
41009                     name : i,
41010                     displayField:'val',
41011                     typeAhead: false,
41012                     mode: 'local',
41013                     editable : false,
41014                     triggerAction: 'all',
41015                     emptyText:'Select',
41016                     selectOnFocus:true,
41017                     width: item.width ? item.width  : 130,
41018                     listeners : {
41019                         'select': function(c, r, i) {
41020                             tb.selectedNode.setAttribute(c.name, r.get('val'));
41021                         }
41022                     }
41023
41024                 }));
41025                 continue;
41026                     
41027                  
41028                 
41029                 tb.addField( new Roo.form.TextField({
41030                     name: i,
41031                     width: 100,
41032                     //allowBlank:false,
41033                     value: ''
41034                 }));
41035                 continue;
41036             }
41037             tb.addField( new Roo.form.TextField({
41038                 name: i,
41039                 width: item.width,
41040                 //allowBlank:true,
41041                 value: '',
41042                 listeners: {
41043                     'change' : function(f, nv, ov) {
41044                         tb.selectedNode.setAttribute(f.name, nv);
41045                     }
41046                 }
41047             }));
41048              
41049         }
41050         tb.el.on('click', function(e){
41051             e.preventDefault(); // what does this do?
41052         });
41053         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
41054         tb.el.hide();
41055         tb.name = nm;
41056         // dont need to disable them... as they will get hidden
41057         return tb;
41058          
41059         
41060     },
41061     buildFooter : function()
41062     {
41063         
41064         var fel = this.editor.wrap.createChild();
41065         this.footer = new Roo.Toolbar(fel);
41066         // toolbar has scrolly on left / right?
41067         var footDisp= new Roo.Toolbar.Fill();
41068         var _t = this;
41069         this.footer.add(
41070             {
41071                 text : '&lt;',
41072                 xtype: 'Button',
41073                 handler : function() {
41074                     _t.footDisp.scrollTo('left',0,true)
41075                 }
41076             }
41077         );
41078         this.footer.add( footDisp );
41079         this.footer.add( 
41080             {
41081                 text : '&gt;',
41082                 xtype: 'Button',
41083                 handler : function() {
41084                     // no animation..
41085                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
41086                 }
41087             }
41088         );
41089         var fel = Roo.get(footDisp.el);
41090         fel.addClass('x-editor-context');
41091         this.footDispWrap = fel; 
41092         this.footDispWrap.overflow  = 'hidden';
41093         
41094         this.footDisp = fel.createChild();
41095         this.footDispWrap.on('click', this.onContextClick, this)
41096         
41097         
41098     },
41099     onContextClick : function (ev,dom)
41100     {
41101         ev.preventDefault();
41102         var  cn = dom.className;
41103         Roo.log(cn);
41104         if (!cn.match(/x-ed-loc-/)) {
41105             return;
41106         }
41107         var n = cn.split('-').pop();
41108         var ans = this.footerEls;
41109         var sel = ans[n];
41110         
41111          // pick
41112         var range = this.editor.createRange();
41113         
41114         range.selectNodeContents(sel);
41115         //range.selectNode(sel);
41116         
41117         
41118         var selection = this.editor.getSelection();
41119         selection.removeAllRanges();
41120         selection.addRange(range);
41121         
41122         
41123         
41124         this.updateToolbar(null, null, sel);
41125         
41126         
41127     }
41128     
41129     
41130     
41131     
41132     
41133 });
41134
41135
41136
41137
41138
41139 /*
41140  * Based on:
41141  * Ext JS Library 1.1.1
41142  * Copyright(c) 2006-2007, Ext JS, LLC.
41143  *
41144  * Originally Released Under LGPL - original licence link has changed is not relivant.
41145  *
41146  * Fork - LGPL
41147  * <script type="text/javascript">
41148  */
41149  
41150 /**
41151  * @class Roo.form.BasicForm
41152  * @extends Roo.util.Observable
41153  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41154  * @constructor
41155  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41156  * @param {Object} config Configuration options
41157  */
41158 Roo.form.BasicForm = function(el, config){
41159     this.allItems = [];
41160     this.childForms = [];
41161     Roo.apply(this, config);
41162     /*
41163      * The Roo.form.Field items in this form.
41164      * @type MixedCollection
41165      */
41166      
41167      
41168     this.items = new Roo.util.MixedCollection(false, function(o){
41169         return o.id || (o.id = Roo.id());
41170     });
41171     this.addEvents({
41172         /**
41173          * @event beforeaction
41174          * Fires before any action is performed. Return false to cancel the action.
41175          * @param {Form} this
41176          * @param {Action} action The action to be performed
41177          */
41178         beforeaction: true,
41179         /**
41180          * @event actionfailed
41181          * Fires when an action fails.
41182          * @param {Form} this
41183          * @param {Action} action The action that failed
41184          */
41185         actionfailed : true,
41186         /**
41187          * @event actioncomplete
41188          * Fires when an action is completed.
41189          * @param {Form} this
41190          * @param {Action} action The action that completed
41191          */
41192         actioncomplete : true
41193     });
41194     if(el){
41195         this.initEl(el);
41196     }
41197     Roo.form.BasicForm.superclass.constructor.call(this);
41198 };
41199
41200 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41201     /**
41202      * @cfg {String} method
41203      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41204      */
41205     /**
41206      * @cfg {DataReader} reader
41207      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41208      * This is optional as there is built-in support for processing JSON.
41209      */
41210     /**
41211      * @cfg {DataReader} errorReader
41212      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41213      * This is completely optional as there is built-in support for processing JSON.
41214      */
41215     /**
41216      * @cfg {String} url
41217      * The URL to use for form actions if one isn't supplied in the action options.
41218      */
41219     /**
41220      * @cfg {Boolean} fileUpload
41221      * Set to true if this form is a file upload.
41222      */
41223      
41224     /**
41225      * @cfg {Object} baseParams
41226      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41227      */
41228      /**
41229      
41230     /**
41231      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41232      */
41233     timeout: 30,
41234
41235     // private
41236     activeAction : null,
41237
41238     /**
41239      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41240      * or setValues() data instead of when the form was first created.
41241      */
41242     trackResetOnLoad : false,
41243     
41244     
41245     /**
41246      * childForms - used for multi-tab forms
41247      * @type {Array}
41248      */
41249     childForms : false,
41250     
41251     /**
41252      * allItems - full list of fields.
41253      * @type {Array}
41254      */
41255     allItems : false,
41256     
41257     /**
41258      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41259      * element by passing it or its id or mask the form itself by passing in true.
41260      * @type Mixed
41261      */
41262     waitMsgTarget : false,
41263
41264     // private
41265     initEl : function(el){
41266         this.el = Roo.get(el);
41267         this.id = this.el.id || Roo.id();
41268         this.el.on('submit', this.onSubmit, this);
41269         this.el.addClass('x-form');
41270     },
41271
41272     // private
41273     onSubmit : function(e){
41274         e.stopEvent();
41275     },
41276
41277     /**
41278      * Returns true if client-side validation on the form is successful.
41279      * @return Boolean
41280      */
41281     isValid : function(){
41282         var valid = true;
41283         this.items.each(function(f){
41284            if(!f.validate()){
41285                valid = false;
41286            }
41287         });
41288         return valid;
41289     },
41290
41291     /**
41292      * Returns true if any fields in this form have changed since their original load.
41293      * @return Boolean
41294      */
41295     isDirty : function(){
41296         var dirty = false;
41297         this.items.each(function(f){
41298            if(f.isDirty()){
41299                dirty = true;
41300                return false;
41301            }
41302         });
41303         return dirty;
41304     },
41305
41306     /**
41307      * Performs a predefined action (submit or load) or custom actions you define on this form.
41308      * @param {String} actionName The name of the action type
41309      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41310      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41311      * accept other config options):
41312      * <pre>
41313 Property          Type             Description
41314 ----------------  ---------------  ----------------------------------------------------------------------------------
41315 url               String           The url for the action (defaults to the form's url)
41316 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41317 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41318 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41319                                    validate the form on the client (defaults to false)
41320      * </pre>
41321      * @return {BasicForm} this
41322      */
41323     doAction : function(action, options){
41324         if(typeof action == 'string'){
41325             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41326         }
41327         if(this.fireEvent('beforeaction', this, action) !== false){
41328             this.beforeAction(action);
41329             action.run.defer(100, action);
41330         }
41331         return this;
41332     },
41333
41334     /**
41335      * Shortcut to do a submit action.
41336      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41337      * @return {BasicForm} this
41338      */
41339     submit : function(options){
41340         this.doAction('submit', options);
41341         return this;
41342     },
41343
41344     /**
41345      * Shortcut to do a load action.
41346      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41347      * @return {BasicForm} this
41348      */
41349     load : function(options){
41350         this.doAction('load', options);
41351         return this;
41352     },
41353
41354     /**
41355      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41356      * @param {Record} record The record to edit
41357      * @return {BasicForm} this
41358      */
41359     updateRecord : function(record){
41360         record.beginEdit();
41361         var fs = record.fields;
41362         fs.each(function(f){
41363             var field = this.findField(f.name);
41364             if(field){
41365                 record.set(f.name, field.getValue());
41366             }
41367         }, this);
41368         record.endEdit();
41369         return this;
41370     },
41371
41372     /**
41373      * Loads an Roo.data.Record into this form.
41374      * @param {Record} record The record to load
41375      * @return {BasicForm} this
41376      */
41377     loadRecord : function(record){
41378         this.setValues(record.data);
41379         return this;
41380     },
41381
41382     // private
41383     beforeAction : function(action){
41384         var o = action.options;
41385         
41386        
41387         if(this.waitMsgTarget === true){
41388             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41389         }else if(this.waitMsgTarget){
41390             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41391             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41392         }else {
41393             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41394         }
41395          
41396     },
41397
41398     // private
41399     afterAction : function(action, success){
41400         this.activeAction = null;
41401         var o = action.options;
41402         
41403         if(this.waitMsgTarget === true){
41404             this.el.unmask();
41405         }else if(this.waitMsgTarget){
41406             this.waitMsgTarget.unmask();
41407         }else{
41408             Roo.MessageBox.updateProgress(1);
41409             Roo.MessageBox.hide();
41410         }
41411          
41412         if(success){
41413             if(o.reset){
41414                 this.reset();
41415             }
41416             Roo.callback(o.success, o.scope, [this, action]);
41417             this.fireEvent('actioncomplete', this, action);
41418             
41419         }else{
41420             
41421             // failure condition..
41422             // we have a scenario where updates need confirming.
41423             // eg. if a locking scenario exists..
41424             // we look for { errors : { needs_confirm : true }} in the response.
41425             if (typeof(action.result.errors.needs_confirm) != 'undefined') {
41426                 var _t = this;
41427                 Roo.MessageBox.confirm(
41428                     "Change requires confirmation",
41429                     action.result.errorMsg,
41430                     function(r) {
41431                         if (r != 'yes') {
41432                             return;
41433                         }
41434                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
41435                     }
41436                     
41437                 );
41438                 
41439                 
41440                 
41441                 return;
41442             }
41443             
41444             Roo.callback(o.failure, o.scope, [this, action]);
41445             // show an error message if no failed handler is set..
41446             if (!this.hasListener('actionfailed')) {
41447                 Roo.MessageBox.alert("Error",
41448                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
41449                         action.result.errorMsg :
41450                         "Saving Failed, please check your entries"
41451                 );
41452             }
41453             
41454             this.fireEvent('actionfailed', this, action);
41455         }
41456         
41457     },
41458
41459     /**
41460      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41461      * @param {String} id The value to search for
41462      * @return Field
41463      */
41464     findField : function(id){
41465         var field = this.items.get(id);
41466         if(!field){
41467             this.items.each(function(f){
41468                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41469                     field = f;
41470                     return false;
41471                 }
41472             });
41473         }
41474         return field || null;
41475     },
41476
41477     /**
41478      * Add a secondary form to this one, 
41479      * Used to provide tabbed forms. One form is primary, with hidden values 
41480      * which mirror the elements from the other forms.
41481      * 
41482      * @param {Roo.form.Form} form to add.
41483      * 
41484      */
41485     addForm : function(form)
41486     {
41487        
41488         if (this.childForms.indexOf(form) > -1) {
41489             // already added..
41490             return;
41491         }
41492         this.childForms.push(form);
41493         var n = '';
41494         Roo.each(form.allItems, function (fe) {
41495             
41496             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41497             if (this.findField(n)) { // already added..
41498                 return;
41499             }
41500             var add = new Roo.form.Hidden({
41501                 name : n
41502             });
41503             add.render(this.el);
41504             
41505             this.add( add );
41506         }, this);
41507         
41508     },
41509     /**
41510      * Mark fields in this form invalid in bulk.
41511      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41512      * @return {BasicForm} this
41513      */
41514     markInvalid : function(errors){
41515         if(errors instanceof Array){
41516             for(var i = 0, len = errors.length; i < len; i++){
41517                 var fieldError = errors[i];
41518                 var f = this.findField(fieldError.id);
41519                 if(f){
41520                     f.markInvalid(fieldError.msg);
41521                 }
41522             }
41523         }else{
41524             var field, id;
41525             for(id in errors){
41526                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41527                     field.markInvalid(errors[id]);
41528                 }
41529             }
41530         }
41531         Roo.each(this.childForms || [], function (f) {
41532             f.markInvalid(errors);
41533         });
41534         
41535         return this;
41536     },
41537
41538     /**
41539      * Set values for fields in this form in bulk.
41540      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41541      * @return {BasicForm} this
41542      */
41543     setValues : function(values){
41544         if(values instanceof Array){ // array of objects
41545             for(var i = 0, len = values.length; i < len; i++){
41546                 var v = values[i];
41547                 var f = this.findField(v.id);
41548                 if(f){
41549                     f.setValue(v.value);
41550                     if(this.trackResetOnLoad){
41551                         f.originalValue = f.getValue();
41552                     }
41553                 }
41554             }
41555         }else{ // object hash
41556             var field, id;
41557             for(id in values){
41558                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41559                     
41560                     if (field.setFromData && 
41561                         field.valueField && 
41562                         field.displayField &&
41563                         // combos' with local stores can 
41564                         // be queried via setValue()
41565                         // to set their value..
41566                         (field.store && !field.store.isLocal)
41567                         ) {
41568                         // it's a combo
41569                         var sd = { };
41570                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41571                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41572                         field.setFromData(sd);
41573                         
41574                     } else {
41575                         field.setValue(values[id]);
41576                     }
41577                     
41578                     
41579                     if(this.trackResetOnLoad){
41580                         field.originalValue = field.getValue();
41581                     }
41582                 }
41583             }
41584         }
41585          
41586         Roo.each(this.childForms || [], function (f) {
41587             f.setValues(values);
41588         });
41589                 
41590         return this;
41591     },
41592
41593     /**
41594      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41595      * they are returned as an array.
41596      * @param {Boolean} asString
41597      * @return {Object}
41598      */
41599     getValues : function(asString){
41600         if (this.childForms) {
41601             // copy values from the child forms
41602             Roo.each(this.childForms, function (f) {
41603                 this.setValues(f.getValues());
41604             }, this);
41605         }
41606         
41607         
41608         
41609         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41610         if(asString === true){
41611             return fs;
41612         }
41613         return Roo.urlDecode(fs);
41614     },
41615     
41616     /**
41617      * Returns the fields in this form as an object with key/value pairs. 
41618      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41619      * @return {Object}
41620      */
41621     getFieldValues : function(with_hidden)
41622     {
41623         if (this.childForms) {
41624             // copy values from the child forms
41625             // should this call getFieldValues - probably not as we do not currently copy
41626             // hidden fields when we generate..
41627             Roo.each(this.childForms, function (f) {
41628                 this.setValues(f.getValues());
41629             }, this);
41630         }
41631         
41632         var ret = {};
41633         this.items.each(function(f){
41634             if (!f.getName()) {
41635                 return;
41636             }
41637             var v = f.getValue();
41638             // not sure if this supported any more..
41639             if ((typeof(v) == 'object') && f.getRawValue) {
41640                 v = f.getRawValue() ; // dates..
41641             }
41642             // combo boxes where name != hiddenName...
41643             if (f.name != f.getName()) {
41644                 ret[f.name] = f.getRawValue();
41645             }
41646             ret[f.getName()] = v;
41647         });
41648         
41649         return ret;
41650     },
41651
41652     /**
41653      * Clears all invalid messages in this form.
41654      * @return {BasicForm} this
41655      */
41656     clearInvalid : function(){
41657         this.items.each(function(f){
41658            f.clearInvalid();
41659         });
41660         
41661         Roo.each(this.childForms || [], function (f) {
41662             f.clearInvalid();
41663         });
41664         
41665         
41666         return this;
41667     },
41668
41669     /**
41670      * Resets this form.
41671      * @return {BasicForm} this
41672      */
41673     reset : function(){
41674         this.items.each(function(f){
41675             f.reset();
41676         });
41677         
41678         Roo.each(this.childForms || [], function (f) {
41679             f.reset();
41680         });
41681        
41682         
41683         return this;
41684     },
41685
41686     /**
41687      * Add Roo.form components to this form.
41688      * @param {Field} field1
41689      * @param {Field} field2 (optional)
41690      * @param {Field} etc (optional)
41691      * @return {BasicForm} this
41692      */
41693     add : function(){
41694         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41695         return this;
41696     },
41697
41698
41699     /**
41700      * Removes a field from the items collection (does NOT remove its markup).
41701      * @param {Field} field
41702      * @return {BasicForm} this
41703      */
41704     remove : function(field){
41705         this.items.remove(field);
41706         return this;
41707     },
41708
41709     /**
41710      * Looks at the fields in this form, checks them for an id attribute,
41711      * and calls applyTo on the existing dom element with that id.
41712      * @return {BasicForm} this
41713      */
41714     render : function(){
41715         this.items.each(function(f){
41716             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41717                 f.applyTo(f.id);
41718             }
41719         });
41720         return this;
41721     },
41722
41723     /**
41724      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41725      * @param {Object} values
41726      * @return {BasicForm} this
41727      */
41728     applyToFields : function(o){
41729         this.items.each(function(f){
41730            Roo.apply(f, o);
41731         });
41732         return this;
41733     },
41734
41735     /**
41736      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41737      * @param {Object} values
41738      * @return {BasicForm} this
41739      */
41740     applyIfToFields : function(o){
41741         this.items.each(function(f){
41742            Roo.applyIf(f, o);
41743         });
41744         return this;
41745     }
41746 });
41747
41748 // back compat
41749 Roo.BasicForm = Roo.form.BasicForm;/*
41750  * Based on:
41751  * Ext JS Library 1.1.1
41752  * Copyright(c) 2006-2007, Ext JS, LLC.
41753  *
41754  * Originally Released Under LGPL - original licence link has changed is not relivant.
41755  *
41756  * Fork - LGPL
41757  * <script type="text/javascript">
41758  */
41759
41760 /**
41761  * @class Roo.form.Form
41762  * @extends Roo.form.BasicForm
41763  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41764  * @constructor
41765  * @param {Object} config Configuration options
41766  */
41767 Roo.form.Form = function(config){
41768     var xitems =  [];
41769     if (config.items) {
41770         xitems = config.items;
41771         delete config.items;
41772     }
41773    
41774     
41775     Roo.form.Form.superclass.constructor.call(this, null, config);
41776     this.url = this.url || this.action;
41777     if(!this.root){
41778         this.root = new Roo.form.Layout(Roo.applyIf({
41779             id: Roo.id()
41780         }, config));
41781     }
41782     this.active = this.root;
41783     /**
41784      * Array of all the buttons that have been added to this form via {@link addButton}
41785      * @type Array
41786      */
41787     this.buttons = [];
41788     this.allItems = [];
41789     this.addEvents({
41790         /**
41791          * @event clientvalidation
41792          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41793          * @param {Form} this
41794          * @param {Boolean} valid true if the form has passed client-side validation
41795          */
41796         clientvalidation: true,
41797         /**
41798          * @event rendered
41799          * Fires when the form is rendered
41800          * @param {Roo.form.Form} form
41801          */
41802         rendered : true
41803     });
41804     
41805     if (this.progressUrl) {
41806             // push a hidden field onto the list of fields..
41807             this.addxtype( {
41808                     xns: Roo.form, 
41809                     xtype : 'Hidden', 
41810                     name : 'UPLOAD_IDENTIFIER' 
41811             });
41812         }
41813         
41814     
41815     Roo.each(xitems, this.addxtype, this);
41816     
41817     
41818     
41819 };
41820
41821 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41822     /**
41823      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41824      */
41825     /**
41826      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41827      */
41828     /**
41829      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41830      */
41831     buttonAlign:'center',
41832
41833     /**
41834      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41835      */
41836     minButtonWidth:75,
41837
41838     /**
41839      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41840      * This property cascades to child containers if not set.
41841      */
41842     labelAlign:'left',
41843
41844     /**
41845      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41846      * fires a looping event with that state. This is required to bind buttons to the valid
41847      * state using the config value formBind:true on the button.
41848      */
41849     monitorValid : false,
41850
41851     /**
41852      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41853      */
41854     monitorPoll : 200,
41855     
41856     /**
41857      * @cfg {String} progressUrl - Url to return progress data 
41858      */
41859     
41860     progressUrl : false,
41861   
41862     /**
41863      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41864      * fields are added and the column is closed. If no fields are passed the column remains open
41865      * until end() is called.
41866      * @param {Object} config The config to pass to the column
41867      * @param {Field} field1 (optional)
41868      * @param {Field} field2 (optional)
41869      * @param {Field} etc (optional)
41870      * @return Column The column container object
41871      */
41872     column : function(c){
41873         var col = new Roo.form.Column(c);
41874         this.start(col);
41875         if(arguments.length > 1){ // duplicate code required because of Opera
41876             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41877             this.end();
41878         }
41879         return col;
41880     },
41881
41882     /**
41883      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41884      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41885      * until end() is called.
41886      * @param {Object} config The config to pass to the fieldset
41887      * @param {Field} field1 (optional)
41888      * @param {Field} field2 (optional)
41889      * @param {Field} etc (optional)
41890      * @return FieldSet The fieldset container object
41891      */
41892     fieldset : function(c){
41893         var fs = new Roo.form.FieldSet(c);
41894         this.start(fs);
41895         if(arguments.length > 1){ // duplicate code required because of Opera
41896             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41897             this.end();
41898         }
41899         return fs;
41900     },
41901
41902     /**
41903      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41904      * fields are added and the container is closed. If no fields are passed the container remains open
41905      * until end() is called.
41906      * @param {Object} config The config to pass to the Layout
41907      * @param {Field} field1 (optional)
41908      * @param {Field} field2 (optional)
41909      * @param {Field} etc (optional)
41910      * @return Layout The container object
41911      */
41912     container : function(c){
41913         var l = new Roo.form.Layout(c);
41914         this.start(l);
41915         if(arguments.length > 1){ // duplicate code required because of Opera
41916             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41917             this.end();
41918         }
41919         return l;
41920     },
41921
41922     /**
41923      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41924      * @param {Object} container A Roo.form.Layout or subclass of Layout
41925      * @return {Form} this
41926      */
41927     start : function(c){
41928         // cascade label info
41929         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41930         this.active.stack.push(c);
41931         c.ownerCt = this.active;
41932         this.active = c;
41933         return this;
41934     },
41935
41936     /**
41937      * Closes the current open container
41938      * @return {Form} this
41939      */
41940     end : function(){
41941         if(this.active == this.root){
41942             return this;
41943         }
41944         this.active = this.active.ownerCt;
41945         return this;
41946     },
41947
41948     /**
41949      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41950      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41951      * as the label of the field.
41952      * @param {Field} field1
41953      * @param {Field} field2 (optional)
41954      * @param {Field} etc. (optional)
41955      * @return {Form} this
41956      */
41957     add : function(){
41958         this.active.stack.push.apply(this.active.stack, arguments);
41959         this.allItems.push.apply(this.allItems,arguments);
41960         var r = [];
41961         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41962             if(a[i].isFormField){
41963                 r.push(a[i]);
41964             }
41965         }
41966         if(r.length > 0){
41967             Roo.form.Form.superclass.add.apply(this, r);
41968         }
41969         return this;
41970     },
41971     
41972
41973     
41974     
41975     
41976      /**
41977      * Find any element that has been added to a form, using it's ID or name
41978      * This can include framesets, columns etc. along with regular fields..
41979      * @param {String} id - id or name to find.
41980      
41981      * @return {Element} e - or false if nothing found.
41982      */
41983     findbyId : function(id)
41984     {
41985         var ret = false;
41986         if (!id) {
41987             return ret;
41988         }
41989         Roo.each(this.allItems, function(f){
41990             if (f.id == id || f.name == id ){
41991                 ret = f;
41992                 return false;
41993             }
41994         });
41995         return ret;
41996     },
41997
41998     
41999     
42000     /**
42001      * Render this form into the passed container. This should only be called once!
42002      * @param {String/HTMLElement/Element} container The element this component should be rendered into
42003      * @return {Form} this
42004      */
42005     render : function(ct)
42006     {
42007         
42008         
42009         
42010         ct = Roo.get(ct);
42011         var o = this.autoCreate || {
42012             tag: 'form',
42013             method : this.method || 'POST',
42014             id : this.id || Roo.id()
42015         };
42016         this.initEl(ct.createChild(o));
42017
42018         this.root.render(this.el);
42019         
42020        
42021              
42022         this.items.each(function(f){
42023             f.render('x-form-el-'+f.id);
42024         });
42025
42026         if(this.buttons.length > 0){
42027             // tables are required to maintain order and for correct IE layout
42028             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
42029                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
42030                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
42031             }}, null, true);
42032             var tr = tb.getElementsByTagName('tr')[0];
42033             for(var i = 0, len = this.buttons.length; i < len; i++) {
42034                 var b = this.buttons[i];
42035                 var td = document.createElement('td');
42036                 td.className = 'x-form-btn-td';
42037                 b.render(tr.appendChild(td));
42038             }
42039         }
42040         if(this.monitorValid){ // initialize after render
42041             this.startMonitoring();
42042         }
42043         this.fireEvent('rendered', this);
42044         return this;
42045     },
42046
42047     /**
42048      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
42049      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
42050      * object or a valid Roo.DomHelper element config
42051      * @param {Function} handler The function called when the button is clicked
42052      * @param {Object} scope (optional) The scope of the handler function
42053      * @return {Roo.Button}
42054      */
42055     addButton : function(config, handler, scope){
42056         var bc = {
42057             handler: handler,
42058             scope: scope,
42059             minWidth: this.minButtonWidth,
42060             hideParent:true
42061         };
42062         if(typeof config == "string"){
42063             bc.text = config;
42064         }else{
42065             Roo.apply(bc, config);
42066         }
42067         var btn = new Roo.Button(null, bc);
42068         this.buttons.push(btn);
42069         return btn;
42070     },
42071
42072      /**
42073      * Adds a series of form elements (using the xtype property as the factory method.
42074      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
42075      * @param {Object} config 
42076      */
42077     
42078     addxtype : function()
42079     {
42080         var ar = Array.prototype.slice.call(arguments, 0);
42081         var ret = false;
42082         for(var i = 0; i < ar.length; i++) {
42083             if (!ar[i]) {
42084                 continue; // skip -- if this happends something invalid got sent, we 
42085                 // should ignore it, as basically that interface element will not show up
42086                 // and that should be pretty obvious!!
42087             }
42088             
42089             if (Roo.form[ar[i].xtype]) {
42090                 ar[i].form = this;
42091                 var fe = Roo.factory(ar[i], Roo.form);
42092                 if (!ret) {
42093                     ret = fe;
42094                 }
42095                 fe.form = this;
42096                 if (fe.store) {
42097                     fe.store.form = this;
42098                 }
42099                 if (fe.isLayout) {  
42100                          
42101                     this.start(fe);
42102                     this.allItems.push(fe);
42103                     if (fe.items && fe.addxtype) {
42104                         fe.addxtype.apply(fe, fe.items);
42105                         delete fe.items;
42106                     }
42107                      this.end();
42108                     continue;
42109                 }
42110                 
42111                 
42112                  
42113                 this.add(fe);
42114               //  console.log('adding ' + ar[i].xtype);
42115             }
42116             if (ar[i].xtype == 'Button') {  
42117                 //console.log('adding button');
42118                 //console.log(ar[i]);
42119                 this.addButton(ar[i]);
42120                 this.allItems.push(fe);
42121                 continue;
42122             }
42123             
42124             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
42125                 alert('end is not supported on xtype any more, use items');
42126             //    this.end();
42127             //    //console.log('adding end');
42128             }
42129             
42130         }
42131         return ret;
42132     },
42133     
42134     /**
42135      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
42136      * option "monitorValid"
42137      */
42138     startMonitoring : function(){
42139         if(!this.bound){
42140             this.bound = true;
42141             Roo.TaskMgr.start({
42142                 run : this.bindHandler,
42143                 interval : this.monitorPoll || 200,
42144                 scope: this
42145             });
42146         }
42147     },
42148
42149     /**
42150      * Stops monitoring of the valid state of this form
42151      */
42152     stopMonitoring : function(){
42153         this.bound = false;
42154     },
42155
42156     // private
42157     bindHandler : function(){
42158         if(!this.bound){
42159             return false; // stops binding
42160         }
42161         var valid = true;
42162         this.items.each(function(f){
42163             if(!f.isValid(true)){
42164                 valid = false;
42165                 return false;
42166             }
42167         });
42168         for(var i = 0, len = this.buttons.length; i < len; i++){
42169             var btn = this.buttons[i];
42170             if(btn.formBind === true && btn.disabled === valid){
42171                 btn.setDisabled(!valid);
42172             }
42173         }
42174         this.fireEvent('clientvalidation', this, valid);
42175     }
42176     
42177     
42178     
42179     
42180     
42181     
42182     
42183     
42184 });
42185
42186
42187 // back compat
42188 Roo.Form = Roo.form.Form;
42189 /*
42190  * Based on:
42191  * Ext JS Library 1.1.1
42192  * Copyright(c) 2006-2007, Ext JS, LLC.
42193  *
42194  * Originally Released Under LGPL - original licence link has changed is not relivant.
42195  *
42196  * Fork - LGPL
42197  * <script type="text/javascript">
42198  */
42199  
42200  /**
42201  * @class Roo.form.Action
42202  * Internal Class used to handle form actions
42203  * @constructor
42204  * @param {Roo.form.BasicForm} el The form element or its id
42205  * @param {Object} config Configuration options
42206  */
42207  
42208  
42209 // define the action interface
42210 Roo.form.Action = function(form, options){
42211     this.form = form;
42212     this.options = options || {};
42213 };
42214 /**
42215  * Client Validation Failed
42216  * @const 
42217  */
42218 Roo.form.Action.CLIENT_INVALID = 'client';
42219 /**
42220  * Server Validation Failed
42221  * @const 
42222  */
42223  Roo.form.Action.SERVER_INVALID = 'server';
42224  /**
42225  * Connect to Server Failed
42226  * @const 
42227  */
42228 Roo.form.Action.CONNECT_FAILURE = 'connect';
42229 /**
42230  * Reading Data from Server Failed
42231  * @const 
42232  */
42233 Roo.form.Action.LOAD_FAILURE = 'load';
42234
42235 Roo.form.Action.prototype = {
42236     type : 'default',
42237     failureType : undefined,
42238     response : undefined,
42239     result : undefined,
42240
42241     // interface method
42242     run : function(options){
42243
42244     },
42245
42246     // interface method
42247     success : function(response){
42248
42249     },
42250
42251     // interface method
42252     handleResponse : function(response){
42253
42254     },
42255
42256     // default connection failure
42257     failure : function(response){
42258         
42259         this.response = response;
42260         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42261         this.form.afterAction(this, false);
42262     },
42263
42264     processResponse : function(response){
42265         this.response = response;
42266         if(!response.responseText){
42267             return true;
42268         }
42269         this.result = this.handleResponse(response);
42270         return this.result;
42271     },
42272
42273     // utility functions used internally
42274     getUrl : function(appendParams){
42275         var url = this.options.url || this.form.url || this.form.el.dom.action;
42276         if(appendParams){
42277             var p = this.getParams();
42278             if(p){
42279                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42280             }
42281         }
42282         return url;
42283     },
42284
42285     getMethod : function(){
42286         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42287     },
42288
42289     getParams : function(){
42290         var bp = this.form.baseParams;
42291         var p = this.options.params;
42292         if(p){
42293             if(typeof p == "object"){
42294                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42295             }else if(typeof p == 'string' && bp){
42296                 p += '&' + Roo.urlEncode(bp);
42297             }
42298         }else if(bp){
42299             p = Roo.urlEncode(bp);
42300         }
42301         return p;
42302     },
42303
42304     createCallback : function(){
42305         return {
42306             success: this.success,
42307             failure: this.failure,
42308             scope: this,
42309             timeout: (this.form.timeout*1000),
42310             upload: this.form.fileUpload ? this.success : undefined
42311         };
42312     }
42313 };
42314
42315 Roo.form.Action.Submit = function(form, options){
42316     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42317 };
42318
42319 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42320     type : 'submit',
42321
42322     haveProgress : false,
42323     uploadComplete : false,
42324     
42325     // uploadProgress indicator.
42326     uploadProgress : function()
42327     {
42328         if (!this.form.progressUrl) {
42329             return;
42330         }
42331         
42332         if (!this.haveProgress) {
42333             Roo.MessageBox.progress("Uploading", "Uploading");
42334         }
42335         if (this.uploadComplete) {
42336            Roo.MessageBox.hide();
42337            return;
42338         }
42339         
42340         this.haveProgress = true;
42341    
42342         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42343         
42344         var c = new Roo.data.Connection();
42345         c.request({
42346             url : this.form.progressUrl,
42347             params: {
42348                 id : uid
42349             },
42350             method: 'GET',
42351             success : function(req){
42352                //console.log(data);
42353                 var rdata = false;
42354                 var edata;
42355                 try  {
42356                    rdata = Roo.decode(req.responseText)
42357                 } catch (e) {
42358                     Roo.log("Invalid data from server..");
42359                     Roo.log(edata);
42360                     return;
42361                 }
42362                 if (!rdata || !rdata.success) {
42363                     Roo.log(rdata);
42364                     return;
42365                 }
42366                 var data = rdata.data;
42367                 
42368                 if (this.uploadComplete) {
42369                    Roo.MessageBox.hide();
42370                    return;
42371                 }
42372                    
42373                 if (data){
42374                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42375                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42376                     );
42377                 }
42378                 this.uploadProgress.defer(2000,this);
42379             },
42380        
42381             failure: function(data) {
42382                 Roo.log('progress url failed ');
42383                 Roo.log(data);
42384             },
42385             scope : this
42386         });
42387            
42388     },
42389     
42390     
42391     run : function()
42392     {
42393         // run get Values on the form, so it syncs any secondary forms.
42394         this.form.getValues();
42395         
42396         var o = this.options;
42397         var method = this.getMethod();
42398         var isPost = method == 'POST';
42399         if(o.clientValidation === false || this.form.isValid()){
42400             
42401             if (this.form.progressUrl) {
42402                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42403                     (new Date() * 1) + '' + Math.random());
42404                     
42405             } 
42406             
42407             
42408             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42409                 form:this.form.el.dom,
42410                 url:this.getUrl(!isPost),
42411                 method: method,
42412                 params:isPost ? this.getParams() : null,
42413                 isUpload: this.form.fileUpload
42414             }));
42415             
42416             this.uploadProgress();
42417
42418         }else if (o.clientValidation !== false){ // client validation failed
42419             this.failureType = Roo.form.Action.CLIENT_INVALID;
42420             this.form.afterAction(this, false);
42421         }
42422     },
42423
42424     success : function(response)
42425     {
42426         this.uploadComplete= true;
42427         if (this.haveProgress) {
42428             Roo.MessageBox.hide();
42429         }
42430         
42431         
42432         var result = this.processResponse(response);
42433         if(result === true || result.success){
42434             this.form.afterAction(this, true);
42435             return;
42436         }
42437         if(result.errors){
42438             this.form.markInvalid(result.errors);
42439             this.failureType = Roo.form.Action.SERVER_INVALID;
42440         }
42441         this.form.afterAction(this, false);
42442     },
42443     failure : function(response)
42444     {
42445         this.uploadComplete= true;
42446         if (this.haveProgress) {
42447             Roo.MessageBox.hide();
42448         }
42449         
42450         this.response = response;
42451         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42452         this.form.afterAction(this, false);
42453     },
42454     
42455     handleResponse : function(response){
42456         if(this.form.errorReader){
42457             var rs = this.form.errorReader.read(response);
42458             var errors = [];
42459             if(rs.records){
42460                 for(var i = 0, len = rs.records.length; i < len; i++) {
42461                     var r = rs.records[i];
42462                     errors[i] = r.data;
42463                 }
42464             }
42465             if(errors.length < 1){
42466                 errors = null;
42467             }
42468             return {
42469                 success : rs.success,
42470                 errors : errors
42471             };
42472         }
42473         var ret = false;
42474         try {
42475             ret = Roo.decode(response.responseText);
42476         } catch (e) {
42477             ret = {
42478                 success: false,
42479                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42480                 errors : []
42481             };
42482         }
42483         return ret;
42484         
42485     }
42486 });
42487
42488
42489 Roo.form.Action.Load = function(form, options){
42490     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42491     this.reader = this.form.reader;
42492 };
42493
42494 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42495     type : 'load',
42496
42497     run : function(){
42498         
42499         Roo.Ajax.request(Roo.apply(
42500                 this.createCallback(), {
42501                     method:this.getMethod(),
42502                     url:this.getUrl(false),
42503                     params:this.getParams()
42504         }));
42505     },
42506
42507     success : function(response){
42508         
42509         var result = this.processResponse(response);
42510         if(result === true || !result.success || !result.data){
42511             this.failureType = Roo.form.Action.LOAD_FAILURE;
42512             this.form.afterAction(this, false);
42513             return;
42514         }
42515         this.form.clearInvalid();
42516         this.form.setValues(result.data);
42517         this.form.afterAction(this, true);
42518     },
42519
42520     handleResponse : function(response){
42521         if(this.form.reader){
42522             var rs = this.form.reader.read(response);
42523             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42524             return {
42525                 success : rs.success,
42526                 data : data
42527             };
42528         }
42529         return Roo.decode(response.responseText);
42530     }
42531 });
42532
42533 Roo.form.Action.ACTION_TYPES = {
42534     'load' : Roo.form.Action.Load,
42535     'submit' : Roo.form.Action.Submit
42536 };/*
42537  * Based on:
42538  * Ext JS Library 1.1.1
42539  * Copyright(c) 2006-2007, Ext JS, LLC.
42540  *
42541  * Originally Released Under LGPL - original licence link has changed is not relivant.
42542  *
42543  * Fork - LGPL
42544  * <script type="text/javascript">
42545  */
42546  
42547 /**
42548  * @class Roo.form.Layout
42549  * @extends Roo.Component
42550  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42551  * @constructor
42552  * @param {Object} config Configuration options
42553  */
42554 Roo.form.Layout = function(config){
42555     var xitems = [];
42556     if (config.items) {
42557         xitems = config.items;
42558         delete config.items;
42559     }
42560     Roo.form.Layout.superclass.constructor.call(this, config);
42561     this.stack = [];
42562     Roo.each(xitems, this.addxtype, this);
42563      
42564 };
42565
42566 Roo.extend(Roo.form.Layout, Roo.Component, {
42567     /**
42568      * @cfg {String/Object} autoCreate
42569      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42570      */
42571     /**
42572      * @cfg {String/Object/Function} style
42573      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42574      * a function which returns such a specification.
42575      */
42576     /**
42577      * @cfg {String} labelAlign
42578      * Valid values are "left," "top" and "right" (defaults to "left")
42579      */
42580     /**
42581      * @cfg {Number} labelWidth
42582      * Fixed width in pixels of all field labels (defaults to undefined)
42583      */
42584     /**
42585      * @cfg {Boolean} clear
42586      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42587      */
42588     clear : true,
42589     /**
42590      * @cfg {String} labelSeparator
42591      * The separator to use after field labels (defaults to ':')
42592      */
42593     labelSeparator : ':',
42594     /**
42595      * @cfg {Boolean} hideLabels
42596      * True to suppress the display of field labels in this layout (defaults to false)
42597      */
42598     hideLabels : false,
42599
42600     // private
42601     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42602     
42603     isLayout : true,
42604     
42605     // private
42606     onRender : function(ct, position){
42607         if(this.el){ // from markup
42608             this.el = Roo.get(this.el);
42609         }else {  // generate
42610             var cfg = this.getAutoCreate();
42611             this.el = ct.createChild(cfg, position);
42612         }
42613         if(this.style){
42614             this.el.applyStyles(this.style);
42615         }
42616         if(this.labelAlign){
42617             this.el.addClass('x-form-label-'+this.labelAlign);
42618         }
42619         if(this.hideLabels){
42620             this.labelStyle = "display:none";
42621             this.elementStyle = "padding-left:0;";
42622         }else{
42623             if(typeof this.labelWidth == 'number'){
42624                 this.labelStyle = "width:"+this.labelWidth+"px;";
42625                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42626             }
42627             if(this.labelAlign == 'top'){
42628                 this.labelStyle = "width:auto;";
42629                 this.elementStyle = "padding-left:0;";
42630             }
42631         }
42632         var stack = this.stack;
42633         var slen = stack.length;
42634         if(slen > 0){
42635             if(!this.fieldTpl){
42636                 var t = new Roo.Template(
42637                     '<div class="x-form-item {5}">',
42638                         '<label for="{0}" style="{2}">{1}{4}</label>',
42639                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42640                         '</div>',
42641                     '</div><div class="x-form-clear-left"></div>'
42642                 );
42643                 t.disableFormats = true;
42644                 t.compile();
42645                 Roo.form.Layout.prototype.fieldTpl = t;
42646             }
42647             for(var i = 0; i < slen; i++) {
42648                 if(stack[i].isFormField){
42649                     this.renderField(stack[i]);
42650                 }else{
42651                     this.renderComponent(stack[i]);
42652                 }
42653             }
42654         }
42655         if(this.clear){
42656             this.el.createChild({cls:'x-form-clear'});
42657         }
42658     },
42659
42660     // private
42661     renderField : function(f){
42662         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42663                f.id, //0
42664                f.fieldLabel, //1
42665                f.labelStyle||this.labelStyle||'', //2
42666                this.elementStyle||'', //3
42667                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42668                f.itemCls||this.itemCls||''  //5
42669        ], true).getPrevSibling());
42670     },
42671
42672     // private
42673     renderComponent : function(c){
42674         c.render(c.isLayout ? this.el : this.el.createChild());    
42675     },
42676     /**
42677      * Adds a object form elements (using the xtype property as the factory method.)
42678      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42679      * @param {Object} config 
42680      */
42681     addxtype : function(o)
42682     {
42683         // create the lement.
42684         o.form = this.form;
42685         var fe = Roo.factory(o, Roo.form);
42686         this.form.allItems.push(fe);
42687         this.stack.push(fe);
42688         
42689         if (fe.isFormField) {
42690             this.form.items.add(fe);
42691         }
42692          
42693         return fe;
42694     }
42695 });
42696
42697 /**
42698  * @class Roo.form.Column
42699  * @extends Roo.form.Layout
42700  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42701  * @constructor
42702  * @param {Object} config Configuration options
42703  */
42704 Roo.form.Column = function(config){
42705     Roo.form.Column.superclass.constructor.call(this, config);
42706 };
42707
42708 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42709     /**
42710      * @cfg {Number/String} width
42711      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42712      */
42713     /**
42714      * @cfg {String/Object} autoCreate
42715      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42716      */
42717
42718     // private
42719     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42720
42721     // private
42722     onRender : function(ct, position){
42723         Roo.form.Column.superclass.onRender.call(this, ct, position);
42724         if(this.width){
42725             this.el.setWidth(this.width);
42726         }
42727     }
42728 });
42729
42730
42731 /**
42732  * @class Roo.form.Row
42733  * @extends Roo.form.Layout
42734  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42735  * @constructor
42736  * @param {Object} config Configuration options
42737  */
42738
42739  
42740 Roo.form.Row = function(config){
42741     Roo.form.Row.superclass.constructor.call(this, config);
42742 };
42743  
42744 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42745       /**
42746      * @cfg {Number/String} width
42747      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42748      */
42749     /**
42750      * @cfg {Number/String} height
42751      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42752      */
42753     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42754     
42755     padWidth : 20,
42756     // private
42757     onRender : function(ct, position){
42758         //console.log('row render');
42759         if(!this.rowTpl){
42760             var t = new Roo.Template(
42761                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42762                     '<label for="{0}" style="{2}">{1}{4}</label>',
42763                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42764                     '</div>',
42765                 '</div>'
42766             );
42767             t.disableFormats = true;
42768             t.compile();
42769             Roo.form.Layout.prototype.rowTpl = t;
42770         }
42771         this.fieldTpl = this.rowTpl;
42772         
42773         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42774         var labelWidth = 100;
42775         
42776         if ((this.labelAlign != 'top')) {
42777             if (typeof this.labelWidth == 'number') {
42778                 labelWidth = this.labelWidth
42779             }
42780             this.padWidth =  20 + labelWidth;
42781             
42782         }
42783         
42784         Roo.form.Column.superclass.onRender.call(this, ct, position);
42785         if(this.width){
42786             this.el.setWidth(this.width);
42787         }
42788         if(this.height){
42789             this.el.setHeight(this.height);
42790         }
42791     },
42792     
42793     // private
42794     renderField : function(f){
42795         f.fieldEl = this.fieldTpl.append(this.el, [
42796                f.id, f.fieldLabel,
42797                f.labelStyle||this.labelStyle||'',
42798                this.elementStyle||'',
42799                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42800                f.itemCls||this.itemCls||'',
42801                f.width ? f.width + this.padWidth : 160 + this.padWidth
42802        ],true);
42803     }
42804 });
42805  
42806
42807 /**
42808  * @class Roo.form.FieldSet
42809  * @extends Roo.form.Layout
42810  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42811  * @constructor
42812  * @param {Object} config Configuration options
42813  */
42814 Roo.form.FieldSet = function(config){
42815     Roo.form.FieldSet.superclass.constructor.call(this, config);
42816 };
42817
42818 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42819     /**
42820      * @cfg {String} legend
42821      * The text to display as the legend for the FieldSet (defaults to '')
42822      */
42823     /**
42824      * @cfg {String/Object} autoCreate
42825      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42826      */
42827
42828     // private
42829     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42830
42831     // private
42832     onRender : function(ct, position){
42833         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42834         if(this.legend){
42835             this.setLegend(this.legend);
42836         }
42837     },
42838
42839     // private
42840     setLegend : function(text){
42841         if(this.rendered){
42842             this.el.child('legend').update(text);
42843         }
42844     }
42845 });/*
42846  * Based on:
42847  * Ext JS Library 1.1.1
42848  * Copyright(c) 2006-2007, Ext JS, LLC.
42849  *
42850  * Originally Released Under LGPL - original licence link has changed is not relivant.
42851  *
42852  * Fork - LGPL
42853  * <script type="text/javascript">
42854  */
42855 /**
42856  * @class Roo.form.VTypes
42857  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42858  * @singleton
42859  */
42860 Roo.form.VTypes = function(){
42861     // closure these in so they are only created once.
42862     var alpha = /^[a-zA-Z_]+$/;
42863     var alphanum = /^[a-zA-Z0-9_]+$/;
42864     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42865     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42866
42867     // All these messages and functions are configurable
42868     return {
42869         /**
42870          * The function used to validate email addresses
42871          * @param {String} value The email address
42872          */
42873         'email' : function(v){
42874             return email.test(v);
42875         },
42876         /**
42877          * The error text to display when the email validation function returns false
42878          * @type String
42879          */
42880         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42881         /**
42882          * The keystroke filter mask to be applied on email input
42883          * @type RegExp
42884          */
42885         'emailMask' : /[a-z0-9_\.\-@]/i,
42886
42887         /**
42888          * The function used to validate URLs
42889          * @param {String} value The URL
42890          */
42891         'url' : function(v){
42892             return url.test(v);
42893         },
42894         /**
42895          * The error text to display when the url validation function returns false
42896          * @type String
42897          */
42898         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42899         
42900         /**
42901          * The function used to validate alpha values
42902          * @param {String} value The value
42903          */
42904         'alpha' : function(v){
42905             return alpha.test(v);
42906         },
42907         /**
42908          * The error text to display when the alpha validation function returns false
42909          * @type String
42910          */
42911         'alphaText' : 'This field should only contain letters and _',
42912         /**
42913          * The keystroke filter mask to be applied on alpha input
42914          * @type RegExp
42915          */
42916         'alphaMask' : /[a-z_]/i,
42917
42918         /**
42919          * The function used to validate alphanumeric values
42920          * @param {String} value The value
42921          */
42922         'alphanum' : function(v){
42923             return alphanum.test(v);
42924         },
42925         /**
42926          * The error text to display when the alphanumeric validation function returns false
42927          * @type String
42928          */
42929         'alphanumText' : 'This field should only contain letters, numbers and _',
42930         /**
42931          * The keystroke filter mask to be applied on alphanumeric input
42932          * @type RegExp
42933          */
42934         'alphanumMask' : /[a-z0-9_]/i
42935     };
42936 }();//<script type="text/javascript">
42937
42938 /**
42939  * @class Roo.form.FCKeditor
42940  * @extends Roo.form.TextArea
42941  * Wrapper around the FCKEditor http://www.fckeditor.net
42942  * @constructor
42943  * Creates a new FCKeditor
42944  * @param {Object} config Configuration options
42945  */
42946 Roo.form.FCKeditor = function(config){
42947     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42948     this.addEvents({
42949          /**
42950          * @event editorinit
42951          * Fired when the editor is initialized - you can add extra handlers here..
42952          * @param {FCKeditor} this
42953          * @param {Object} the FCK object.
42954          */
42955         editorinit : true
42956     });
42957     
42958     
42959 };
42960 Roo.form.FCKeditor.editors = { };
42961 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42962 {
42963     //defaultAutoCreate : {
42964     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42965     //},
42966     // private
42967     /**
42968      * @cfg {Object} fck options - see fck manual for details.
42969      */
42970     fckconfig : false,
42971     
42972     /**
42973      * @cfg {Object} fck toolbar set (Basic or Default)
42974      */
42975     toolbarSet : 'Basic',
42976     /**
42977      * @cfg {Object} fck BasePath
42978      */ 
42979     basePath : '/fckeditor/',
42980     
42981     
42982     frame : false,
42983     
42984     value : '',
42985     
42986    
42987     onRender : function(ct, position)
42988     {
42989         if(!this.el){
42990             this.defaultAutoCreate = {
42991                 tag: "textarea",
42992                 style:"width:300px;height:60px;",
42993                 autocomplete: "off"
42994             };
42995         }
42996         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42997         /*
42998         if(this.grow){
42999             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
43000             if(this.preventScrollbars){
43001                 this.el.setStyle("overflow", "hidden");
43002             }
43003             this.el.setHeight(this.growMin);
43004         }
43005         */
43006         //console.log('onrender' + this.getId() );
43007         Roo.form.FCKeditor.editors[this.getId()] = this;
43008          
43009
43010         this.replaceTextarea() ;
43011         
43012     },
43013     
43014     getEditor : function() {
43015         return this.fckEditor;
43016     },
43017     /**
43018      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
43019      * @param {Mixed} value The value to set
43020      */
43021     
43022     
43023     setValue : function(value)
43024     {
43025         //console.log('setValue: ' + value);
43026         
43027         if(typeof(value) == 'undefined') { // not sure why this is happending...
43028             return;
43029         }
43030         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43031         
43032         //if(!this.el || !this.getEditor()) {
43033         //    this.value = value;
43034             //this.setValue.defer(100,this,[value]);    
43035         //    return;
43036         //} 
43037         
43038         if(!this.getEditor()) {
43039             return;
43040         }
43041         
43042         this.getEditor().SetData(value);
43043         
43044         //
43045
43046     },
43047
43048     /**
43049      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
43050      * @return {Mixed} value The field value
43051      */
43052     getValue : function()
43053     {
43054         
43055         if (this.frame && this.frame.dom.style.display == 'none') {
43056             return Roo.form.FCKeditor.superclass.getValue.call(this);
43057         }
43058         
43059         if(!this.el || !this.getEditor()) {
43060            
43061            // this.getValue.defer(100,this); 
43062             return this.value;
43063         }
43064        
43065         
43066         var value=this.getEditor().GetData();
43067         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
43068         return Roo.form.FCKeditor.superclass.getValue.call(this);
43069         
43070
43071     },
43072
43073     /**
43074      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
43075      * @return {Mixed} value The field value
43076      */
43077     getRawValue : function()
43078     {
43079         if (this.frame && this.frame.dom.style.display == 'none') {
43080             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43081         }
43082         
43083         if(!this.el || !this.getEditor()) {
43084             //this.getRawValue.defer(100,this); 
43085             return this.value;
43086             return;
43087         }
43088         
43089         
43090         
43091         var value=this.getEditor().GetData();
43092         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
43093         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
43094          
43095     },
43096     
43097     setSize : function(w,h) {
43098         
43099         
43100         
43101         //if (this.frame && this.frame.dom.style.display == 'none') {
43102         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43103         //    return;
43104         //}
43105         //if(!this.el || !this.getEditor()) {
43106         //    this.setSize.defer(100,this, [w,h]); 
43107         //    return;
43108         //}
43109         
43110         
43111         
43112         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
43113         
43114         this.frame.dom.setAttribute('width', w);
43115         this.frame.dom.setAttribute('height', h);
43116         this.frame.setSize(w,h);
43117         
43118     },
43119     
43120     toggleSourceEdit : function(value) {
43121         
43122       
43123          
43124         this.el.dom.style.display = value ? '' : 'none';
43125         this.frame.dom.style.display = value ?  'none' : '';
43126         
43127     },
43128     
43129     
43130     focus: function(tag)
43131     {
43132         if (this.frame.dom.style.display == 'none') {
43133             return Roo.form.FCKeditor.superclass.focus.call(this);
43134         }
43135         if(!this.el || !this.getEditor()) {
43136             this.focus.defer(100,this, [tag]); 
43137             return;
43138         }
43139         
43140         
43141         
43142         
43143         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
43144         this.getEditor().Focus();
43145         if (tgs.length) {
43146             if (!this.getEditor().Selection.GetSelection()) {
43147                 this.focus.defer(100,this, [tag]); 
43148                 return;
43149             }
43150             
43151             
43152             var r = this.getEditor().EditorDocument.createRange();
43153             r.setStart(tgs[0],0);
43154             r.setEnd(tgs[0],0);
43155             this.getEditor().Selection.GetSelection().removeAllRanges();
43156             this.getEditor().Selection.GetSelection().addRange(r);
43157             this.getEditor().Focus();
43158         }
43159         
43160     },
43161     
43162     
43163     
43164     replaceTextarea : function()
43165     {
43166         if ( document.getElementById( this.getId() + '___Frame' ) )
43167             return ;
43168         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43169         //{
43170             // We must check the elements firstly using the Id and then the name.
43171         var oTextarea = document.getElementById( this.getId() );
43172         
43173         var colElementsByName = document.getElementsByName( this.getId() ) ;
43174          
43175         oTextarea.style.display = 'none' ;
43176
43177         if ( oTextarea.tabIndex ) {            
43178             this.TabIndex = oTextarea.tabIndex ;
43179         }
43180         
43181         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43182         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43183         this.frame = Roo.get(this.getId() + '___Frame')
43184     },
43185     
43186     _getConfigHtml : function()
43187     {
43188         var sConfig = '' ;
43189
43190         for ( var o in this.fckconfig ) {
43191             sConfig += sConfig.length > 0  ? '&amp;' : '';
43192             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43193         }
43194
43195         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43196     },
43197     
43198     
43199     _getIFrameHtml : function()
43200     {
43201         var sFile = 'fckeditor.html' ;
43202         /* no idea what this is about..
43203         try
43204         {
43205             if ( (/fcksource=true/i).test( window.top.location.search ) )
43206                 sFile = 'fckeditor.original.html' ;
43207         }
43208         catch (e) { 
43209         */
43210
43211         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43212         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43213         
43214         
43215         var html = '<iframe id="' + this.getId() +
43216             '___Frame" src="' + sLink +
43217             '" width="' + this.width +
43218             '" height="' + this.height + '"' +
43219             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43220             ' frameborder="0" scrolling="no"></iframe>' ;
43221
43222         return html ;
43223     },
43224     
43225     _insertHtmlBefore : function( html, element )
43226     {
43227         if ( element.insertAdjacentHTML )       {
43228             // IE
43229             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43230         } else { // Gecko
43231             var oRange = document.createRange() ;
43232             oRange.setStartBefore( element ) ;
43233             var oFragment = oRange.createContextualFragment( html );
43234             element.parentNode.insertBefore( oFragment, element ) ;
43235         }
43236     }
43237     
43238     
43239   
43240     
43241     
43242     
43243     
43244
43245 });
43246
43247 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43248
43249 function FCKeditor_OnComplete(editorInstance){
43250     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43251     f.fckEditor = editorInstance;
43252     //console.log("loaded");
43253     f.fireEvent('editorinit', f, editorInstance);
43254
43255   
43256
43257  
43258
43259
43260
43261
43262
43263
43264
43265
43266
43267
43268
43269
43270
43271
43272
43273 //<script type="text/javascript">
43274 /**
43275  * @class Roo.form.GridField
43276  * @extends Roo.form.Field
43277  * Embed a grid (or editable grid into a form)
43278  * STATUS ALPHA
43279  * 
43280  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43281  * it needs 
43282  * xgrid.store = Roo.data.Store
43283  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43284  * xgrid.store.reader = Roo.data.JsonReader 
43285  * 
43286  * 
43287  * @constructor
43288  * Creates a new GridField
43289  * @param {Object} config Configuration options
43290  */
43291 Roo.form.GridField = function(config){
43292     Roo.form.GridField.superclass.constructor.call(this, config);
43293      
43294 };
43295
43296 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43297     /**
43298      * @cfg {Number} width  - used to restrict width of grid..
43299      */
43300     width : 100,
43301     /**
43302      * @cfg {Number} height - used to restrict height of grid..
43303      */
43304     height : 50,
43305      /**
43306      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43307          * 
43308          *}
43309      */
43310     xgrid : false, 
43311     /**
43312      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43313      * {tag: "input", type: "checkbox", autocomplete: "off"})
43314      */
43315    // defaultAutoCreate : { tag: 'div' },
43316     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43317     /**
43318      * @cfg {String} addTitle Text to include for adding a title.
43319      */
43320     addTitle : false,
43321     //
43322     onResize : function(){
43323         Roo.form.Field.superclass.onResize.apply(this, arguments);
43324     },
43325
43326     initEvents : function(){
43327         // Roo.form.Checkbox.superclass.initEvents.call(this);
43328         // has no events...
43329        
43330     },
43331
43332
43333     getResizeEl : function(){
43334         return this.wrap;
43335     },
43336
43337     getPositionEl : function(){
43338         return this.wrap;
43339     },
43340
43341     // private
43342     onRender : function(ct, position){
43343         
43344         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43345         var style = this.style;
43346         delete this.style;
43347         
43348         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43349         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43350         this.viewEl = this.wrap.createChild({ tag: 'div' });
43351         if (style) {
43352             this.viewEl.applyStyles(style);
43353         }
43354         if (this.width) {
43355             this.viewEl.setWidth(this.width);
43356         }
43357         if (this.height) {
43358             this.viewEl.setHeight(this.height);
43359         }
43360         //if(this.inputValue !== undefined){
43361         //this.setValue(this.value);
43362         
43363         
43364         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43365         
43366         
43367         this.grid.render();
43368         this.grid.getDataSource().on('remove', this.refreshValue, this);
43369         this.grid.getDataSource().on('update', this.refreshValue, this);
43370         this.grid.on('afteredit', this.refreshValue, this);
43371  
43372     },
43373      
43374     
43375     /**
43376      * Sets the value of the item. 
43377      * @param {String} either an object  or a string..
43378      */
43379     setValue : function(v){
43380         //this.value = v;
43381         v = v || []; // empty set..
43382         // this does not seem smart - it really only affects memoryproxy grids..
43383         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43384             var ds = this.grid.getDataSource();
43385             // assumes a json reader..
43386             var data = {}
43387             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43388             ds.loadData( data);
43389         }
43390         // clear selection so it does not get stale.
43391         if (this.grid.sm) { 
43392             this.grid.sm.clearSelections();
43393         }
43394         
43395         Roo.form.GridField.superclass.setValue.call(this, v);
43396         this.refreshValue();
43397         // should load data in the grid really....
43398     },
43399     
43400     // private
43401     refreshValue: function() {
43402          var val = [];
43403         this.grid.getDataSource().each(function(r) {
43404             val.push(r.data);
43405         });
43406         this.el.dom.value = Roo.encode(val);
43407     }
43408     
43409      
43410     
43411     
43412 });/*
43413  * Based on:
43414  * Ext JS Library 1.1.1
43415  * Copyright(c) 2006-2007, Ext JS, LLC.
43416  *
43417  * Originally Released Under LGPL - original licence link has changed is not relivant.
43418  *
43419  * Fork - LGPL
43420  * <script type="text/javascript">
43421  */
43422 /**
43423  * @class Roo.form.DisplayField
43424  * @extends Roo.form.Field
43425  * A generic Field to display non-editable data.
43426  * @constructor
43427  * Creates a new Display Field item.
43428  * @param {Object} config Configuration options
43429  */
43430 Roo.form.DisplayField = function(config){
43431     Roo.form.DisplayField.superclass.constructor.call(this, config);
43432     
43433 };
43434
43435 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43436     inputType:      'hidden',
43437     allowBlank:     true,
43438     readOnly:         true,
43439     
43440  
43441     /**
43442      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43443      */
43444     focusClass : undefined,
43445     /**
43446      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43447      */
43448     fieldClass: 'x-form-field',
43449     
43450      /**
43451      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43452      */
43453     valueRenderer: undefined,
43454     
43455     width: 100,
43456     /**
43457      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43458      * {tag: "input", type: "checkbox", autocomplete: "off"})
43459      */
43460      
43461  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43462
43463     onResize : function(){
43464         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43465         
43466     },
43467
43468     initEvents : function(){
43469         // Roo.form.Checkbox.superclass.initEvents.call(this);
43470         // has no events...
43471        
43472     },
43473
43474
43475     getResizeEl : function(){
43476         return this.wrap;
43477     },
43478
43479     getPositionEl : function(){
43480         return this.wrap;
43481     },
43482
43483     // private
43484     onRender : function(ct, position){
43485         
43486         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43487         //if(this.inputValue !== undefined){
43488         this.wrap = this.el.wrap();
43489         
43490         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43491         
43492         if (this.bodyStyle) {
43493             this.viewEl.applyStyles(this.bodyStyle);
43494         }
43495         //this.viewEl.setStyle('padding', '2px');
43496         
43497         this.setValue(this.value);
43498         
43499     },
43500 /*
43501     // private
43502     initValue : Roo.emptyFn,
43503
43504   */
43505
43506         // private
43507     onClick : function(){
43508         
43509     },
43510
43511     /**
43512      * Sets the checked state of the checkbox.
43513      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43514      */
43515     setValue : function(v){
43516         this.value = v;
43517         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43518         // this might be called before we have a dom element..
43519         if (!this.viewEl) {
43520             return;
43521         }
43522         this.viewEl.dom.innerHTML = html;
43523         Roo.form.DisplayField.superclass.setValue.call(this, v);
43524
43525     }
43526 });/*
43527  * 
43528  * Licence- LGPL
43529  * 
43530  */
43531
43532 /**
43533  * @class Roo.form.DayPicker
43534  * @extends Roo.form.Field
43535  * A Day picker show [M] [T] [W] ....
43536  * @constructor
43537  * Creates a new Day Picker
43538  * @param {Object} config Configuration options
43539  */
43540 Roo.form.DayPicker= function(config){
43541     Roo.form.DayPicker.superclass.constructor.call(this, config);
43542      
43543 };
43544
43545 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43546     /**
43547      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43548      */
43549     focusClass : undefined,
43550     /**
43551      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43552      */
43553     fieldClass: "x-form-field",
43554    
43555     /**
43556      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43557      * {tag: "input", type: "checkbox", autocomplete: "off"})
43558      */
43559     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43560     
43561    
43562     actionMode : 'viewEl', 
43563     //
43564     // private
43565  
43566     inputType : 'hidden',
43567     
43568      
43569     inputElement: false, // real input element?
43570     basedOn: false, // ????
43571     
43572     isFormField: true, // not sure where this is needed!!!!
43573
43574     onResize : function(){
43575         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43576         if(!this.boxLabel){
43577             this.el.alignTo(this.wrap, 'c-c');
43578         }
43579     },
43580
43581     initEvents : function(){
43582         Roo.form.Checkbox.superclass.initEvents.call(this);
43583         this.el.on("click", this.onClick,  this);
43584         this.el.on("change", this.onClick,  this);
43585     },
43586
43587
43588     getResizeEl : function(){
43589         return this.wrap;
43590     },
43591
43592     getPositionEl : function(){
43593         return this.wrap;
43594     },
43595
43596     
43597     // private
43598     onRender : function(ct, position){
43599         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43600        
43601         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43602         
43603         var r1 = '<table><tr>';
43604         var r2 = '<tr class="x-form-daypick-icons">';
43605         for (var i=0; i < 7; i++) {
43606             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43607             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43608         }
43609         
43610         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43611         viewEl.select('img').on('click', this.onClick, this);
43612         this.viewEl = viewEl;   
43613         
43614         
43615         // this will not work on Chrome!!!
43616         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43617         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43618         
43619         
43620           
43621
43622     },
43623
43624     // private
43625     initValue : Roo.emptyFn,
43626
43627     /**
43628      * Returns the checked state of the checkbox.
43629      * @return {Boolean} True if checked, else false
43630      */
43631     getValue : function(){
43632         return this.el.dom.value;
43633         
43634     },
43635
43636         // private
43637     onClick : function(e){ 
43638         //this.setChecked(!this.checked);
43639         Roo.get(e.target).toggleClass('x-menu-item-checked');
43640         this.refreshValue();
43641         //if(this.el.dom.checked != this.checked){
43642         //    this.setValue(this.el.dom.checked);
43643        // }
43644     },
43645     
43646     // private
43647     refreshValue : function()
43648     {
43649         var val = '';
43650         this.viewEl.select('img',true).each(function(e,i,n)  {
43651             val += e.is(".x-menu-item-checked") ? String(n) : '';
43652         });
43653         this.setValue(val, true);
43654     },
43655
43656     /**
43657      * Sets the checked state of the checkbox.
43658      * On is always based on a string comparison between inputValue and the param.
43659      * @param {Boolean/String} value - the value to set 
43660      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43661      */
43662     setValue : function(v,suppressEvent){
43663         if (!this.el.dom) {
43664             return;
43665         }
43666         var old = this.el.dom.value ;
43667         this.el.dom.value = v;
43668         if (suppressEvent) {
43669             return ;
43670         }
43671          
43672         // update display..
43673         this.viewEl.select('img',true).each(function(e,i,n)  {
43674             
43675             var on = e.is(".x-menu-item-checked");
43676             var newv = v.indexOf(String(n)) > -1;
43677             if (on != newv) {
43678                 e.toggleClass('x-menu-item-checked');
43679             }
43680             
43681         });
43682         
43683         
43684         this.fireEvent('change', this, v, old);
43685         
43686         
43687     },
43688    
43689     // handle setting of hidden value by some other method!!?!?
43690     setFromHidden: function()
43691     {
43692         if(!this.el){
43693             return;
43694         }
43695         //console.log("SET FROM HIDDEN");
43696         //alert('setFrom hidden');
43697         this.setValue(this.el.dom.value);
43698     },
43699     
43700     onDestroy : function()
43701     {
43702         if(this.viewEl){
43703             Roo.get(this.viewEl).remove();
43704         }
43705          
43706         Roo.form.DayPicker.superclass.onDestroy.call(this);
43707     }
43708
43709 });/*
43710  * RooJS Library 1.1.1
43711  * Copyright(c) 2008-2011  Alan Knowles
43712  *
43713  * License - LGPL
43714  */
43715  
43716
43717 /**
43718  * @class Roo.form.ComboCheck
43719  * @extends Roo.form.ComboBox
43720  * A combobox for multiple select items.
43721  *
43722  * FIXME - could do with a reset button..
43723  * 
43724  * @constructor
43725  * Create a new ComboCheck
43726  * @param {Object} config Configuration options
43727  */
43728 Roo.form.ComboCheck = function(config){
43729     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43730     // should verify some data...
43731     // like
43732     // hiddenName = required..
43733     // displayField = required
43734     // valudField == required
43735     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43736     var _t = this;
43737     Roo.each(req, function(e) {
43738         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43739             throw "Roo.form.ComboCheck : missing value for: " + e;
43740         }
43741     });
43742     
43743     
43744 };
43745
43746 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43747      
43748      
43749     editable : false,
43750      
43751     selectedClass: 'x-menu-item-checked', 
43752     
43753     // private
43754     onRender : function(ct, position){
43755         var _t = this;
43756         
43757         
43758         
43759         if(!this.tpl){
43760             var cls = 'x-combo-list';
43761
43762             
43763             this.tpl =  new Roo.Template({
43764                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43765                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43766                    '<span>{' + this.displayField + '}</span>' +
43767                     '</div>' 
43768                 
43769             });
43770         }
43771  
43772         
43773         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43774         this.view.singleSelect = false;
43775         this.view.multiSelect = true;
43776         this.view.toggleSelect = true;
43777         this.pageTb.add(new Roo.Toolbar.Fill(), {
43778             
43779             text: 'Done',
43780             handler: function()
43781             {
43782                 _t.collapse();
43783             }
43784         });
43785     },
43786     
43787     onViewOver : function(e, t){
43788         // do nothing...
43789         return;
43790         
43791     },
43792     
43793     onViewClick : function(doFocus,index){
43794         return;
43795         
43796     },
43797     select: function () {
43798         //Roo.log("SELECT CALLED");
43799     },
43800      
43801     selectByValue : function(xv, scrollIntoView){
43802         var ar = this.getValueArray();
43803         var sels = [];
43804         
43805         Roo.each(ar, function(v) {
43806             if(v === undefined || v === null){
43807                 return;
43808             }
43809             var r = this.findRecord(this.valueField, v);
43810             if(r){
43811                 sels.push(this.store.indexOf(r))
43812                 
43813             }
43814         },this);
43815         this.view.select(sels);
43816         return false;
43817     },
43818     
43819     
43820     
43821     onSelect : function(record, index){
43822        // Roo.log("onselect Called");
43823        // this is only called by the clear button now..
43824         this.view.clearSelections();
43825         this.setValue('[]');
43826         if (this.value != this.valueBefore) {
43827             this.fireEvent('change', this, this.value, this.valueBefore);
43828         }
43829     },
43830     getValueArray : function()
43831     {
43832         var ar = [] ;
43833         
43834         try {
43835             //Roo.log(this.value);
43836             if (typeof(this.value) == 'undefined') {
43837                 return [];
43838             }
43839             var ar = Roo.decode(this.value);
43840             return  ar instanceof Array ? ar : []; //?? valid?
43841             
43842         } catch(e) {
43843             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43844             return [];
43845         }
43846          
43847     },
43848     expand : function ()
43849     {
43850         Roo.form.ComboCheck.superclass.expand.call(this);
43851         this.valueBefore = this.value;
43852         
43853
43854     },
43855     
43856     collapse : function(){
43857         Roo.form.ComboCheck.superclass.collapse.call(this);
43858         var sl = this.view.getSelectedIndexes();
43859         var st = this.store;
43860         var nv = [];
43861         var tv = [];
43862         var r;
43863         Roo.each(sl, function(i) {
43864             r = st.getAt(i);
43865             nv.push(r.get(this.valueField));
43866         },this);
43867         this.setValue(Roo.encode(nv));
43868         if (this.value != this.valueBefore) {
43869
43870             this.fireEvent('change', this, this.value, this.valueBefore);
43871         }
43872         
43873     },
43874     
43875     setValue : function(v){
43876         // Roo.log(v);
43877         this.value = v;
43878         
43879         var vals = this.getValueArray();
43880         var tv = [];
43881         Roo.each(vals, function(k) {
43882             var r = this.findRecord(this.valueField, k);
43883             if(r){
43884                 tv.push(r.data[this.displayField]);
43885             }else if(this.valueNotFoundText !== undefined){
43886                 tv.push( this.valueNotFoundText );
43887             }
43888         },this);
43889        // Roo.log(tv);
43890         
43891         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43892         this.hiddenField.value = v;
43893         this.value = v;
43894     }
43895     
43896 });//<script type="text/javasscript">
43897  
43898
43899 /**
43900  * @class Roo.DDView
43901  * A DnD enabled version of Roo.View.
43902  * @param {Element/String} container The Element in which to create the View.
43903  * @param {String} tpl The template string used to create the markup for each element of the View
43904  * @param {Object} config The configuration properties. These include all the config options of
43905  * {@link Roo.View} plus some specific to this class.<br>
43906  * <p>
43907  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43908  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43909  * <p>
43910  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43911 .x-view-drag-insert-above {
43912         border-top:1px dotted #3366cc;
43913 }
43914 .x-view-drag-insert-below {
43915         border-bottom:1px dotted #3366cc;
43916 }
43917 </code></pre>
43918  * 
43919  */
43920  
43921 Roo.DDView = function(container, tpl, config) {
43922     Roo.DDView.superclass.constructor.apply(this, arguments);
43923     this.getEl().setStyle("outline", "0px none");
43924     this.getEl().unselectable();
43925     if (this.dragGroup) {
43926                 this.setDraggable(this.dragGroup.split(","));
43927     }
43928     if (this.dropGroup) {
43929                 this.setDroppable(this.dropGroup.split(","));
43930     }
43931     if (this.deletable) {
43932         this.setDeletable();
43933     }
43934     this.isDirtyFlag = false;
43935         this.addEvents({
43936                 "drop" : true
43937         });
43938 };
43939
43940 Roo.extend(Roo.DDView, Roo.View, {
43941 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43942 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43943 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43944 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43945
43946         isFormField: true,
43947
43948         reset: Roo.emptyFn,
43949         
43950         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43951
43952         validate: function() {
43953                 return true;
43954         },
43955         
43956         destroy: function() {
43957                 this.purgeListeners();
43958                 this.getEl.removeAllListeners();
43959                 this.getEl().remove();
43960                 if (this.dragZone) {
43961                         if (this.dragZone.destroy) {
43962                                 this.dragZone.destroy();
43963                         }
43964                 }
43965                 if (this.dropZone) {
43966                         if (this.dropZone.destroy) {
43967                                 this.dropZone.destroy();
43968                         }
43969                 }
43970         },
43971
43972 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43973         getName: function() {
43974                 return this.name;
43975         },
43976
43977 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43978         setValue: function(v) {
43979                 if (!this.store) {
43980                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43981                 }
43982                 var data = {};
43983                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43984                 this.store.proxy = new Roo.data.MemoryProxy(data);
43985                 this.store.load();
43986         },
43987
43988 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43989         getValue: function() {
43990                 var result = '(';
43991                 this.store.each(function(rec) {
43992                         result += rec.id + ',';
43993                 });
43994                 return result.substr(0, result.length - 1) + ')';
43995         },
43996         
43997         getIds: function() {
43998                 var i = 0, result = new Array(this.store.getCount());
43999                 this.store.each(function(rec) {
44000                         result[i++] = rec.id;
44001                 });
44002                 return result;
44003         },
44004         
44005         isDirty: function() {
44006                 return this.isDirtyFlag;
44007         },
44008
44009 /**
44010  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
44011  *      whole Element becomes the target, and this causes the drop gesture to append.
44012  */
44013     getTargetFromEvent : function(e) {
44014                 var target = e.getTarget();
44015                 while ((target !== null) && (target.parentNode != this.el.dom)) {
44016                 target = target.parentNode;
44017                 }
44018                 if (!target) {
44019                         target = this.el.dom.lastChild || this.el.dom;
44020                 }
44021                 return target;
44022     },
44023
44024 /**
44025  *      Create the drag data which consists of an object which has the property "ddel" as
44026  *      the drag proxy element. 
44027  */
44028     getDragData : function(e) {
44029         var target = this.findItemFromChild(e.getTarget());
44030                 if(target) {
44031                         this.handleSelection(e);
44032                         var selNodes = this.getSelectedNodes();
44033             var dragData = {
44034                 source: this,
44035                 copy: this.copy || (this.allowCopy && e.ctrlKey),
44036                 nodes: selNodes,
44037                 records: []
44038                         };
44039                         var selectedIndices = this.getSelectedIndexes();
44040                         for (var i = 0; i < selectedIndices.length; i++) {
44041                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
44042                         }
44043                         if (selNodes.length == 1) {
44044                                 dragData.ddel = target.cloneNode(true); // the div element
44045                         } else {
44046                                 var div = document.createElement('div'); // create the multi element drag "ghost"
44047                                 div.className = 'multi-proxy';
44048                                 for (var i = 0, len = selNodes.length; i < len; i++) {
44049                                         div.appendChild(selNodes[i].cloneNode(true));
44050                                 }
44051                                 dragData.ddel = div;
44052                         }
44053             //console.log(dragData)
44054             //console.log(dragData.ddel.innerHTML)
44055                         return dragData;
44056                 }
44057         //console.log('nodragData')
44058                 return false;
44059     },
44060     
44061 /**     Specify to which ddGroup items in this DDView may be dragged. */
44062     setDraggable: function(ddGroup) {
44063         if (ddGroup instanceof Array) {
44064                 Roo.each(ddGroup, this.setDraggable, this);
44065                 return;
44066         }
44067         if (this.dragZone) {
44068                 this.dragZone.addToGroup(ddGroup);
44069         } else {
44070                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
44071                                 containerScroll: true,
44072                                 ddGroup: ddGroup 
44073
44074                         });
44075 //                      Draggability implies selection. DragZone's mousedown selects the element.
44076                         if (!this.multiSelect) { this.singleSelect = true; }
44077
44078 //                      Wire the DragZone's handlers up to methods in *this*
44079                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
44080                 }
44081     },
44082
44083 /**     Specify from which ddGroup this DDView accepts drops. */
44084     setDroppable: function(ddGroup) {
44085         if (ddGroup instanceof Array) {
44086                 Roo.each(ddGroup, this.setDroppable, this);
44087                 return;
44088         }
44089         if (this.dropZone) {
44090                 this.dropZone.addToGroup(ddGroup);
44091         } else {
44092                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
44093                                 containerScroll: true,
44094                                 ddGroup: ddGroup
44095                         });
44096
44097 //                      Wire the DropZone's handlers up to methods in *this*
44098                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
44099                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
44100                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
44101                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
44102                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
44103                 }
44104     },
44105
44106 /**     Decide whether to drop above or below a View node. */
44107     getDropPoint : function(e, n, dd){
44108         if (n == this.el.dom) { return "above"; }
44109                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
44110                 var c = t + (b - t) / 2;
44111                 var y = Roo.lib.Event.getPageY(e);
44112                 if(y <= c) {
44113                         return "above";
44114                 }else{
44115                         return "below";
44116                 }
44117     },
44118
44119     onNodeEnter : function(n, dd, e, data){
44120                 return false;
44121     },
44122     
44123     onNodeOver : function(n, dd, e, data){
44124                 var pt = this.getDropPoint(e, n, dd);
44125                 // set the insert point style on the target node
44126                 var dragElClass = this.dropNotAllowed;
44127                 if (pt) {
44128                         var targetElClass;
44129                         if (pt == "above"){
44130                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
44131                                 targetElClass = "x-view-drag-insert-above";
44132                         } else {
44133                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
44134                                 targetElClass = "x-view-drag-insert-below";
44135                         }
44136                         if (this.lastInsertClass != targetElClass){
44137                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
44138                                 this.lastInsertClass = targetElClass;
44139                         }
44140                 }
44141                 return dragElClass;
44142         },
44143
44144     onNodeOut : function(n, dd, e, data){
44145                 this.removeDropIndicators(n);
44146     },
44147
44148     onNodeDrop : function(n, dd, e, data){
44149         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44150                 return false;
44151         }
44152         var pt = this.getDropPoint(e, n, dd);
44153                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44154                 if (pt == "below") { insertAt++; }
44155                 for (var i = 0; i < data.records.length; i++) {
44156                         var r = data.records[i];
44157                         var dup = this.store.getById(r.id);
44158                         if (dup && (dd != this.dragZone)) {
44159                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44160                         } else {
44161                                 if (data.copy) {
44162                                         this.store.insert(insertAt++, r.copy());
44163                                 } else {
44164                                         data.source.isDirtyFlag = true;
44165                                         r.store.remove(r);
44166                                         this.store.insert(insertAt++, r);
44167                                 }
44168                                 this.isDirtyFlag = true;
44169                         }
44170                 }
44171                 this.dragZone.cachedTarget = null;
44172                 return true;
44173     },
44174
44175     removeDropIndicators : function(n){
44176                 if(n){
44177                         Roo.fly(n).removeClass([
44178                                 "x-view-drag-insert-above",
44179                                 "x-view-drag-insert-below"]);
44180                         this.lastInsertClass = "_noclass";
44181                 }
44182     },
44183
44184 /**
44185  *      Utility method. Add a delete option to the DDView's context menu.
44186  *      @param {String} imageUrl The URL of the "delete" icon image.
44187  */
44188         setDeletable: function(imageUrl) {
44189                 if (!this.singleSelect && !this.multiSelect) {
44190                         this.singleSelect = true;
44191                 }
44192                 var c = this.getContextMenu();
44193                 this.contextMenu.on("itemclick", function(item) {
44194                         switch (item.id) {
44195                                 case "delete":
44196                                         this.remove(this.getSelectedIndexes());
44197                                         break;
44198                         }
44199                 }, this);
44200                 this.contextMenu.add({
44201                         icon: imageUrl,
44202                         id: "delete",
44203                         text: 'Delete'
44204                 });
44205         },
44206         
44207 /**     Return the context menu for this DDView. */
44208         getContextMenu: function() {
44209                 if (!this.contextMenu) {
44210 //                      Create the View's context menu
44211                         this.contextMenu = new Roo.menu.Menu({
44212                                 id: this.id + "-contextmenu"
44213                         });
44214                         this.el.on("contextmenu", this.showContextMenu, this);
44215                 }
44216                 return this.contextMenu;
44217         },
44218         
44219         disableContextMenu: function() {
44220                 if (this.contextMenu) {
44221                         this.el.un("contextmenu", this.showContextMenu, this);
44222                 }
44223         },
44224
44225         showContextMenu: function(e, item) {
44226         item = this.findItemFromChild(e.getTarget());
44227                 if (item) {
44228                         e.stopEvent();
44229                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44230                         this.contextMenu.showAt(e.getXY());
44231             }
44232     },
44233
44234 /**
44235  *      Remove {@link Roo.data.Record}s at the specified indices.
44236  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44237  */
44238     remove: function(selectedIndices) {
44239                 selectedIndices = [].concat(selectedIndices);
44240                 for (var i = 0; i < selectedIndices.length; i++) {
44241                         var rec = this.store.getAt(selectedIndices[i]);
44242                         this.store.remove(rec);
44243                 }
44244     },
44245
44246 /**
44247  *      Double click fires the event, but also, if this is draggable, and there is only one other
44248  *      related DropZone, it transfers the selected node.
44249  */
44250     onDblClick : function(e){
44251         var item = this.findItemFromChild(e.getTarget());
44252         if(item){
44253             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44254                 return false;
44255             }
44256             if (this.dragGroup) {
44257                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44258                     while (targets.indexOf(this.dropZone) > -1) {
44259                             targets.remove(this.dropZone);
44260                                 }
44261                     if (targets.length == 1) {
44262                                         this.dragZone.cachedTarget = null;
44263                         var el = Roo.get(targets[0].getEl());
44264                         var box = el.getBox(true);
44265                         targets[0].onNodeDrop(el.dom, {
44266                                 target: el.dom,
44267                                 xy: [box.x, box.y + box.height - 1]
44268                         }, null, this.getDragData(e));
44269                     }
44270                 }
44271         }
44272     },
44273     
44274     handleSelection: function(e) {
44275                 this.dragZone.cachedTarget = null;
44276         var item = this.findItemFromChild(e.getTarget());
44277         if (!item) {
44278                 this.clearSelections(true);
44279                 return;
44280         }
44281                 if (item && (this.multiSelect || this.singleSelect)){
44282                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44283                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44284                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44285                                 this.unselect(item);
44286                         } else {
44287                                 this.select(item, this.multiSelect && e.ctrlKey);
44288                                 this.lastSelection = item;
44289                         }
44290                 }
44291     },
44292
44293     onItemClick : function(item, index, e){
44294                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44295                         return false;
44296                 }
44297                 return true;
44298     },
44299
44300     unselect : function(nodeInfo, suppressEvent){
44301                 var node = this.getNode(nodeInfo);
44302                 if(node && this.isSelected(node)){
44303                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44304                                 Roo.fly(node).removeClass(this.selectedClass);
44305                                 this.selections.remove(node);
44306                                 if(!suppressEvent){
44307                                         this.fireEvent("selectionchange", this, this.selections);
44308                                 }
44309                         }
44310                 }
44311     }
44312 });
44313 /*
44314  * Based on:
44315  * Ext JS Library 1.1.1
44316  * Copyright(c) 2006-2007, Ext JS, LLC.
44317  *
44318  * Originally Released Under LGPL - original licence link has changed is not relivant.
44319  *
44320  * Fork - LGPL
44321  * <script type="text/javascript">
44322  */
44323  
44324 /**
44325  * @class Roo.LayoutManager
44326  * @extends Roo.util.Observable
44327  * Base class for layout managers.
44328  */
44329 Roo.LayoutManager = function(container, config){
44330     Roo.LayoutManager.superclass.constructor.call(this);
44331     this.el = Roo.get(container);
44332     // ie scrollbar fix
44333     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44334         document.body.scroll = "no";
44335     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44336         this.el.position('relative');
44337     }
44338     this.id = this.el.id;
44339     this.el.addClass("x-layout-container");
44340     /** false to disable window resize monitoring @type Boolean */
44341     this.monitorWindowResize = true;
44342     this.regions = {};
44343     this.addEvents({
44344         /**
44345          * @event layout
44346          * Fires when a layout is performed. 
44347          * @param {Roo.LayoutManager} this
44348          */
44349         "layout" : true,
44350         /**
44351          * @event regionresized
44352          * Fires when the user resizes a region. 
44353          * @param {Roo.LayoutRegion} region The resized region
44354          * @param {Number} newSize The new size (width for east/west, height for north/south)
44355          */
44356         "regionresized" : true,
44357         /**
44358          * @event regioncollapsed
44359          * Fires when a region is collapsed. 
44360          * @param {Roo.LayoutRegion} region The collapsed region
44361          */
44362         "regioncollapsed" : true,
44363         /**
44364          * @event regionexpanded
44365          * Fires when a region is expanded.  
44366          * @param {Roo.LayoutRegion} region The expanded region
44367          */
44368         "regionexpanded" : true
44369     });
44370     this.updating = false;
44371     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44372 };
44373
44374 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44375     /**
44376      * Returns true if this layout is currently being updated
44377      * @return {Boolean}
44378      */
44379     isUpdating : function(){
44380         return this.updating; 
44381     },
44382     
44383     /**
44384      * Suspend the LayoutManager from doing auto-layouts while
44385      * making multiple add or remove calls
44386      */
44387     beginUpdate : function(){
44388         this.updating = true;    
44389     },
44390     
44391     /**
44392      * Restore auto-layouts and optionally disable the manager from performing a layout
44393      * @param {Boolean} noLayout true to disable a layout update 
44394      */
44395     endUpdate : function(noLayout){
44396         this.updating = false;
44397         if(!noLayout){
44398             this.layout();
44399         }    
44400     },
44401     
44402     layout: function(){
44403         
44404     },
44405     
44406     onRegionResized : function(region, newSize){
44407         this.fireEvent("regionresized", region, newSize);
44408         this.layout();
44409     },
44410     
44411     onRegionCollapsed : function(region){
44412         this.fireEvent("regioncollapsed", region);
44413     },
44414     
44415     onRegionExpanded : function(region){
44416         this.fireEvent("regionexpanded", region);
44417     },
44418         
44419     /**
44420      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44421      * performs box-model adjustments.
44422      * @return {Object} The size as an object {width: (the width), height: (the height)}
44423      */
44424     getViewSize : function(){
44425         var size;
44426         if(this.el.dom != document.body){
44427             size = this.el.getSize();
44428         }else{
44429             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44430         }
44431         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44432         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44433         return size;
44434     },
44435     
44436     /**
44437      * Returns the Element this layout is bound to.
44438      * @return {Roo.Element}
44439      */
44440     getEl : function(){
44441         return this.el;
44442     },
44443     
44444     /**
44445      * Returns the specified region.
44446      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44447      * @return {Roo.LayoutRegion}
44448      */
44449     getRegion : function(target){
44450         return this.regions[target.toLowerCase()];
44451     },
44452     
44453     onWindowResize : function(){
44454         if(this.monitorWindowResize){
44455             this.layout();
44456         }
44457     }
44458 });/*
44459  * Based on:
44460  * Ext JS Library 1.1.1
44461  * Copyright(c) 2006-2007, Ext JS, LLC.
44462  *
44463  * Originally Released Under LGPL - original licence link has changed is not relivant.
44464  *
44465  * Fork - LGPL
44466  * <script type="text/javascript">
44467  */
44468 /**
44469  * @class Roo.BorderLayout
44470  * @extends Roo.LayoutManager
44471  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44472  * please see: <br><br>
44473  * <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>
44474  * <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>
44475  * Example:
44476  <pre><code>
44477  var layout = new Roo.BorderLayout(document.body, {
44478     north: {
44479         initialSize: 25,
44480         titlebar: false
44481     },
44482     west: {
44483         split:true,
44484         initialSize: 200,
44485         minSize: 175,
44486         maxSize: 400,
44487         titlebar: true,
44488         collapsible: true
44489     },
44490     east: {
44491         split:true,
44492         initialSize: 202,
44493         minSize: 175,
44494         maxSize: 400,
44495         titlebar: true,
44496         collapsible: true
44497     },
44498     south: {
44499         split:true,
44500         initialSize: 100,
44501         minSize: 100,
44502         maxSize: 200,
44503         titlebar: true,
44504         collapsible: true
44505     },
44506     center: {
44507         titlebar: true,
44508         autoScroll:true,
44509         resizeTabs: true,
44510         minTabWidth: 50,
44511         preferredTabWidth: 150
44512     }
44513 });
44514
44515 // shorthand
44516 var CP = Roo.ContentPanel;
44517
44518 layout.beginUpdate();
44519 layout.add("north", new CP("north", "North"));
44520 layout.add("south", new CP("south", {title: "South", closable: true}));
44521 layout.add("west", new CP("west", {title: "West"}));
44522 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44523 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44524 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44525 layout.getRegion("center").showPanel("center1");
44526 layout.endUpdate();
44527 </code></pre>
44528
44529 <b>The container the layout is rendered into can be either the body element or any other element.
44530 If it is not the body element, the container needs to either be an absolute positioned element,
44531 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44532 the container size if it is not the body element.</b>
44533
44534 * @constructor
44535 * Create a new BorderLayout
44536 * @param {String/HTMLElement/Element} container The container this layout is bound to
44537 * @param {Object} config Configuration options
44538  */
44539 Roo.BorderLayout = function(container, config){
44540     config = config || {};
44541     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44542     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44543     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44544         var target = this.factory.validRegions[i];
44545         if(config[target]){
44546             this.addRegion(target, config[target]);
44547         }
44548     }
44549 };
44550
44551 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44552     /**
44553      * Creates and adds a new region if it doesn't already exist.
44554      * @param {String} target The target region key (north, south, east, west or center).
44555      * @param {Object} config The regions config object
44556      * @return {BorderLayoutRegion} The new region
44557      */
44558     addRegion : function(target, config){
44559         if(!this.regions[target]){
44560             var r = this.factory.create(target, this, config);
44561             this.bindRegion(target, r);
44562         }
44563         return this.regions[target];
44564     },
44565
44566     // private (kinda)
44567     bindRegion : function(name, r){
44568         this.regions[name] = r;
44569         r.on("visibilitychange", this.layout, this);
44570         r.on("paneladded", this.layout, this);
44571         r.on("panelremoved", this.layout, this);
44572         r.on("invalidated", this.layout, this);
44573         r.on("resized", this.onRegionResized, this);
44574         r.on("collapsed", this.onRegionCollapsed, this);
44575         r.on("expanded", this.onRegionExpanded, this);
44576     },
44577
44578     /**
44579      * Performs a layout update.
44580      */
44581     layout : function(){
44582         if(this.updating) return;
44583         var size = this.getViewSize();
44584         var w = size.width;
44585         var h = size.height;
44586         var centerW = w;
44587         var centerH = h;
44588         var centerY = 0;
44589         var centerX = 0;
44590         //var x = 0, y = 0;
44591
44592         var rs = this.regions;
44593         var north = rs["north"];
44594         var south = rs["south"]; 
44595         var west = rs["west"];
44596         var east = rs["east"];
44597         var center = rs["center"];
44598         //if(this.hideOnLayout){ // not supported anymore
44599             //c.el.setStyle("display", "none");
44600         //}
44601         if(north && north.isVisible()){
44602             var b = north.getBox();
44603             var m = north.getMargins();
44604             b.width = w - (m.left+m.right);
44605             b.x = m.left;
44606             b.y = m.top;
44607             centerY = b.height + b.y + m.bottom;
44608             centerH -= centerY;
44609             north.updateBox(this.safeBox(b));
44610         }
44611         if(south && south.isVisible()){
44612             var b = south.getBox();
44613             var m = south.getMargins();
44614             b.width = w - (m.left+m.right);
44615             b.x = m.left;
44616             var totalHeight = (b.height + m.top + m.bottom);
44617             b.y = h - totalHeight + m.top;
44618             centerH -= totalHeight;
44619             south.updateBox(this.safeBox(b));
44620         }
44621         if(west && west.isVisible()){
44622             var b = west.getBox();
44623             var m = west.getMargins();
44624             b.height = centerH - (m.top+m.bottom);
44625             b.x = m.left;
44626             b.y = centerY + m.top;
44627             var totalWidth = (b.width + m.left + m.right);
44628             centerX += totalWidth;
44629             centerW -= totalWidth;
44630             west.updateBox(this.safeBox(b));
44631         }
44632         if(east && east.isVisible()){
44633             var b = east.getBox();
44634             var m = east.getMargins();
44635             b.height = centerH - (m.top+m.bottom);
44636             var totalWidth = (b.width + m.left + m.right);
44637             b.x = w - totalWidth + m.left;
44638             b.y = centerY + m.top;
44639             centerW -= totalWidth;
44640             east.updateBox(this.safeBox(b));
44641         }
44642         if(center){
44643             var m = center.getMargins();
44644             var centerBox = {
44645                 x: centerX + m.left,
44646                 y: centerY + m.top,
44647                 width: centerW - (m.left+m.right),
44648                 height: centerH - (m.top+m.bottom)
44649             };
44650             //if(this.hideOnLayout){
44651                 //center.el.setStyle("display", "block");
44652             //}
44653             center.updateBox(this.safeBox(centerBox));
44654         }
44655         this.el.repaint();
44656         this.fireEvent("layout", this);
44657     },
44658
44659     // private
44660     safeBox : function(box){
44661         box.width = Math.max(0, box.width);
44662         box.height = Math.max(0, box.height);
44663         return box;
44664     },
44665
44666     /**
44667      * Adds a ContentPanel (or subclass) to this layout.
44668      * @param {String} target The target region key (north, south, east, west or center).
44669      * @param {Roo.ContentPanel} panel The panel to add
44670      * @return {Roo.ContentPanel} The added panel
44671      */
44672     add : function(target, panel){
44673          
44674         target = target.toLowerCase();
44675         return this.regions[target].add(panel);
44676     },
44677
44678     /**
44679      * Remove a ContentPanel (or subclass) to this layout.
44680      * @param {String} target The target region key (north, south, east, west or center).
44681      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44682      * @return {Roo.ContentPanel} The removed panel
44683      */
44684     remove : function(target, panel){
44685         target = target.toLowerCase();
44686         return this.regions[target].remove(panel);
44687     },
44688
44689     /**
44690      * Searches all regions for a panel with the specified id
44691      * @param {String} panelId
44692      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44693      */
44694     findPanel : function(panelId){
44695         var rs = this.regions;
44696         for(var target in rs){
44697             if(typeof rs[target] != "function"){
44698                 var p = rs[target].getPanel(panelId);
44699                 if(p){
44700                     return p;
44701                 }
44702             }
44703         }
44704         return null;
44705     },
44706
44707     /**
44708      * Searches all regions for a panel with the specified id and activates (shows) it.
44709      * @param {String/ContentPanel} panelId The panels id or the panel itself
44710      * @return {Roo.ContentPanel} The shown panel or null
44711      */
44712     showPanel : function(panelId) {
44713       var rs = this.regions;
44714       for(var target in rs){
44715          var r = rs[target];
44716          if(typeof r != "function"){
44717             if(r.hasPanel(panelId)){
44718                return r.showPanel(panelId);
44719             }
44720          }
44721       }
44722       return null;
44723    },
44724
44725    /**
44726      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44727      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44728      */
44729     restoreState : function(provider){
44730         if(!provider){
44731             provider = Roo.state.Manager;
44732         }
44733         var sm = new Roo.LayoutStateManager();
44734         sm.init(this, provider);
44735     },
44736
44737     /**
44738      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44739      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44740      * a valid ContentPanel config object.  Example:
44741      * <pre><code>
44742 // Create the main layout
44743 var layout = new Roo.BorderLayout('main-ct', {
44744     west: {
44745         split:true,
44746         minSize: 175,
44747         titlebar: true
44748     },
44749     center: {
44750         title:'Components'
44751     }
44752 }, 'main-ct');
44753
44754 // Create and add multiple ContentPanels at once via configs
44755 layout.batchAdd({
44756    west: {
44757        id: 'source-files',
44758        autoCreate:true,
44759        title:'Ext Source Files',
44760        autoScroll:true,
44761        fitToFrame:true
44762    },
44763    center : {
44764        el: cview,
44765        autoScroll:true,
44766        fitToFrame:true,
44767        toolbar: tb,
44768        resizeEl:'cbody'
44769    }
44770 });
44771 </code></pre>
44772      * @param {Object} regions An object containing ContentPanel configs by region name
44773      */
44774     batchAdd : function(regions){
44775         this.beginUpdate();
44776         for(var rname in regions){
44777             var lr = this.regions[rname];
44778             if(lr){
44779                 this.addTypedPanels(lr, regions[rname]);
44780             }
44781         }
44782         this.endUpdate();
44783     },
44784
44785     // private
44786     addTypedPanels : function(lr, ps){
44787         if(typeof ps == 'string'){
44788             lr.add(new Roo.ContentPanel(ps));
44789         }
44790         else if(ps instanceof Array){
44791             for(var i =0, len = ps.length; i < len; i++){
44792                 this.addTypedPanels(lr, ps[i]);
44793             }
44794         }
44795         else if(!ps.events){ // raw config?
44796             var el = ps.el;
44797             delete ps.el; // prevent conflict
44798             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44799         }
44800         else {  // panel object assumed!
44801             lr.add(ps);
44802         }
44803     },
44804     /**
44805      * Adds a xtype elements to the layout.
44806      * <pre><code>
44807
44808 layout.addxtype({
44809        xtype : 'ContentPanel',
44810        region: 'west',
44811        items: [ .... ]
44812    }
44813 );
44814
44815 layout.addxtype({
44816         xtype : 'NestedLayoutPanel',
44817         region: 'west',
44818         layout: {
44819            center: { },
44820            west: { }   
44821         },
44822         items : [ ... list of content panels or nested layout panels.. ]
44823    }
44824 );
44825 </code></pre>
44826      * @param {Object} cfg Xtype definition of item to add.
44827      */
44828     addxtype : function(cfg)
44829     {
44830         // basically accepts a pannel...
44831         // can accept a layout region..!?!?
44832         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44833         
44834         if (!cfg.xtype.match(/Panel$/)) {
44835             return false;
44836         }
44837         var ret = false;
44838         
44839         if (typeof(cfg.region) == 'undefined') {
44840             Roo.log("Failed to add Panel, region was not set");
44841             Roo.log(cfg);
44842             return false;
44843         }
44844         var region = cfg.region;
44845         delete cfg.region;
44846         
44847           
44848         var xitems = [];
44849         if (cfg.items) {
44850             xitems = cfg.items;
44851             delete cfg.items;
44852         }
44853         var nb = false;
44854         
44855         switch(cfg.xtype) 
44856         {
44857             case 'ContentPanel':  // ContentPanel (el, cfg)
44858             case 'ScrollPanel':  // ContentPanel (el, cfg)
44859                 if(cfg.autoCreate) {
44860                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44861                 } else {
44862                     var el = this.el.createChild();
44863                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44864                 }
44865                 
44866                 this.add(region, ret);
44867                 break;
44868             
44869             
44870             case 'TreePanel': // our new panel!
44871                 cfg.el = this.el.createChild();
44872                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44873                 this.add(region, ret);
44874                 break;
44875             
44876             case 'NestedLayoutPanel': 
44877                 // create a new Layout (which is  a Border Layout...
44878                 var el = this.el.createChild();
44879                 var clayout = cfg.layout;
44880                 delete cfg.layout;
44881                 clayout.items   = clayout.items  || [];
44882                 // replace this exitems with the clayout ones..
44883                 xitems = clayout.items;
44884                  
44885                 
44886                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44887                     cfg.background = false;
44888                 }
44889                 var layout = new Roo.BorderLayout(el, clayout);
44890                 
44891                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44892                 //console.log('adding nested layout panel '  + cfg.toSource());
44893                 this.add(region, ret);
44894                 nb = {}; /// find first...
44895                 break;
44896                 
44897             case 'GridPanel': 
44898             
44899                 // needs grid and region
44900                 
44901                 //var el = this.getRegion(region).el.createChild();
44902                 var el = this.el.createChild();
44903                 // create the grid first...
44904                 
44905                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44906                 delete cfg.grid;
44907                 if (region == 'center' && this.active ) {
44908                     cfg.background = false;
44909                 }
44910                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44911                 
44912                 this.add(region, ret);
44913                 if (cfg.background) {
44914                     ret.on('activate', function(gp) {
44915                         if (!gp.grid.rendered) {
44916                             gp.grid.render();
44917                         }
44918                     });
44919                 } else {
44920                     grid.render();
44921                 }
44922                 break;
44923            
44924                
44925                 
44926                 
44927             default: 
44928                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44929                 return null;
44930              // GridPanel (grid, cfg)
44931             
44932         }
44933         this.beginUpdate();
44934         // add children..
44935         var region = '';
44936         var abn = {};
44937         Roo.each(xitems, function(i)  {
44938             region = nb && i.region ? i.region : false;
44939             
44940             var add = ret.addxtype(i);
44941            
44942             if (region) {
44943                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
44944                 if (!i.background) {
44945                     abn[region] = nb[region] ;
44946                 }
44947             }
44948             
44949         });
44950         this.endUpdate();
44951
44952         // make the last non-background panel active..
44953         //if (nb) { Roo.log(abn); }
44954         if (nb) {
44955             
44956             for(var r in abn) {
44957                 region = this.getRegion(r);
44958                 if (region) {
44959                     // tried using nb[r], but it does not work..
44960                      
44961                     region.showPanel(abn[r]);
44962                    
44963                 }
44964             }
44965         }
44966         return ret;
44967         
44968     }
44969 });
44970
44971 /**
44972  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44973  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44974  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44975  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44976  * <pre><code>
44977 // shorthand
44978 var CP = Roo.ContentPanel;
44979
44980 var layout = Roo.BorderLayout.create({
44981     north: {
44982         initialSize: 25,
44983         titlebar: false,
44984         panels: [new CP("north", "North")]
44985     },
44986     west: {
44987         split:true,
44988         initialSize: 200,
44989         minSize: 175,
44990         maxSize: 400,
44991         titlebar: true,
44992         collapsible: true,
44993         panels: [new CP("west", {title: "West"})]
44994     },
44995     east: {
44996         split:true,
44997         initialSize: 202,
44998         minSize: 175,
44999         maxSize: 400,
45000         titlebar: true,
45001         collapsible: true,
45002         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
45003     },
45004     south: {
45005         split:true,
45006         initialSize: 100,
45007         minSize: 100,
45008         maxSize: 200,
45009         titlebar: true,
45010         collapsible: true,
45011         panels: [new CP("south", {title: "South", closable: true})]
45012     },
45013     center: {
45014         titlebar: true,
45015         autoScroll:true,
45016         resizeTabs: true,
45017         minTabWidth: 50,
45018         preferredTabWidth: 150,
45019         panels: [
45020             new CP("center1", {title: "Close Me", closable: true}),
45021             new CP("center2", {title: "Center Panel", closable: false})
45022         ]
45023     }
45024 }, document.body);
45025
45026 layout.getRegion("center").showPanel("center1");
45027 </code></pre>
45028  * @param config
45029  * @param targetEl
45030  */
45031 Roo.BorderLayout.create = function(config, targetEl){
45032     var layout = new Roo.BorderLayout(targetEl || document.body, config);
45033     layout.beginUpdate();
45034     var regions = Roo.BorderLayout.RegionFactory.validRegions;
45035     for(var j = 0, jlen = regions.length; j < jlen; j++){
45036         var lr = regions[j];
45037         if(layout.regions[lr] && config[lr].panels){
45038             var r = layout.regions[lr];
45039             var ps = config[lr].panels;
45040             layout.addTypedPanels(r, ps);
45041         }
45042     }
45043     layout.endUpdate();
45044     return layout;
45045 };
45046
45047 // private
45048 Roo.BorderLayout.RegionFactory = {
45049     // private
45050     validRegions : ["north","south","east","west","center"],
45051
45052     // private
45053     create : function(target, mgr, config){
45054         target = target.toLowerCase();
45055         if(config.lightweight || config.basic){
45056             return new Roo.BasicLayoutRegion(mgr, config, target);
45057         }
45058         switch(target){
45059             case "north":
45060                 return new Roo.NorthLayoutRegion(mgr, config);
45061             case "south":
45062                 return new Roo.SouthLayoutRegion(mgr, config);
45063             case "east":
45064                 return new Roo.EastLayoutRegion(mgr, config);
45065             case "west":
45066                 return new Roo.WestLayoutRegion(mgr, config);
45067             case "center":
45068                 return new Roo.CenterLayoutRegion(mgr, config);
45069         }
45070         throw 'Layout region "'+target+'" not supported.';
45071     }
45072 };/*
45073  * Based on:
45074  * Ext JS Library 1.1.1
45075  * Copyright(c) 2006-2007, Ext JS, LLC.
45076  *
45077  * Originally Released Under LGPL - original licence link has changed is not relivant.
45078  *
45079  * Fork - LGPL
45080  * <script type="text/javascript">
45081  */
45082  
45083 /**
45084  * @class Roo.BasicLayoutRegion
45085  * @extends Roo.util.Observable
45086  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
45087  * and does not have a titlebar, tabs or any other features. All it does is size and position 
45088  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
45089  */
45090 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
45091     this.mgr = mgr;
45092     this.position  = pos;
45093     this.events = {
45094         /**
45095          * @scope Roo.BasicLayoutRegion
45096          */
45097         
45098         /**
45099          * @event beforeremove
45100          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
45101          * @param {Roo.LayoutRegion} this
45102          * @param {Roo.ContentPanel} panel The panel
45103          * @param {Object} e The cancel event object
45104          */
45105         "beforeremove" : true,
45106         /**
45107          * @event invalidated
45108          * Fires when the layout for this region is changed.
45109          * @param {Roo.LayoutRegion} this
45110          */
45111         "invalidated" : true,
45112         /**
45113          * @event visibilitychange
45114          * Fires when this region is shown or hidden 
45115          * @param {Roo.LayoutRegion} this
45116          * @param {Boolean} visibility true or false
45117          */
45118         "visibilitychange" : true,
45119         /**
45120          * @event paneladded
45121          * Fires when a panel is added. 
45122          * @param {Roo.LayoutRegion} this
45123          * @param {Roo.ContentPanel} panel The panel
45124          */
45125         "paneladded" : true,
45126         /**
45127          * @event panelremoved
45128          * Fires when a panel is removed. 
45129          * @param {Roo.LayoutRegion} this
45130          * @param {Roo.ContentPanel} panel The panel
45131          */
45132         "panelremoved" : true,
45133         /**
45134          * @event collapsed
45135          * Fires when this region is collapsed.
45136          * @param {Roo.LayoutRegion} this
45137          */
45138         "collapsed" : true,
45139         /**
45140          * @event expanded
45141          * Fires when this region is expanded.
45142          * @param {Roo.LayoutRegion} this
45143          */
45144         "expanded" : true,
45145         /**
45146          * @event slideshow
45147          * Fires when this region is slid into view.
45148          * @param {Roo.LayoutRegion} this
45149          */
45150         "slideshow" : true,
45151         /**
45152          * @event slidehide
45153          * Fires when this region slides out of view. 
45154          * @param {Roo.LayoutRegion} this
45155          */
45156         "slidehide" : true,
45157         /**
45158          * @event panelactivated
45159          * Fires when a panel is activated. 
45160          * @param {Roo.LayoutRegion} this
45161          * @param {Roo.ContentPanel} panel The activated panel
45162          */
45163         "panelactivated" : true,
45164         /**
45165          * @event resized
45166          * Fires when the user resizes this region. 
45167          * @param {Roo.LayoutRegion} this
45168          * @param {Number} newSize The new size (width for east/west, height for north/south)
45169          */
45170         "resized" : true
45171     };
45172     /** A collection of panels in this region. @type Roo.util.MixedCollection */
45173     this.panels = new Roo.util.MixedCollection();
45174     this.panels.getKey = this.getPanelId.createDelegate(this);
45175     this.box = null;
45176     this.activePanel = null;
45177     // ensure listeners are added...
45178     
45179     if (config.listeners || config.events) {
45180         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45181             listeners : config.listeners || {},
45182             events : config.events || {}
45183         });
45184     }
45185     
45186     if(skipConfig !== true){
45187         this.applyConfig(config);
45188     }
45189 };
45190
45191 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45192     getPanelId : function(p){
45193         return p.getId();
45194     },
45195     
45196     applyConfig : function(config){
45197         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45198         this.config = config;
45199         
45200     },
45201     
45202     /**
45203      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45204      * the width, for horizontal (north, south) the height.
45205      * @param {Number} newSize The new width or height
45206      */
45207     resizeTo : function(newSize){
45208         var el = this.el ? this.el :
45209                  (this.activePanel ? this.activePanel.getEl() : null);
45210         if(el){
45211             switch(this.position){
45212                 case "east":
45213                 case "west":
45214                     el.setWidth(newSize);
45215                     this.fireEvent("resized", this, newSize);
45216                 break;
45217                 case "north":
45218                 case "south":
45219                     el.setHeight(newSize);
45220                     this.fireEvent("resized", this, newSize);
45221                 break;                
45222             }
45223         }
45224     },
45225     
45226     getBox : function(){
45227         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45228     },
45229     
45230     getMargins : function(){
45231         return this.margins;
45232     },
45233     
45234     updateBox : function(box){
45235         this.box = box;
45236         var el = this.activePanel.getEl();
45237         el.dom.style.left = box.x + "px";
45238         el.dom.style.top = box.y + "px";
45239         this.activePanel.setSize(box.width, box.height);
45240     },
45241     
45242     /**
45243      * Returns the container element for this region.
45244      * @return {Roo.Element}
45245      */
45246     getEl : function(){
45247         return this.activePanel;
45248     },
45249     
45250     /**
45251      * Returns true if this region is currently visible.
45252      * @return {Boolean}
45253      */
45254     isVisible : function(){
45255         return this.activePanel ? true : false;
45256     },
45257     
45258     setActivePanel : function(panel){
45259         panel = this.getPanel(panel);
45260         if(this.activePanel && this.activePanel != panel){
45261             this.activePanel.setActiveState(false);
45262             this.activePanel.getEl().setLeftTop(-10000,-10000);
45263         }
45264         this.activePanel = panel;
45265         panel.setActiveState(true);
45266         if(this.box){
45267             panel.setSize(this.box.width, this.box.height);
45268         }
45269         this.fireEvent("panelactivated", this, panel);
45270         this.fireEvent("invalidated");
45271     },
45272     
45273     /**
45274      * Show the specified panel.
45275      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45276      * @return {Roo.ContentPanel} The shown panel or null
45277      */
45278     showPanel : function(panel){
45279         if(panel = this.getPanel(panel)){
45280             this.setActivePanel(panel);
45281         }
45282         return panel;
45283     },
45284     
45285     /**
45286      * Get the active panel for this region.
45287      * @return {Roo.ContentPanel} The active panel or null
45288      */
45289     getActivePanel : function(){
45290         return this.activePanel;
45291     },
45292     
45293     /**
45294      * Add the passed ContentPanel(s)
45295      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45296      * @return {Roo.ContentPanel} The panel added (if only one was added)
45297      */
45298     add : function(panel){
45299         if(arguments.length > 1){
45300             for(var i = 0, len = arguments.length; i < len; i++) {
45301                 this.add(arguments[i]);
45302             }
45303             return null;
45304         }
45305         if(this.hasPanel(panel)){
45306             this.showPanel(panel);
45307             return panel;
45308         }
45309         var el = panel.getEl();
45310         if(el.dom.parentNode != this.mgr.el.dom){
45311             this.mgr.el.dom.appendChild(el.dom);
45312         }
45313         if(panel.setRegion){
45314             panel.setRegion(this);
45315         }
45316         this.panels.add(panel);
45317         el.setStyle("position", "absolute");
45318         if(!panel.background){
45319             this.setActivePanel(panel);
45320             if(this.config.initialSize && this.panels.getCount()==1){
45321                 this.resizeTo(this.config.initialSize);
45322             }
45323         }
45324         this.fireEvent("paneladded", this, panel);
45325         return panel;
45326     },
45327     
45328     /**
45329      * Returns true if the panel is in this region.
45330      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45331      * @return {Boolean}
45332      */
45333     hasPanel : function(panel){
45334         if(typeof panel == "object"){ // must be panel obj
45335             panel = panel.getId();
45336         }
45337         return this.getPanel(panel) ? true : false;
45338     },
45339     
45340     /**
45341      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45342      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45343      * @param {Boolean} preservePanel Overrides the config preservePanel option
45344      * @return {Roo.ContentPanel} The panel that was removed
45345      */
45346     remove : function(panel, preservePanel){
45347         panel = this.getPanel(panel);
45348         if(!panel){
45349             return null;
45350         }
45351         var e = {};
45352         this.fireEvent("beforeremove", this, panel, e);
45353         if(e.cancel === true){
45354             return null;
45355         }
45356         var panelId = panel.getId();
45357         this.panels.removeKey(panelId);
45358         return panel;
45359     },
45360     
45361     /**
45362      * Returns the panel specified or null if it's not in this region.
45363      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45364      * @return {Roo.ContentPanel}
45365      */
45366     getPanel : function(id){
45367         if(typeof id == "object"){ // must be panel obj
45368             return id;
45369         }
45370         return this.panels.get(id);
45371     },
45372     
45373     /**
45374      * Returns this regions position (north/south/east/west/center).
45375      * @return {String} 
45376      */
45377     getPosition: function(){
45378         return this.position;    
45379     }
45380 });/*
45381  * Based on:
45382  * Ext JS Library 1.1.1
45383  * Copyright(c) 2006-2007, Ext JS, LLC.
45384  *
45385  * Originally Released Under LGPL - original licence link has changed is not relivant.
45386  *
45387  * Fork - LGPL
45388  * <script type="text/javascript">
45389  */
45390  
45391 /**
45392  * @class Roo.LayoutRegion
45393  * @extends Roo.BasicLayoutRegion
45394  * This class represents a region in a layout manager.
45395  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45396  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45397  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45398  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45399  * @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})
45400  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45401  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45402  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45403  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45404  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45405  * @cfg {String}    title           The title for the region (overrides panel titles)
45406  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45407  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45408  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45409  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45410  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45411  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45412  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45413  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45414  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45415  * @cfg {Boolean}   showPin         True to show a pin button
45416  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45417  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45418  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45419  * @cfg {Number}    width           For East/West panels
45420  * @cfg {Number}    height          For North/South panels
45421  * @cfg {Boolean}   split           To show the splitter
45422  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45423  */
45424 Roo.LayoutRegion = function(mgr, config, pos){
45425     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45426     var dh = Roo.DomHelper;
45427     /** This region's container element 
45428     * @type Roo.Element */
45429     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45430     /** This region's title element 
45431     * @type Roo.Element */
45432
45433     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45434         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45435         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45436     ]}, true);
45437     this.titleEl.enableDisplayMode();
45438     /** This region's title text element 
45439     * @type HTMLElement */
45440     this.titleTextEl = this.titleEl.dom.firstChild;
45441     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45442     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45443     this.closeBtn.enableDisplayMode();
45444     this.closeBtn.on("click", this.closeClicked, this);
45445     this.closeBtn.hide();
45446
45447     this.createBody(config);
45448     this.visible = true;
45449     this.collapsed = false;
45450
45451     if(config.hideWhenEmpty){
45452         this.hide();
45453         this.on("paneladded", this.validateVisibility, this);
45454         this.on("panelremoved", this.validateVisibility, this);
45455     }
45456     this.applyConfig(config);
45457 };
45458
45459 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45460
45461     createBody : function(){
45462         /** This region's body element 
45463         * @type Roo.Element */
45464         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45465     },
45466
45467     applyConfig : function(c){
45468         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45469             var dh = Roo.DomHelper;
45470             if(c.titlebar !== false){
45471                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45472                 this.collapseBtn.on("click", this.collapse, this);
45473                 this.collapseBtn.enableDisplayMode();
45474
45475                 if(c.showPin === true || this.showPin){
45476                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45477                     this.stickBtn.enableDisplayMode();
45478                     this.stickBtn.on("click", this.expand, this);
45479                     this.stickBtn.hide();
45480                 }
45481             }
45482             /** This region's collapsed element
45483             * @type Roo.Element */
45484             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45485                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45486             ]}, true);
45487             if(c.floatable !== false){
45488                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45489                this.collapsedEl.on("click", this.collapseClick, this);
45490             }
45491
45492             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45493                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45494                    id: "message", unselectable: "on", style:{"float":"left"}});
45495                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45496              }
45497             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45498             this.expandBtn.on("click", this.expand, this);
45499         }
45500         if(this.collapseBtn){
45501             this.collapseBtn.setVisible(c.collapsible == true);
45502         }
45503         this.cmargins = c.cmargins || this.cmargins ||
45504                          (this.position == "west" || this.position == "east" ?
45505                              {top: 0, left: 2, right:2, bottom: 0} :
45506                              {top: 2, left: 0, right:0, bottom: 2});
45507         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45508         this.bottomTabs = c.tabPosition != "top";
45509         this.autoScroll = c.autoScroll || false;
45510         if(this.autoScroll){
45511             this.bodyEl.setStyle("overflow", "auto");
45512         }else{
45513             this.bodyEl.setStyle("overflow", "hidden");
45514         }
45515         //if(c.titlebar !== false){
45516             if((!c.titlebar && !c.title) || c.titlebar === false){
45517                 this.titleEl.hide();
45518             }else{
45519                 this.titleEl.show();
45520                 if(c.title){
45521                     this.titleTextEl.innerHTML = c.title;
45522                 }
45523             }
45524         //}
45525         this.duration = c.duration || .30;
45526         this.slideDuration = c.slideDuration || .45;
45527         this.config = c;
45528         if(c.collapsed){
45529             this.collapse(true);
45530         }
45531         if(c.hidden){
45532             this.hide();
45533         }
45534     },
45535     /**
45536      * Returns true if this region is currently visible.
45537      * @return {Boolean}
45538      */
45539     isVisible : function(){
45540         return this.visible;
45541     },
45542
45543     /**
45544      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45545      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45546      */
45547     setCollapsedTitle : function(title){
45548         title = title || "&#160;";
45549         if(this.collapsedTitleTextEl){
45550             this.collapsedTitleTextEl.innerHTML = title;
45551         }
45552     },
45553
45554     getBox : function(){
45555         var b;
45556         if(!this.collapsed){
45557             b = this.el.getBox(false, true);
45558         }else{
45559             b = this.collapsedEl.getBox(false, true);
45560         }
45561         return b;
45562     },
45563
45564     getMargins : function(){
45565         return this.collapsed ? this.cmargins : this.margins;
45566     },
45567
45568     highlight : function(){
45569         this.el.addClass("x-layout-panel-dragover");
45570     },
45571
45572     unhighlight : function(){
45573         this.el.removeClass("x-layout-panel-dragover");
45574     },
45575
45576     updateBox : function(box){
45577         this.box = box;
45578         if(!this.collapsed){
45579             this.el.dom.style.left = box.x + "px";
45580             this.el.dom.style.top = box.y + "px";
45581             this.updateBody(box.width, box.height);
45582         }else{
45583             this.collapsedEl.dom.style.left = box.x + "px";
45584             this.collapsedEl.dom.style.top = box.y + "px";
45585             this.collapsedEl.setSize(box.width, box.height);
45586         }
45587         if(this.tabs){
45588             this.tabs.autoSizeTabs();
45589         }
45590     },
45591
45592     updateBody : function(w, h){
45593         if(w !== null){
45594             this.el.setWidth(w);
45595             w -= this.el.getBorderWidth("rl");
45596             if(this.config.adjustments){
45597                 w += this.config.adjustments[0];
45598             }
45599         }
45600         if(h !== null){
45601             this.el.setHeight(h);
45602             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45603             h -= this.el.getBorderWidth("tb");
45604             if(this.config.adjustments){
45605                 h += this.config.adjustments[1];
45606             }
45607             this.bodyEl.setHeight(h);
45608             if(this.tabs){
45609                 h = this.tabs.syncHeight(h);
45610             }
45611         }
45612         if(this.panelSize){
45613             w = w !== null ? w : this.panelSize.width;
45614             h = h !== null ? h : this.panelSize.height;
45615         }
45616         if(this.activePanel){
45617             var el = this.activePanel.getEl();
45618             w = w !== null ? w : el.getWidth();
45619             h = h !== null ? h : el.getHeight();
45620             this.panelSize = {width: w, height: h};
45621             this.activePanel.setSize(w, h);
45622         }
45623         if(Roo.isIE && this.tabs){
45624             this.tabs.el.repaint();
45625         }
45626     },
45627
45628     /**
45629      * Returns the container element for this region.
45630      * @return {Roo.Element}
45631      */
45632     getEl : function(){
45633         return this.el;
45634     },
45635
45636     /**
45637      * Hides this region.
45638      */
45639     hide : function(){
45640         if(!this.collapsed){
45641             this.el.dom.style.left = "-2000px";
45642             this.el.hide();
45643         }else{
45644             this.collapsedEl.dom.style.left = "-2000px";
45645             this.collapsedEl.hide();
45646         }
45647         this.visible = false;
45648         this.fireEvent("visibilitychange", this, false);
45649     },
45650
45651     /**
45652      * Shows this region if it was previously hidden.
45653      */
45654     show : function(){
45655         if(!this.collapsed){
45656             this.el.show();
45657         }else{
45658             this.collapsedEl.show();
45659         }
45660         this.visible = true;
45661         this.fireEvent("visibilitychange", this, true);
45662     },
45663
45664     closeClicked : function(){
45665         if(this.activePanel){
45666             this.remove(this.activePanel);
45667         }
45668     },
45669
45670     collapseClick : function(e){
45671         if(this.isSlid){
45672            e.stopPropagation();
45673            this.slideIn();
45674         }else{
45675            e.stopPropagation();
45676            this.slideOut();
45677         }
45678     },
45679
45680     /**
45681      * Collapses this region.
45682      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45683      */
45684     collapse : function(skipAnim){
45685         if(this.collapsed) return;
45686         this.collapsed = true;
45687         if(this.split){
45688             this.split.el.hide();
45689         }
45690         if(this.config.animate && skipAnim !== true){
45691             this.fireEvent("invalidated", this);
45692             this.animateCollapse();
45693         }else{
45694             this.el.setLocation(-20000,-20000);
45695             this.el.hide();
45696             this.collapsedEl.show();
45697             this.fireEvent("collapsed", this);
45698             this.fireEvent("invalidated", this);
45699         }
45700     },
45701
45702     animateCollapse : function(){
45703         // overridden
45704     },
45705
45706     /**
45707      * Expands this region if it was previously collapsed.
45708      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45709      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45710      */
45711     expand : function(e, skipAnim){
45712         if(e) e.stopPropagation();
45713         if(!this.collapsed || this.el.hasActiveFx()) return;
45714         if(this.isSlid){
45715             this.afterSlideIn();
45716             skipAnim = true;
45717         }
45718         this.collapsed = false;
45719         if(this.config.animate && skipAnim !== true){
45720             this.animateExpand();
45721         }else{
45722             this.el.show();
45723             if(this.split){
45724                 this.split.el.show();
45725             }
45726             this.collapsedEl.setLocation(-2000,-2000);
45727             this.collapsedEl.hide();
45728             this.fireEvent("invalidated", this);
45729             this.fireEvent("expanded", this);
45730         }
45731     },
45732
45733     animateExpand : function(){
45734         // overridden
45735     },
45736
45737     initTabs : function()
45738     {
45739         this.bodyEl.setStyle("overflow", "hidden");
45740         var ts = new Roo.TabPanel(
45741                 this.bodyEl.dom,
45742                 {
45743                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45744                     disableTooltips: this.config.disableTabTips,
45745                     toolbar : this.config.toolbar
45746                 }
45747         );
45748         if(this.config.hideTabs){
45749             ts.stripWrap.setDisplayed(false);
45750         }
45751         this.tabs = ts;
45752         ts.resizeTabs = this.config.resizeTabs === true;
45753         ts.minTabWidth = this.config.minTabWidth || 40;
45754         ts.maxTabWidth = this.config.maxTabWidth || 250;
45755         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45756         ts.monitorResize = false;
45757         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45758         ts.bodyEl.addClass('x-layout-tabs-body');
45759         this.panels.each(this.initPanelAsTab, this);
45760     },
45761
45762     initPanelAsTab : function(panel){
45763         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45764                     this.config.closeOnTab && panel.isClosable());
45765         if(panel.tabTip !== undefined){
45766             ti.setTooltip(panel.tabTip);
45767         }
45768         ti.on("activate", function(){
45769               this.setActivePanel(panel);
45770         }, this);
45771         if(this.config.closeOnTab){
45772             ti.on("beforeclose", function(t, e){
45773                 e.cancel = true;
45774                 this.remove(panel);
45775             }, this);
45776         }
45777         return ti;
45778     },
45779
45780     updatePanelTitle : function(panel, title){
45781         if(this.activePanel == panel){
45782             this.updateTitle(title);
45783         }
45784         if(this.tabs){
45785             var ti = this.tabs.getTab(panel.getEl().id);
45786             ti.setText(title);
45787             if(panel.tabTip !== undefined){
45788                 ti.setTooltip(panel.tabTip);
45789             }
45790         }
45791     },
45792
45793     updateTitle : function(title){
45794         if(this.titleTextEl && !this.config.title){
45795             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45796         }
45797     },
45798
45799     setActivePanel : function(panel){
45800         panel = this.getPanel(panel);
45801         if(this.activePanel && this.activePanel != panel){
45802             this.activePanel.setActiveState(false);
45803         }
45804         this.activePanel = panel;
45805         panel.setActiveState(true);
45806         if(this.panelSize){
45807             panel.setSize(this.panelSize.width, this.panelSize.height);
45808         }
45809         if(this.closeBtn){
45810             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45811         }
45812         this.updateTitle(panel.getTitle());
45813         if(this.tabs){
45814             this.fireEvent("invalidated", this);
45815         }
45816         this.fireEvent("panelactivated", this, panel);
45817     },
45818
45819     /**
45820      * Shows the specified panel.
45821      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45822      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45823      */
45824     showPanel : function(panel){
45825         if(panel = this.getPanel(panel)){
45826             if(this.tabs){
45827                 var tab = this.tabs.getTab(panel.getEl().id);
45828                 if(tab.isHidden()){
45829                     this.tabs.unhideTab(tab.id);
45830                 }
45831                 tab.activate();
45832             }else{
45833                 this.setActivePanel(panel);
45834             }
45835         }
45836         return panel;
45837     },
45838
45839     /**
45840      * Get the active panel for this region.
45841      * @return {Roo.ContentPanel} The active panel or null
45842      */
45843     getActivePanel : function(){
45844         return this.activePanel;
45845     },
45846
45847     validateVisibility : function(){
45848         if(this.panels.getCount() < 1){
45849             this.updateTitle("&#160;");
45850             this.closeBtn.hide();
45851             this.hide();
45852         }else{
45853             if(!this.isVisible()){
45854                 this.show();
45855             }
45856         }
45857     },
45858
45859     /**
45860      * Adds the passed ContentPanel(s) to this region.
45861      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45862      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45863      */
45864     add : function(panel){
45865         if(arguments.length > 1){
45866             for(var i = 0, len = arguments.length; i < len; i++) {
45867                 this.add(arguments[i]);
45868             }
45869             return null;
45870         }
45871         if(this.hasPanel(panel)){
45872             this.showPanel(panel);
45873             return panel;
45874         }
45875         panel.setRegion(this);
45876         this.panels.add(panel);
45877         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45878             this.bodyEl.dom.appendChild(panel.getEl().dom);
45879             if(panel.background !== true){
45880                 this.setActivePanel(panel);
45881             }
45882             this.fireEvent("paneladded", this, panel);
45883             return panel;
45884         }
45885         if(!this.tabs){
45886             this.initTabs();
45887         }else{
45888             this.initPanelAsTab(panel);
45889         }
45890         if(panel.background !== true){
45891             this.tabs.activate(panel.getEl().id);
45892         }
45893         this.fireEvent("paneladded", this, panel);
45894         return panel;
45895     },
45896
45897     /**
45898      * Hides the tab for the specified panel.
45899      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45900      */
45901     hidePanel : function(panel){
45902         if(this.tabs && (panel = this.getPanel(panel))){
45903             this.tabs.hideTab(panel.getEl().id);
45904         }
45905     },
45906
45907     /**
45908      * Unhides the tab for a previously hidden panel.
45909      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45910      */
45911     unhidePanel : function(panel){
45912         if(this.tabs && (panel = this.getPanel(panel))){
45913             this.tabs.unhideTab(panel.getEl().id);
45914         }
45915     },
45916
45917     clearPanels : function(){
45918         while(this.panels.getCount() > 0){
45919              this.remove(this.panels.first());
45920         }
45921     },
45922
45923     /**
45924      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45925      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45926      * @param {Boolean} preservePanel Overrides the config preservePanel option
45927      * @return {Roo.ContentPanel} The panel that was removed
45928      */
45929     remove : function(panel, preservePanel){
45930         panel = this.getPanel(panel);
45931         if(!panel){
45932             return null;
45933         }
45934         var e = {};
45935         this.fireEvent("beforeremove", this, panel, e);
45936         if(e.cancel === true){
45937             return null;
45938         }
45939         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45940         var panelId = panel.getId();
45941         this.panels.removeKey(panelId);
45942         if(preservePanel){
45943             document.body.appendChild(panel.getEl().dom);
45944         }
45945         if(this.tabs){
45946             this.tabs.removeTab(panel.getEl().id);
45947         }else if (!preservePanel){
45948             this.bodyEl.dom.removeChild(panel.getEl().dom);
45949         }
45950         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45951             var p = this.panels.first();
45952             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45953             tempEl.appendChild(p.getEl().dom);
45954             this.bodyEl.update("");
45955             this.bodyEl.dom.appendChild(p.getEl().dom);
45956             tempEl = null;
45957             this.updateTitle(p.getTitle());
45958             this.tabs = null;
45959             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45960             this.setActivePanel(p);
45961         }
45962         panel.setRegion(null);
45963         if(this.activePanel == panel){
45964             this.activePanel = null;
45965         }
45966         if(this.config.autoDestroy !== false && preservePanel !== true){
45967             try{panel.destroy();}catch(e){}
45968         }
45969         this.fireEvent("panelremoved", this, panel);
45970         return panel;
45971     },
45972
45973     /**
45974      * Returns the TabPanel component used by this region
45975      * @return {Roo.TabPanel}
45976      */
45977     getTabs : function(){
45978         return this.tabs;
45979     },
45980
45981     createTool : function(parentEl, className){
45982         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45983             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45984         btn.addClassOnOver("x-layout-tools-button-over");
45985         return btn;
45986     }
45987 });/*
45988  * Based on:
45989  * Ext JS Library 1.1.1
45990  * Copyright(c) 2006-2007, Ext JS, LLC.
45991  *
45992  * Originally Released Under LGPL - original licence link has changed is not relivant.
45993  *
45994  * Fork - LGPL
45995  * <script type="text/javascript">
45996  */
45997  
45998
45999
46000 /**
46001  * @class Roo.SplitLayoutRegion
46002  * @extends Roo.LayoutRegion
46003  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
46004  */
46005 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
46006     this.cursor = cursor;
46007     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
46008 };
46009
46010 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
46011     splitTip : "Drag to resize.",
46012     collapsibleSplitTip : "Drag to resize. Double click to hide.",
46013     useSplitTips : false,
46014
46015     applyConfig : function(config){
46016         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
46017         if(config.split){
46018             if(!this.split){
46019                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
46020                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
46021                 /** The SplitBar for this region 
46022                 * @type Roo.SplitBar */
46023                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
46024                 this.split.on("moved", this.onSplitMove, this);
46025                 this.split.useShim = config.useShim === true;
46026                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
46027                 if(this.useSplitTips){
46028                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
46029                 }
46030                 if(config.collapsible){
46031                     this.split.el.on("dblclick", this.collapse,  this);
46032                 }
46033             }
46034             if(typeof config.minSize != "undefined"){
46035                 this.split.minSize = config.minSize;
46036             }
46037             if(typeof config.maxSize != "undefined"){
46038                 this.split.maxSize = config.maxSize;
46039             }
46040             if(config.hideWhenEmpty || config.hidden || config.collapsed){
46041                 this.hideSplitter();
46042             }
46043         }
46044     },
46045
46046     getHMaxSize : function(){
46047          var cmax = this.config.maxSize || 10000;
46048          var center = this.mgr.getRegion("center");
46049          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
46050     },
46051
46052     getVMaxSize : function(){
46053          var cmax = this.config.maxSize || 10000;
46054          var center = this.mgr.getRegion("center");
46055          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
46056     },
46057
46058     onSplitMove : function(split, newSize){
46059         this.fireEvent("resized", this, newSize);
46060     },
46061     
46062     /** 
46063      * Returns the {@link Roo.SplitBar} for this region.
46064      * @return {Roo.SplitBar}
46065      */
46066     getSplitBar : function(){
46067         return this.split;
46068     },
46069     
46070     hide : function(){
46071         this.hideSplitter();
46072         Roo.SplitLayoutRegion.superclass.hide.call(this);
46073     },
46074
46075     hideSplitter : function(){
46076         if(this.split){
46077             this.split.el.setLocation(-2000,-2000);
46078             this.split.el.hide();
46079         }
46080     },
46081
46082     show : function(){
46083         if(this.split){
46084             this.split.el.show();
46085         }
46086         Roo.SplitLayoutRegion.superclass.show.call(this);
46087     },
46088     
46089     beforeSlide: function(){
46090         if(Roo.isGecko){// firefox overflow auto bug workaround
46091             this.bodyEl.clip();
46092             if(this.tabs) this.tabs.bodyEl.clip();
46093             if(this.activePanel){
46094                 this.activePanel.getEl().clip();
46095                 
46096                 if(this.activePanel.beforeSlide){
46097                     this.activePanel.beforeSlide();
46098                 }
46099             }
46100         }
46101     },
46102     
46103     afterSlide : function(){
46104         if(Roo.isGecko){// firefox overflow auto bug workaround
46105             this.bodyEl.unclip();
46106             if(this.tabs) this.tabs.bodyEl.unclip();
46107             if(this.activePanel){
46108                 this.activePanel.getEl().unclip();
46109                 if(this.activePanel.afterSlide){
46110                     this.activePanel.afterSlide();
46111                 }
46112             }
46113         }
46114     },
46115
46116     initAutoHide : function(){
46117         if(this.autoHide !== false){
46118             if(!this.autoHideHd){
46119                 var st = new Roo.util.DelayedTask(this.slideIn, this);
46120                 this.autoHideHd = {
46121                     "mouseout": function(e){
46122                         if(!e.within(this.el, true)){
46123                             st.delay(500);
46124                         }
46125                     },
46126                     "mouseover" : function(e){
46127                         st.cancel();
46128                     },
46129                     scope : this
46130                 };
46131             }
46132             this.el.on(this.autoHideHd);
46133         }
46134     },
46135
46136     clearAutoHide : function(){
46137         if(this.autoHide !== false){
46138             this.el.un("mouseout", this.autoHideHd.mouseout);
46139             this.el.un("mouseover", this.autoHideHd.mouseover);
46140         }
46141     },
46142
46143     clearMonitor : function(){
46144         Roo.get(document).un("click", this.slideInIf, this);
46145     },
46146
46147     // these names are backwards but not changed for compat
46148     slideOut : function(){
46149         if(this.isSlid || this.el.hasActiveFx()){
46150             return;
46151         }
46152         this.isSlid = true;
46153         if(this.collapseBtn){
46154             this.collapseBtn.hide();
46155         }
46156         this.closeBtnState = this.closeBtn.getStyle('display');
46157         this.closeBtn.hide();
46158         if(this.stickBtn){
46159             this.stickBtn.show();
46160         }
46161         this.el.show();
46162         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
46163         this.beforeSlide();
46164         this.el.setStyle("z-index", 10001);
46165         this.el.slideIn(this.getSlideAnchor(), {
46166             callback: function(){
46167                 this.afterSlide();
46168                 this.initAutoHide();
46169                 Roo.get(document).on("click", this.slideInIf, this);
46170                 this.fireEvent("slideshow", this);
46171             },
46172             scope: this,
46173             block: true
46174         });
46175     },
46176
46177     afterSlideIn : function(){
46178         this.clearAutoHide();
46179         this.isSlid = false;
46180         this.clearMonitor();
46181         this.el.setStyle("z-index", "");
46182         if(this.collapseBtn){
46183             this.collapseBtn.show();
46184         }
46185         this.closeBtn.setStyle('display', this.closeBtnState);
46186         if(this.stickBtn){
46187             this.stickBtn.hide();
46188         }
46189         this.fireEvent("slidehide", this);
46190     },
46191
46192     slideIn : function(cb){
46193         if(!this.isSlid || this.el.hasActiveFx()){
46194             Roo.callback(cb);
46195             return;
46196         }
46197         this.isSlid = false;
46198         this.beforeSlide();
46199         this.el.slideOut(this.getSlideAnchor(), {
46200             callback: function(){
46201                 this.el.setLeftTop(-10000, -10000);
46202                 this.afterSlide();
46203                 this.afterSlideIn();
46204                 Roo.callback(cb);
46205             },
46206             scope: this,
46207             block: true
46208         });
46209     },
46210     
46211     slideInIf : function(e){
46212         if(!e.within(this.el)){
46213             this.slideIn();
46214         }
46215     },
46216
46217     animateCollapse : function(){
46218         this.beforeSlide();
46219         this.el.setStyle("z-index", 20000);
46220         var anchor = this.getSlideAnchor();
46221         this.el.slideOut(anchor, {
46222             callback : function(){
46223                 this.el.setStyle("z-index", "");
46224                 this.collapsedEl.slideIn(anchor, {duration:.3});
46225                 this.afterSlide();
46226                 this.el.setLocation(-10000,-10000);
46227                 this.el.hide();
46228                 this.fireEvent("collapsed", this);
46229             },
46230             scope: this,
46231             block: true
46232         });
46233     },
46234
46235     animateExpand : function(){
46236         this.beforeSlide();
46237         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46238         this.el.setStyle("z-index", 20000);
46239         this.collapsedEl.hide({
46240             duration:.1
46241         });
46242         this.el.slideIn(this.getSlideAnchor(), {
46243             callback : function(){
46244                 this.el.setStyle("z-index", "");
46245                 this.afterSlide();
46246                 if(this.split){
46247                     this.split.el.show();
46248                 }
46249                 this.fireEvent("invalidated", this);
46250                 this.fireEvent("expanded", this);
46251             },
46252             scope: this,
46253             block: true
46254         });
46255     },
46256
46257     anchors : {
46258         "west" : "left",
46259         "east" : "right",
46260         "north" : "top",
46261         "south" : "bottom"
46262     },
46263
46264     sanchors : {
46265         "west" : "l",
46266         "east" : "r",
46267         "north" : "t",
46268         "south" : "b"
46269     },
46270
46271     canchors : {
46272         "west" : "tl-tr",
46273         "east" : "tr-tl",
46274         "north" : "tl-bl",
46275         "south" : "bl-tl"
46276     },
46277
46278     getAnchor : function(){
46279         return this.anchors[this.position];
46280     },
46281
46282     getCollapseAnchor : function(){
46283         return this.canchors[this.position];
46284     },
46285
46286     getSlideAnchor : function(){
46287         return this.sanchors[this.position];
46288     },
46289
46290     getAlignAdj : function(){
46291         var cm = this.cmargins;
46292         switch(this.position){
46293             case "west":
46294                 return [0, 0];
46295             break;
46296             case "east":
46297                 return [0, 0];
46298             break;
46299             case "north":
46300                 return [0, 0];
46301             break;
46302             case "south":
46303                 return [0, 0];
46304             break;
46305         }
46306     },
46307
46308     getExpandAdj : function(){
46309         var c = this.collapsedEl, cm = this.cmargins;
46310         switch(this.position){
46311             case "west":
46312                 return [-(cm.right+c.getWidth()+cm.left), 0];
46313             break;
46314             case "east":
46315                 return [cm.right+c.getWidth()+cm.left, 0];
46316             break;
46317             case "north":
46318                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46319             break;
46320             case "south":
46321                 return [0, cm.top+cm.bottom+c.getHeight()];
46322             break;
46323         }
46324     }
46325 });/*
46326  * Based on:
46327  * Ext JS Library 1.1.1
46328  * Copyright(c) 2006-2007, Ext JS, LLC.
46329  *
46330  * Originally Released Under LGPL - original licence link has changed is not relivant.
46331  *
46332  * Fork - LGPL
46333  * <script type="text/javascript">
46334  */
46335 /*
46336  * These classes are private internal classes
46337  */
46338 Roo.CenterLayoutRegion = function(mgr, config){
46339     Roo.LayoutRegion.call(this, mgr, config, "center");
46340     this.visible = true;
46341     this.minWidth = config.minWidth || 20;
46342     this.minHeight = config.minHeight || 20;
46343 };
46344
46345 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46346     hide : function(){
46347         // center panel can't be hidden
46348     },
46349     
46350     show : function(){
46351         // center panel can't be hidden
46352     },
46353     
46354     getMinWidth: function(){
46355         return this.minWidth;
46356     },
46357     
46358     getMinHeight: function(){
46359         return this.minHeight;
46360     }
46361 });
46362
46363
46364 Roo.NorthLayoutRegion = function(mgr, config){
46365     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46366     if(this.split){
46367         this.split.placement = Roo.SplitBar.TOP;
46368         this.split.orientation = Roo.SplitBar.VERTICAL;
46369         this.split.el.addClass("x-layout-split-v");
46370     }
46371     var size = config.initialSize || config.height;
46372     if(typeof size != "undefined"){
46373         this.el.setHeight(size);
46374     }
46375 };
46376 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46377     orientation: Roo.SplitBar.VERTICAL,
46378     getBox : function(){
46379         if(this.collapsed){
46380             return this.collapsedEl.getBox();
46381         }
46382         var box = this.el.getBox();
46383         if(this.split){
46384             box.height += this.split.el.getHeight();
46385         }
46386         return box;
46387     },
46388     
46389     updateBox : function(box){
46390         if(this.split && !this.collapsed){
46391             box.height -= this.split.el.getHeight();
46392             this.split.el.setLeft(box.x);
46393             this.split.el.setTop(box.y+box.height);
46394             this.split.el.setWidth(box.width);
46395         }
46396         if(this.collapsed){
46397             this.updateBody(box.width, null);
46398         }
46399         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46400     }
46401 });
46402
46403 Roo.SouthLayoutRegion = function(mgr, config){
46404     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46405     if(this.split){
46406         this.split.placement = Roo.SplitBar.BOTTOM;
46407         this.split.orientation = Roo.SplitBar.VERTICAL;
46408         this.split.el.addClass("x-layout-split-v");
46409     }
46410     var size = config.initialSize || config.height;
46411     if(typeof size != "undefined"){
46412         this.el.setHeight(size);
46413     }
46414 };
46415 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46416     orientation: Roo.SplitBar.VERTICAL,
46417     getBox : function(){
46418         if(this.collapsed){
46419             return this.collapsedEl.getBox();
46420         }
46421         var box = this.el.getBox();
46422         if(this.split){
46423             var sh = this.split.el.getHeight();
46424             box.height += sh;
46425             box.y -= sh;
46426         }
46427         return box;
46428     },
46429     
46430     updateBox : function(box){
46431         if(this.split && !this.collapsed){
46432             var sh = this.split.el.getHeight();
46433             box.height -= sh;
46434             box.y += sh;
46435             this.split.el.setLeft(box.x);
46436             this.split.el.setTop(box.y-sh);
46437             this.split.el.setWidth(box.width);
46438         }
46439         if(this.collapsed){
46440             this.updateBody(box.width, null);
46441         }
46442         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46443     }
46444 });
46445
46446 Roo.EastLayoutRegion = function(mgr, config){
46447     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46448     if(this.split){
46449         this.split.placement = Roo.SplitBar.RIGHT;
46450         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46451         this.split.el.addClass("x-layout-split-h");
46452     }
46453     var size = config.initialSize || config.width;
46454     if(typeof size != "undefined"){
46455         this.el.setWidth(size);
46456     }
46457 };
46458 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46459     orientation: Roo.SplitBar.HORIZONTAL,
46460     getBox : function(){
46461         if(this.collapsed){
46462             return this.collapsedEl.getBox();
46463         }
46464         var box = this.el.getBox();
46465         if(this.split){
46466             var sw = this.split.el.getWidth();
46467             box.width += sw;
46468             box.x -= sw;
46469         }
46470         return box;
46471     },
46472
46473     updateBox : function(box){
46474         if(this.split && !this.collapsed){
46475             var sw = this.split.el.getWidth();
46476             box.width -= sw;
46477             this.split.el.setLeft(box.x);
46478             this.split.el.setTop(box.y);
46479             this.split.el.setHeight(box.height);
46480             box.x += sw;
46481         }
46482         if(this.collapsed){
46483             this.updateBody(null, box.height);
46484         }
46485         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46486     }
46487 });
46488
46489 Roo.WestLayoutRegion = function(mgr, config){
46490     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46491     if(this.split){
46492         this.split.placement = Roo.SplitBar.LEFT;
46493         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46494         this.split.el.addClass("x-layout-split-h");
46495     }
46496     var size = config.initialSize || config.width;
46497     if(typeof size != "undefined"){
46498         this.el.setWidth(size);
46499     }
46500 };
46501 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46502     orientation: Roo.SplitBar.HORIZONTAL,
46503     getBox : function(){
46504         if(this.collapsed){
46505             return this.collapsedEl.getBox();
46506         }
46507         var box = this.el.getBox();
46508         if(this.split){
46509             box.width += this.split.el.getWidth();
46510         }
46511         return box;
46512     },
46513     
46514     updateBox : function(box){
46515         if(this.split && !this.collapsed){
46516             var sw = this.split.el.getWidth();
46517             box.width -= sw;
46518             this.split.el.setLeft(box.x+box.width);
46519             this.split.el.setTop(box.y);
46520             this.split.el.setHeight(box.height);
46521         }
46522         if(this.collapsed){
46523             this.updateBody(null, box.height);
46524         }
46525         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46526     }
46527 });
46528 /*
46529  * Based on:
46530  * Ext JS Library 1.1.1
46531  * Copyright(c) 2006-2007, Ext JS, LLC.
46532  *
46533  * Originally Released Under LGPL - original licence link has changed is not relivant.
46534  *
46535  * Fork - LGPL
46536  * <script type="text/javascript">
46537  */
46538  
46539  
46540 /*
46541  * Private internal class for reading and applying state
46542  */
46543 Roo.LayoutStateManager = function(layout){
46544      // default empty state
46545      this.state = {
46546         north: {},
46547         south: {},
46548         east: {},
46549         west: {}       
46550     };
46551 };
46552
46553 Roo.LayoutStateManager.prototype = {
46554     init : function(layout, provider){
46555         this.provider = provider;
46556         var state = provider.get(layout.id+"-layout-state");
46557         if(state){
46558             var wasUpdating = layout.isUpdating();
46559             if(!wasUpdating){
46560                 layout.beginUpdate();
46561             }
46562             for(var key in state){
46563                 if(typeof state[key] != "function"){
46564                     var rstate = state[key];
46565                     var r = layout.getRegion(key);
46566                     if(r && rstate){
46567                         if(rstate.size){
46568                             r.resizeTo(rstate.size);
46569                         }
46570                         if(rstate.collapsed == true){
46571                             r.collapse(true);
46572                         }else{
46573                             r.expand(null, true);
46574                         }
46575                     }
46576                 }
46577             }
46578             if(!wasUpdating){
46579                 layout.endUpdate();
46580             }
46581             this.state = state; 
46582         }
46583         this.layout = layout;
46584         layout.on("regionresized", this.onRegionResized, this);
46585         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46586         layout.on("regionexpanded", this.onRegionExpanded, this);
46587     },
46588     
46589     storeState : function(){
46590         this.provider.set(this.layout.id+"-layout-state", this.state);
46591     },
46592     
46593     onRegionResized : function(region, newSize){
46594         this.state[region.getPosition()].size = newSize;
46595         this.storeState();
46596     },
46597     
46598     onRegionCollapsed : function(region){
46599         this.state[region.getPosition()].collapsed = true;
46600         this.storeState();
46601     },
46602     
46603     onRegionExpanded : function(region){
46604         this.state[region.getPosition()].collapsed = false;
46605         this.storeState();
46606     }
46607 };/*
46608  * Based on:
46609  * Ext JS Library 1.1.1
46610  * Copyright(c) 2006-2007, Ext JS, LLC.
46611  *
46612  * Originally Released Under LGPL - original licence link has changed is not relivant.
46613  *
46614  * Fork - LGPL
46615  * <script type="text/javascript">
46616  */
46617 /**
46618  * @class Roo.ContentPanel
46619  * @extends Roo.util.Observable
46620  * A basic ContentPanel element.
46621  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46622  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46623  * @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
46624  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46625  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46626  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46627  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46628  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46629  * @cfg {String} title          The title for this panel
46630  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46631  * @cfg {String} url            Calls {@link #setUrl} with this value
46632  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46633  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46634  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46635  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46636
46637  * @constructor
46638  * Create a new ContentPanel.
46639  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46640  * @param {String/Object} config A string to set only the title or a config object
46641  * @param {String} content (optional) Set the HTML content for this panel
46642  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46643  */
46644 Roo.ContentPanel = function(el, config, content){
46645     
46646      
46647     /*
46648     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46649         config = el;
46650         el = Roo.id();
46651     }
46652     if (config && config.parentLayout) { 
46653         el = config.parentLayout.el.createChild(); 
46654     }
46655     */
46656     if(el.autoCreate){ // xtype is available if this is called from factory
46657         config = el;
46658         el = Roo.id();
46659     }
46660     this.el = Roo.get(el);
46661     if(!this.el && config && config.autoCreate){
46662         if(typeof config.autoCreate == "object"){
46663             if(!config.autoCreate.id){
46664                 config.autoCreate.id = config.id||el;
46665             }
46666             this.el = Roo.DomHelper.append(document.body,
46667                         config.autoCreate, true);
46668         }else{
46669             this.el = Roo.DomHelper.append(document.body,
46670                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46671         }
46672     }
46673     this.closable = false;
46674     this.loaded = false;
46675     this.active = false;
46676     if(typeof config == "string"){
46677         this.title = config;
46678     }else{
46679         Roo.apply(this, config);
46680     }
46681     
46682     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46683         this.wrapEl = this.el.wrap();
46684         this.toolbar.container = this.el.insertSibling(false, 'before');
46685         this.toolbar = new Roo.Toolbar(this.toolbar);
46686     }
46687     
46688     
46689     
46690     if(this.resizeEl){
46691         this.resizeEl = Roo.get(this.resizeEl, true);
46692     }else{
46693         this.resizeEl = this.el;
46694     }
46695     this.addEvents({
46696         /**
46697          * @event activate
46698          * Fires when this panel is activated. 
46699          * @param {Roo.ContentPanel} this
46700          */
46701         "activate" : true,
46702         /**
46703          * @event deactivate
46704          * Fires when this panel is activated. 
46705          * @param {Roo.ContentPanel} this
46706          */
46707         "deactivate" : true,
46708
46709         /**
46710          * @event resize
46711          * Fires when this panel is resized if fitToFrame is true.
46712          * @param {Roo.ContentPanel} this
46713          * @param {Number} width The width after any component adjustments
46714          * @param {Number} height The height after any component adjustments
46715          */
46716         "resize" : true,
46717         
46718          /**
46719          * @event render
46720          * Fires when this tab is created
46721          * @param {Roo.ContentPanel} this
46722          */
46723         "render" : true
46724         
46725         
46726         
46727     });
46728     if(this.autoScroll){
46729         this.resizeEl.setStyle("overflow", "auto");
46730     } else {
46731         // fix randome scrolling
46732         this.el.on('scroll', function() {
46733             Roo.log('fix random scolling');
46734             this.scrollTo('top',0); 
46735         });
46736     }
46737     content = content || this.content;
46738     if(content){
46739         this.setContent(content);
46740     }
46741     if(config && config.url){
46742         this.setUrl(this.url, this.params, this.loadOnce);
46743     }
46744     
46745     
46746     
46747     Roo.ContentPanel.superclass.constructor.call(this);
46748     
46749     this.fireEvent('render', this);
46750 };
46751
46752 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46753     tabTip:'',
46754     setRegion : function(region){
46755         this.region = region;
46756         if(region){
46757            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46758         }else{
46759            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46760         } 
46761     },
46762     
46763     /**
46764      * Returns the toolbar for this Panel if one was configured. 
46765      * @return {Roo.Toolbar} 
46766      */
46767     getToolbar : function(){
46768         return this.toolbar;
46769     },
46770     
46771     setActiveState : function(active){
46772         this.active = active;
46773         if(!active){
46774             this.fireEvent("deactivate", this);
46775         }else{
46776             this.fireEvent("activate", this);
46777         }
46778     },
46779     /**
46780      * Updates this panel's element
46781      * @param {String} content The new content
46782      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46783     */
46784     setContent : function(content, loadScripts){
46785         this.el.update(content, loadScripts);
46786     },
46787
46788     ignoreResize : function(w, h){
46789         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46790             return true;
46791         }else{
46792             this.lastSize = {width: w, height: h};
46793             return false;
46794         }
46795     },
46796     /**
46797      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46798      * @return {Roo.UpdateManager} The UpdateManager
46799      */
46800     getUpdateManager : function(){
46801         return this.el.getUpdateManager();
46802     },
46803      /**
46804      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46805      * @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:
46806 <pre><code>
46807 panel.load({
46808     url: "your-url.php",
46809     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46810     callback: yourFunction,
46811     scope: yourObject, //(optional scope)
46812     discardUrl: false,
46813     nocache: false,
46814     text: "Loading...",
46815     timeout: 30,
46816     scripts: false
46817 });
46818 </code></pre>
46819      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46820      * 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.
46821      * @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}
46822      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46823      * @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.
46824      * @return {Roo.ContentPanel} this
46825      */
46826     load : function(){
46827         var um = this.el.getUpdateManager();
46828         um.update.apply(um, arguments);
46829         return this;
46830     },
46831
46832
46833     /**
46834      * 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.
46835      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46836      * @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)
46837      * @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)
46838      * @return {Roo.UpdateManager} The UpdateManager
46839      */
46840     setUrl : function(url, params, loadOnce){
46841         if(this.refreshDelegate){
46842             this.removeListener("activate", this.refreshDelegate);
46843         }
46844         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46845         this.on("activate", this.refreshDelegate);
46846         return this.el.getUpdateManager();
46847     },
46848     
46849     _handleRefresh : function(url, params, loadOnce){
46850         if(!loadOnce || !this.loaded){
46851             var updater = this.el.getUpdateManager();
46852             updater.update(url, params, this._setLoaded.createDelegate(this));
46853         }
46854     },
46855     
46856     _setLoaded : function(){
46857         this.loaded = true;
46858     }, 
46859     
46860     /**
46861      * Returns this panel's id
46862      * @return {String} 
46863      */
46864     getId : function(){
46865         return this.el.id;
46866     },
46867     
46868     /** 
46869      * Returns this panel's element - used by regiosn to add.
46870      * @return {Roo.Element} 
46871      */
46872     getEl : function(){
46873         return this.wrapEl || this.el;
46874     },
46875     
46876     adjustForComponents : function(width, height){
46877         if(this.resizeEl != this.el){
46878             width -= this.el.getFrameWidth('lr');
46879             height -= this.el.getFrameWidth('tb');
46880         }
46881         if(this.toolbar){
46882             var te = this.toolbar.getEl();
46883             height -= te.getHeight();
46884             te.setWidth(width);
46885         }
46886         if(this.adjustments){
46887             width += this.adjustments[0];
46888             height += this.adjustments[1];
46889         }
46890         return {"width": width, "height": height};
46891     },
46892     
46893     setSize : function(width, height){
46894         if(this.fitToFrame && !this.ignoreResize(width, height)){
46895             if(this.fitContainer && this.resizeEl != this.el){
46896                 this.el.setSize(width, height);
46897             }
46898             var size = this.adjustForComponents(width, height);
46899             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46900             this.fireEvent('resize', this, size.width, size.height);
46901         }
46902     },
46903     
46904     /**
46905      * Returns this panel's title
46906      * @return {String} 
46907      */
46908     getTitle : function(){
46909         return this.title;
46910     },
46911     
46912     /**
46913      * Set this panel's title
46914      * @param {String} title
46915      */
46916     setTitle : function(title){
46917         this.title = title;
46918         if(this.region){
46919             this.region.updatePanelTitle(this, title);
46920         }
46921     },
46922     
46923     /**
46924      * Returns true is this panel was configured to be closable
46925      * @return {Boolean} 
46926      */
46927     isClosable : function(){
46928         return this.closable;
46929     },
46930     
46931     beforeSlide : function(){
46932         this.el.clip();
46933         this.resizeEl.clip();
46934     },
46935     
46936     afterSlide : function(){
46937         this.el.unclip();
46938         this.resizeEl.unclip();
46939     },
46940     
46941     /**
46942      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46943      *   Will fail silently if the {@link #setUrl} method has not been called.
46944      *   This does not activate the panel, just updates its content.
46945      */
46946     refresh : function(){
46947         if(this.refreshDelegate){
46948            this.loaded = false;
46949            this.refreshDelegate();
46950         }
46951     },
46952     
46953     /**
46954      * Destroys this panel
46955      */
46956     destroy : function(){
46957         this.el.removeAllListeners();
46958         var tempEl = document.createElement("span");
46959         tempEl.appendChild(this.el.dom);
46960         tempEl.innerHTML = "";
46961         this.el.remove();
46962         this.el = null;
46963     },
46964     
46965     /**
46966      * form - if the content panel contains a form - this is a reference to it.
46967      * @type {Roo.form.Form}
46968      */
46969     form : false,
46970     /**
46971      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46972      *    This contains a reference to it.
46973      * @type {Roo.View}
46974      */
46975     view : false,
46976     
46977       /**
46978      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46979      * <pre><code>
46980
46981 layout.addxtype({
46982        xtype : 'Form',
46983        items: [ .... ]
46984    }
46985 );
46986
46987 </code></pre>
46988      * @param {Object} cfg Xtype definition of item to add.
46989      */
46990     
46991     addxtype : function(cfg) {
46992         // add form..
46993         if (cfg.xtype.match(/^Form$/)) {
46994             var el = this.el.createChild();
46995
46996             this.form = new  Roo.form.Form(cfg);
46997             
46998             
46999             if ( this.form.allItems.length) this.form.render(el.dom);
47000             return this.form;
47001         }
47002         // should only have one of theses..
47003         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
47004             // views..
47005             cfg.el = this.el.appendChild(document.createElement("div"));
47006             // factory?
47007             
47008             var ret = new Roo.factory(cfg);
47009             ret.render && ret.render(false, ''); // render blank..
47010             this.view = ret;
47011             return ret;
47012         }
47013         return false;
47014     }
47015 });
47016
47017 /**
47018  * @class Roo.GridPanel
47019  * @extends Roo.ContentPanel
47020  * @constructor
47021  * Create a new GridPanel.
47022  * @param {Roo.grid.Grid} grid The grid for this panel
47023  * @param {String/Object} config A string to set only the panel's title, or a config object
47024  */
47025 Roo.GridPanel = function(grid, config){
47026     
47027   
47028     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
47029         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
47030         
47031     this.wrapper.dom.appendChild(grid.getGridEl().dom);
47032     
47033     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
47034     
47035     if(this.toolbar){
47036         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
47037     }
47038     // xtype created footer. - not sure if will work as we normally have to render first..
47039     if (this.footer && !this.footer.el && this.footer.xtype) {
47040         
47041         this.footer.container = this.grid.getView().getFooterPanel(true);
47042         this.footer.dataSource = this.grid.dataSource;
47043         this.footer = Roo.factory(this.footer, Roo);
47044         
47045     }
47046     
47047     grid.monitorWindowResize = false; // turn off autosizing
47048     grid.autoHeight = false;
47049     grid.autoWidth = false;
47050     this.grid = grid;
47051     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
47052 };
47053
47054 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
47055     getId : function(){
47056         return this.grid.id;
47057     },
47058     
47059     /**
47060      * Returns the grid for this panel
47061      * @return {Roo.grid.Grid} 
47062      */
47063     getGrid : function(){
47064         return this.grid;    
47065     },
47066     
47067     setSize : function(width, height){
47068         if(!this.ignoreResize(width, height)){
47069             var grid = this.grid;
47070             var size = this.adjustForComponents(width, height);
47071             grid.getGridEl().setSize(size.width, size.height);
47072             grid.autoSize();
47073         }
47074     },
47075     
47076     beforeSlide : function(){
47077         this.grid.getView().scroller.clip();
47078     },
47079     
47080     afterSlide : function(){
47081         this.grid.getView().scroller.unclip();
47082     },
47083     
47084     destroy : function(){
47085         this.grid.destroy();
47086         delete this.grid;
47087         Roo.GridPanel.superclass.destroy.call(this); 
47088     }
47089 });
47090
47091
47092 /**
47093  * @class Roo.NestedLayoutPanel
47094  * @extends Roo.ContentPanel
47095  * @constructor
47096  * Create a new NestedLayoutPanel.
47097  * 
47098  * 
47099  * @param {Roo.BorderLayout} layout The layout for this panel
47100  * @param {String/Object} config A string to set only the title or a config object
47101  */
47102 Roo.NestedLayoutPanel = function(layout, config)
47103 {
47104     // construct with only one argument..
47105     /* FIXME - implement nicer consturctors
47106     if (layout.layout) {
47107         config = layout;
47108         layout = config.layout;
47109         delete config.layout;
47110     }
47111     if (layout.xtype && !layout.getEl) {
47112         // then layout needs constructing..
47113         layout = Roo.factory(layout, Roo);
47114     }
47115     */
47116     
47117     
47118     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
47119     
47120     layout.monitorWindowResize = false; // turn off autosizing
47121     this.layout = layout;
47122     this.layout.getEl().addClass("x-layout-nested-layout");
47123     
47124     
47125     
47126     
47127 };
47128
47129 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
47130
47131     setSize : function(width, height){
47132         if(!this.ignoreResize(width, height)){
47133             var size = this.adjustForComponents(width, height);
47134             var el = this.layout.getEl();
47135             el.setSize(size.width, size.height);
47136             var touch = el.dom.offsetWidth;
47137             this.layout.layout();
47138             // ie requires a double layout on the first pass
47139             if(Roo.isIE && !this.initialized){
47140                 this.initialized = true;
47141                 this.layout.layout();
47142             }
47143         }
47144     },
47145     
47146     // activate all subpanels if not currently active..
47147     
47148     setActiveState : function(active){
47149         this.active = active;
47150         if(!active){
47151             this.fireEvent("deactivate", this);
47152             return;
47153         }
47154         
47155         this.fireEvent("activate", this);
47156         // not sure if this should happen before or after..
47157         if (!this.layout) {
47158             return; // should not happen..
47159         }
47160         var reg = false;
47161         for (var r in this.layout.regions) {
47162             reg = this.layout.getRegion(r);
47163             if (reg.getActivePanel()) {
47164                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
47165                 reg.setActivePanel(reg.getActivePanel());
47166                 continue;
47167             }
47168             if (!reg.panels.length) {
47169                 continue;
47170             }
47171             reg.showPanel(reg.getPanel(0));
47172         }
47173         
47174         
47175         
47176         
47177     },
47178     
47179     /**
47180      * Returns the nested BorderLayout for this panel
47181      * @return {Roo.BorderLayout} 
47182      */
47183     getLayout : function(){
47184         return this.layout;
47185     },
47186     
47187      /**
47188      * Adds a xtype elements to the layout of the nested panel
47189      * <pre><code>
47190
47191 panel.addxtype({
47192        xtype : 'ContentPanel',
47193        region: 'west',
47194        items: [ .... ]
47195    }
47196 );
47197
47198 panel.addxtype({
47199         xtype : 'NestedLayoutPanel',
47200         region: 'west',
47201         layout: {
47202            center: { },
47203            west: { }   
47204         },
47205         items : [ ... list of content panels or nested layout panels.. ]
47206    }
47207 );
47208 </code></pre>
47209      * @param {Object} cfg Xtype definition of item to add.
47210      */
47211     addxtype : function(cfg) {
47212         return this.layout.addxtype(cfg);
47213     
47214     }
47215 });
47216
47217 Roo.ScrollPanel = function(el, config, content){
47218     config = config || {};
47219     config.fitToFrame = true;
47220     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47221     
47222     this.el.dom.style.overflow = "hidden";
47223     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47224     this.el.removeClass("x-layout-inactive-content");
47225     this.el.on("mousewheel", this.onWheel, this);
47226
47227     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47228     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47229     up.unselectable(); down.unselectable();
47230     up.on("click", this.scrollUp, this);
47231     down.on("click", this.scrollDown, this);
47232     up.addClassOnOver("x-scroller-btn-over");
47233     down.addClassOnOver("x-scroller-btn-over");
47234     up.addClassOnClick("x-scroller-btn-click");
47235     down.addClassOnClick("x-scroller-btn-click");
47236     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47237
47238     this.resizeEl = this.el;
47239     this.el = wrap; this.up = up; this.down = down;
47240 };
47241
47242 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47243     increment : 100,
47244     wheelIncrement : 5,
47245     scrollUp : function(){
47246         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47247     },
47248
47249     scrollDown : function(){
47250         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47251     },
47252
47253     afterScroll : function(){
47254         var el = this.resizeEl;
47255         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47256         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47257         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47258     },
47259
47260     setSize : function(){
47261         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47262         this.afterScroll();
47263     },
47264
47265     onWheel : function(e){
47266         var d = e.getWheelDelta();
47267         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47268         this.afterScroll();
47269         e.stopEvent();
47270     },
47271
47272     setContent : function(content, loadScripts){
47273         this.resizeEl.update(content, loadScripts);
47274     }
47275
47276 });
47277
47278
47279
47280
47281
47282
47283
47284
47285
47286 /**
47287  * @class Roo.TreePanel
47288  * @extends Roo.ContentPanel
47289  * @constructor
47290  * Create a new TreePanel. - defaults to fit/scoll contents.
47291  * @param {String/Object} config A string to set only the panel's title, or a config object
47292  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47293  */
47294 Roo.TreePanel = function(config){
47295     var el = config.el;
47296     var tree = config.tree;
47297     delete config.tree; 
47298     delete config.el; // hopefull!
47299     
47300     // wrapper for IE7 strict & safari scroll issue
47301     
47302     var treeEl = el.createChild();
47303     config.resizeEl = treeEl;
47304     
47305     
47306     
47307     Roo.TreePanel.superclass.constructor.call(this, el, config);
47308  
47309  
47310     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47311     //console.log(tree);
47312     this.on('activate', function()
47313     {
47314         if (this.tree.rendered) {
47315             return;
47316         }
47317         //console.log('render tree');
47318         this.tree.render();
47319     });
47320     
47321     this.on('resize',  function (cp, w, h) {
47322             this.tree.innerCt.setWidth(w);
47323             this.tree.innerCt.setHeight(h);
47324             this.tree.innerCt.setStyle('overflow-y', 'auto');
47325     });
47326
47327         
47328     
47329 };
47330
47331 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47332     fitToFrame : true,
47333     autoScroll : true
47334 });
47335
47336
47337
47338
47339
47340
47341
47342
47343
47344
47345
47346 /*
47347  * Based on:
47348  * Ext JS Library 1.1.1
47349  * Copyright(c) 2006-2007, Ext JS, LLC.
47350  *
47351  * Originally Released Under LGPL - original licence link has changed is not relivant.
47352  *
47353  * Fork - LGPL
47354  * <script type="text/javascript">
47355  */
47356  
47357
47358 /**
47359  * @class Roo.ReaderLayout
47360  * @extends Roo.BorderLayout
47361  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47362  * center region containing two nested regions (a top one for a list view and one for item preview below),
47363  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47364  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47365  * expedites the setup of the overall layout and regions for this common application style.
47366  * Example:
47367  <pre><code>
47368 var reader = new Roo.ReaderLayout();
47369 var CP = Roo.ContentPanel;  // shortcut for adding
47370
47371 reader.beginUpdate();
47372 reader.add("north", new CP("north", "North"));
47373 reader.add("west", new CP("west", {title: "West"}));
47374 reader.add("east", new CP("east", {title: "East"}));
47375
47376 reader.regions.listView.add(new CP("listView", "List"));
47377 reader.regions.preview.add(new CP("preview", "Preview"));
47378 reader.endUpdate();
47379 </code></pre>
47380 * @constructor
47381 * Create a new ReaderLayout
47382 * @param {Object} config Configuration options
47383 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47384 * document.body if omitted)
47385 */
47386 Roo.ReaderLayout = function(config, renderTo){
47387     var c = config || {size:{}};
47388     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47389         north: c.north !== false ? Roo.apply({
47390             split:false,
47391             initialSize: 32,
47392             titlebar: false
47393         }, c.north) : false,
47394         west: c.west !== false ? Roo.apply({
47395             split:true,
47396             initialSize: 200,
47397             minSize: 175,
47398             maxSize: 400,
47399             titlebar: true,
47400             collapsible: true,
47401             animate: true,
47402             margins:{left:5,right:0,bottom:5,top:5},
47403             cmargins:{left:5,right:5,bottom:5,top:5}
47404         }, c.west) : false,
47405         east: c.east !== false ? Roo.apply({
47406             split:true,
47407             initialSize: 200,
47408             minSize: 175,
47409             maxSize: 400,
47410             titlebar: true,
47411             collapsible: true,
47412             animate: true,
47413             margins:{left:0,right:5,bottom:5,top:5},
47414             cmargins:{left:5,right:5,bottom:5,top:5}
47415         }, c.east) : false,
47416         center: Roo.apply({
47417             tabPosition: 'top',
47418             autoScroll:false,
47419             closeOnTab: true,
47420             titlebar:false,
47421             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47422         }, c.center)
47423     });
47424
47425     this.el.addClass('x-reader');
47426
47427     this.beginUpdate();
47428
47429     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47430         south: c.preview !== false ? Roo.apply({
47431             split:true,
47432             initialSize: 200,
47433             minSize: 100,
47434             autoScroll:true,
47435             collapsible:true,
47436             titlebar: true,
47437             cmargins:{top:5,left:0, right:0, bottom:0}
47438         }, c.preview) : false,
47439         center: Roo.apply({
47440             autoScroll:false,
47441             titlebar:false,
47442             minHeight:200
47443         }, c.listView)
47444     });
47445     this.add('center', new Roo.NestedLayoutPanel(inner,
47446             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47447
47448     this.endUpdate();
47449
47450     this.regions.preview = inner.getRegion('south');
47451     this.regions.listView = inner.getRegion('center');
47452 };
47453
47454 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47455  * Based on:
47456  * Ext JS Library 1.1.1
47457  * Copyright(c) 2006-2007, Ext JS, LLC.
47458  *
47459  * Originally Released Under LGPL - original licence link has changed is not relivant.
47460  *
47461  * Fork - LGPL
47462  * <script type="text/javascript">
47463  */
47464  
47465 /**
47466  * @class Roo.grid.Grid
47467  * @extends Roo.util.Observable
47468  * This class represents the primary interface of a component based grid control.
47469  * <br><br>Usage:<pre><code>
47470  var grid = new Roo.grid.Grid("my-container-id", {
47471      ds: myDataStore,
47472      cm: myColModel,
47473      selModel: mySelectionModel,
47474      autoSizeColumns: true,
47475      monitorWindowResize: false,
47476      trackMouseOver: true
47477  });
47478  // set any options
47479  grid.render();
47480  * </code></pre>
47481  * <b>Common Problems:</b><br/>
47482  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47483  * element will correct this<br/>
47484  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47485  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47486  * are unpredictable.<br/>
47487  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47488  * grid to calculate dimensions/offsets.<br/>
47489   * @constructor
47490  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47491  * The container MUST have some type of size defined for the grid to fill. The container will be
47492  * automatically set to position relative if it isn't already.
47493  * @param {Object} config A config object that sets properties on this grid.
47494  */
47495 Roo.grid.Grid = function(container, config){
47496         // initialize the container
47497         this.container = Roo.get(container);
47498         this.container.update("");
47499         this.container.setStyle("overflow", "hidden");
47500     this.container.addClass('x-grid-container');
47501
47502     this.id = this.container.id;
47503
47504     Roo.apply(this, config);
47505     // check and correct shorthanded configs
47506     if(this.ds){
47507         this.dataSource = this.ds;
47508         delete this.ds;
47509     }
47510     if(this.cm){
47511         this.colModel = this.cm;
47512         delete this.cm;
47513     }
47514     if(this.sm){
47515         this.selModel = this.sm;
47516         delete this.sm;
47517     }
47518
47519     if (this.selModel) {
47520         this.selModel = Roo.factory(this.selModel, Roo.grid);
47521         this.sm = this.selModel;
47522         this.sm.xmodule = this.xmodule || false;
47523     }
47524     if (typeof(this.colModel.config) == 'undefined') {
47525         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47526         this.cm = this.colModel;
47527         this.cm.xmodule = this.xmodule || false;
47528     }
47529     if (this.dataSource) {
47530         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47531         this.ds = this.dataSource;
47532         this.ds.xmodule = this.xmodule || false;
47533          
47534     }
47535     
47536     
47537     
47538     if(this.width){
47539         this.container.setWidth(this.width);
47540     }
47541
47542     if(this.height){
47543         this.container.setHeight(this.height);
47544     }
47545     /** @private */
47546         this.addEvents({
47547         // raw events
47548         /**
47549          * @event click
47550          * The raw click event for the entire grid.
47551          * @param {Roo.EventObject} e
47552          */
47553         "click" : true,
47554         /**
47555          * @event dblclick
47556          * The raw dblclick event for the entire grid.
47557          * @param {Roo.EventObject} e
47558          */
47559         "dblclick" : true,
47560         /**
47561          * @event contextmenu
47562          * The raw contextmenu event for the entire grid.
47563          * @param {Roo.EventObject} e
47564          */
47565         "contextmenu" : true,
47566         /**
47567          * @event mousedown
47568          * The raw mousedown event for the entire grid.
47569          * @param {Roo.EventObject} e
47570          */
47571         "mousedown" : true,
47572         /**
47573          * @event mouseup
47574          * The raw mouseup event for the entire grid.
47575          * @param {Roo.EventObject} e
47576          */
47577         "mouseup" : true,
47578         /**
47579          * @event mouseover
47580          * The raw mouseover event for the entire grid.
47581          * @param {Roo.EventObject} e
47582          */
47583         "mouseover" : true,
47584         /**
47585          * @event mouseout
47586          * The raw mouseout event for the entire grid.
47587          * @param {Roo.EventObject} e
47588          */
47589         "mouseout" : true,
47590         /**
47591          * @event keypress
47592          * The raw keypress event for the entire grid.
47593          * @param {Roo.EventObject} e
47594          */
47595         "keypress" : true,
47596         /**
47597          * @event keydown
47598          * The raw keydown event for the entire grid.
47599          * @param {Roo.EventObject} e
47600          */
47601         "keydown" : true,
47602
47603         // custom events
47604
47605         /**
47606          * @event cellclick
47607          * Fires when a cell is clicked
47608          * @param {Grid} this
47609          * @param {Number} rowIndex
47610          * @param {Number} columnIndex
47611          * @param {Roo.EventObject} e
47612          */
47613         "cellclick" : true,
47614         /**
47615          * @event celldblclick
47616          * Fires when a cell is double clicked
47617          * @param {Grid} this
47618          * @param {Number} rowIndex
47619          * @param {Number} columnIndex
47620          * @param {Roo.EventObject} e
47621          */
47622         "celldblclick" : true,
47623         /**
47624          * @event rowclick
47625          * Fires when a row is clicked
47626          * @param {Grid} this
47627          * @param {Number} rowIndex
47628          * @param {Roo.EventObject} e
47629          */
47630         "rowclick" : true,
47631         /**
47632          * @event rowdblclick
47633          * Fires when a row is double clicked
47634          * @param {Grid} this
47635          * @param {Number} rowIndex
47636          * @param {Roo.EventObject} e
47637          */
47638         "rowdblclick" : true,
47639         /**
47640          * @event headerclick
47641          * Fires when a header is clicked
47642          * @param {Grid} this
47643          * @param {Number} columnIndex
47644          * @param {Roo.EventObject} e
47645          */
47646         "headerclick" : true,
47647         /**
47648          * @event headerdblclick
47649          * Fires when a header cell is double clicked
47650          * @param {Grid} this
47651          * @param {Number} columnIndex
47652          * @param {Roo.EventObject} e
47653          */
47654         "headerdblclick" : true,
47655         /**
47656          * @event rowcontextmenu
47657          * Fires when a row is right clicked
47658          * @param {Grid} this
47659          * @param {Number} rowIndex
47660          * @param {Roo.EventObject} e
47661          */
47662         "rowcontextmenu" : true,
47663         /**
47664          * @event cellcontextmenu
47665          * Fires when a cell is right clicked
47666          * @param {Grid} this
47667          * @param {Number} rowIndex
47668          * @param {Number} cellIndex
47669          * @param {Roo.EventObject} e
47670          */
47671          "cellcontextmenu" : true,
47672         /**
47673          * @event headercontextmenu
47674          * Fires when a header is right clicked
47675          * @param {Grid} this
47676          * @param {Number} columnIndex
47677          * @param {Roo.EventObject} e
47678          */
47679         "headercontextmenu" : true,
47680         /**
47681          * @event bodyscroll
47682          * Fires when the body element is scrolled
47683          * @param {Number} scrollLeft
47684          * @param {Number} scrollTop
47685          */
47686         "bodyscroll" : true,
47687         /**
47688          * @event columnresize
47689          * Fires when the user resizes a column
47690          * @param {Number} columnIndex
47691          * @param {Number} newSize
47692          */
47693         "columnresize" : true,
47694         /**
47695          * @event columnmove
47696          * Fires when the user moves a column
47697          * @param {Number} oldIndex
47698          * @param {Number} newIndex
47699          */
47700         "columnmove" : true,
47701         /**
47702          * @event startdrag
47703          * Fires when row(s) start being dragged
47704          * @param {Grid} this
47705          * @param {Roo.GridDD} dd The drag drop object
47706          * @param {event} e The raw browser event
47707          */
47708         "startdrag" : true,
47709         /**
47710          * @event enddrag
47711          * Fires when a drag operation is complete
47712          * @param {Grid} this
47713          * @param {Roo.GridDD} dd The drag drop object
47714          * @param {event} e The raw browser event
47715          */
47716         "enddrag" : true,
47717         /**
47718          * @event dragdrop
47719          * Fires when dragged row(s) are dropped on a valid DD target
47720          * @param {Grid} this
47721          * @param {Roo.GridDD} dd The drag drop object
47722          * @param {String} targetId The target drag drop object
47723          * @param {event} e The raw browser event
47724          */
47725         "dragdrop" : true,
47726         /**
47727          * @event dragover
47728          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47729          * @param {Grid} this
47730          * @param {Roo.GridDD} dd The drag drop object
47731          * @param {String} targetId The target drag drop object
47732          * @param {event} e The raw browser event
47733          */
47734         "dragover" : true,
47735         /**
47736          * @event dragenter
47737          *  Fires when the dragged row(s) first cross another DD target while being dragged
47738          * @param {Grid} this
47739          * @param {Roo.GridDD} dd The drag drop object
47740          * @param {String} targetId The target drag drop object
47741          * @param {event} e The raw browser event
47742          */
47743         "dragenter" : true,
47744         /**
47745          * @event dragout
47746          * Fires when the dragged row(s) leave another DD target while being dragged
47747          * @param {Grid} this
47748          * @param {Roo.GridDD} dd The drag drop object
47749          * @param {String} targetId The target drag drop object
47750          * @param {event} e The raw browser event
47751          */
47752         "dragout" : true,
47753         /**
47754          * @event rowclass
47755          * Fires when a row is rendered, so you can change add a style to it.
47756          * @param {GridView} gridview   The grid view
47757          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47758          */
47759         'rowclass' : true,
47760
47761         /**
47762          * @event render
47763          * Fires when the grid is rendered
47764          * @param {Grid} grid
47765          */
47766         'render' : true
47767     });
47768
47769     Roo.grid.Grid.superclass.constructor.call(this);
47770 };
47771 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47772     
47773     /**
47774      * @cfg {String} ddGroup - drag drop group.
47775      */
47776
47777     /**
47778      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47779      */
47780     minColumnWidth : 25,
47781
47782     /**
47783      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47784      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47785      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47786      */
47787     autoSizeColumns : false,
47788
47789     /**
47790      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47791      */
47792     autoSizeHeaders : true,
47793
47794     /**
47795      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47796      */
47797     monitorWindowResize : true,
47798
47799     /**
47800      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47801      * rows measured to get a columns size. Default is 0 (all rows).
47802      */
47803     maxRowsToMeasure : 0,
47804
47805     /**
47806      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47807      */
47808     trackMouseOver : true,
47809
47810     /**
47811     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47812     */
47813     
47814     /**
47815     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47816     */
47817     enableDragDrop : false,
47818     
47819     /**
47820     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47821     */
47822     enableColumnMove : true,
47823     
47824     /**
47825     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47826     */
47827     enableColumnHide : true,
47828     
47829     /**
47830     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47831     */
47832     enableRowHeightSync : false,
47833     
47834     /**
47835     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47836     */
47837     stripeRows : true,
47838     
47839     /**
47840     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47841     */
47842     autoHeight : false,
47843
47844     /**
47845      * @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.
47846      */
47847     autoExpandColumn : false,
47848
47849     /**
47850     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47851     * Default is 50.
47852     */
47853     autoExpandMin : 50,
47854
47855     /**
47856     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47857     */
47858     autoExpandMax : 1000,
47859
47860     /**
47861     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47862     */
47863     view : null,
47864
47865     /**
47866     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47867     */
47868     loadMask : false,
47869     /**
47870     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47871     */
47872     dropTarget: false,
47873     
47874    
47875     
47876     // private
47877     rendered : false,
47878
47879     /**
47880     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47881     * of a fixed width. Default is false.
47882     */
47883     /**
47884     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47885     */
47886     /**
47887      * Called once after all setup has been completed and the grid is ready to be rendered.
47888      * @return {Roo.grid.Grid} this
47889      */
47890     render : function()
47891     {
47892         var c = this.container;
47893         // try to detect autoHeight/width mode
47894         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47895             this.autoHeight = true;
47896         }
47897         var view = this.getView();
47898         view.init(this);
47899
47900         c.on("click", this.onClick, this);
47901         c.on("dblclick", this.onDblClick, this);
47902         c.on("contextmenu", this.onContextMenu, this);
47903         c.on("keydown", this.onKeyDown, this);
47904
47905         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47906
47907         this.getSelectionModel().init(this);
47908
47909         view.render();
47910
47911         if(this.loadMask){
47912             this.loadMask = new Roo.LoadMask(this.container,
47913                     Roo.apply({store:this.dataSource}, this.loadMask));
47914         }
47915         
47916         
47917         if (this.toolbar && this.toolbar.xtype) {
47918             this.toolbar.container = this.getView().getHeaderPanel(true);
47919             this.toolbar = new Roo.Toolbar(this.toolbar);
47920         }
47921         if (this.footer && this.footer.xtype) {
47922             this.footer.dataSource = this.getDataSource();
47923             this.footer.container = this.getView().getFooterPanel(true);
47924             this.footer = Roo.factory(this.footer, Roo);
47925         }
47926         if (this.dropTarget && this.dropTarget.xtype) {
47927             delete this.dropTarget.xtype;
47928             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47929         }
47930         
47931         
47932         this.rendered = true;
47933         this.fireEvent('render', this);
47934         return this;
47935     },
47936
47937         /**
47938          * Reconfigures the grid to use a different Store and Column Model.
47939          * The View will be bound to the new objects and refreshed.
47940          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47941          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47942          */
47943     reconfigure : function(dataSource, colModel){
47944         if(this.loadMask){
47945             this.loadMask.destroy();
47946             this.loadMask = new Roo.LoadMask(this.container,
47947                     Roo.apply({store:dataSource}, this.loadMask));
47948         }
47949         this.view.bind(dataSource, colModel);
47950         this.dataSource = dataSource;
47951         this.colModel = colModel;
47952         this.view.refresh(true);
47953     },
47954
47955     // private
47956     onKeyDown : function(e){
47957         this.fireEvent("keydown", e);
47958     },
47959
47960     /**
47961      * Destroy this grid.
47962      * @param {Boolean} removeEl True to remove the element
47963      */
47964     destroy : function(removeEl, keepListeners){
47965         if(this.loadMask){
47966             this.loadMask.destroy();
47967         }
47968         var c = this.container;
47969         c.removeAllListeners();
47970         this.view.destroy();
47971         this.colModel.purgeListeners();
47972         if(!keepListeners){
47973             this.purgeListeners();
47974         }
47975         c.update("");
47976         if(removeEl === true){
47977             c.remove();
47978         }
47979     },
47980
47981     // private
47982     processEvent : function(name, e){
47983         this.fireEvent(name, e);
47984         var t = e.getTarget();
47985         var v = this.view;
47986         var header = v.findHeaderIndex(t);
47987         if(header !== false){
47988             this.fireEvent("header" + name, this, header, e);
47989         }else{
47990             var row = v.findRowIndex(t);
47991             var cell = v.findCellIndex(t);
47992             if(row !== false){
47993                 this.fireEvent("row" + name, this, row, e);
47994                 if(cell !== false){
47995                     this.fireEvent("cell" + name, this, row, cell, e);
47996                 }
47997             }
47998         }
47999     },
48000
48001     // private
48002     onClick : function(e){
48003         this.processEvent("click", e);
48004     },
48005
48006     // private
48007     onContextMenu : function(e, t){
48008         this.processEvent("contextmenu", e);
48009     },
48010
48011     // private
48012     onDblClick : function(e){
48013         this.processEvent("dblclick", e);
48014     },
48015
48016     // private
48017     walkCells : function(row, col, step, fn, scope){
48018         var cm = this.colModel, clen = cm.getColumnCount();
48019         var ds = this.dataSource, rlen = ds.getCount(), first = true;
48020         if(step < 0){
48021             if(col < 0){
48022                 row--;
48023                 first = false;
48024             }
48025             while(row >= 0){
48026                 if(!first){
48027                     col = clen-1;
48028                 }
48029                 first = false;
48030                 while(col >= 0){
48031                     if(fn.call(scope || this, row, col, cm) === true){
48032                         return [row, col];
48033                     }
48034                     col--;
48035                 }
48036                 row--;
48037             }
48038         } else {
48039             if(col >= clen){
48040                 row++;
48041                 first = false;
48042             }
48043             while(row < rlen){
48044                 if(!first){
48045                     col = 0;
48046                 }
48047                 first = false;
48048                 while(col < clen){
48049                     if(fn.call(scope || this, row, col, cm) === true){
48050                         return [row, col];
48051                     }
48052                     col++;
48053                 }
48054                 row++;
48055             }
48056         }
48057         return null;
48058     },
48059
48060     // private
48061     getSelections : function(){
48062         return this.selModel.getSelections();
48063     },
48064
48065     /**
48066      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
48067      * but if manual update is required this method will initiate it.
48068      */
48069     autoSize : function(){
48070         if(this.rendered){
48071             this.view.layout();
48072             if(this.view.adjustForScroll){
48073                 this.view.adjustForScroll();
48074             }
48075         }
48076     },
48077
48078     /**
48079      * Returns the grid's underlying element.
48080      * @return {Element} The element
48081      */
48082     getGridEl : function(){
48083         return this.container;
48084     },
48085
48086     // private for compatibility, overridden by editor grid
48087     stopEditing : function(){},
48088
48089     /**
48090      * Returns the grid's SelectionModel.
48091      * @return {SelectionModel}
48092      */
48093     getSelectionModel : function(){
48094         if(!this.selModel){
48095             this.selModel = new Roo.grid.RowSelectionModel();
48096         }
48097         return this.selModel;
48098     },
48099
48100     /**
48101      * Returns the grid's DataSource.
48102      * @return {DataSource}
48103      */
48104     getDataSource : function(){
48105         return this.dataSource;
48106     },
48107
48108     /**
48109      * Returns the grid's ColumnModel.
48110      * @return {ColumnModel}
48111      */
48112     getColumnModel : function(){
48113         return this.colModel;
48114     },
48115
48116     /**
48117      * Returns the grid's GridView object.
48118      * @return {GridView}
48119      */
48120     getView : function(){
48121         if(!this.view){
48122             this.view = new Roo.grid.GridView(this.viewConfig);
48123         }
48124         return this.view;
48125     },
48126     /**
48127      * Called to get grid's drag proxy text, by default returns this.ddText.
48128      * @return {String}
48129      */
48130     getDragDropText : function(){
48131         var count = this.selModel.getCount();
48132         return String.format(this.ddText, count, count == 1 ? '' : 's');
48133     }
48134 });
48135 /**
48136  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
48137  * %0 is replaced with the number of selected rows.
48138  * @type String
48139  */
48140 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
48141  * Based on:
48142  * Ext JS Library 1.1.1
48143  * Copyright(c) 2006-2007, Ext JS, LLC.
48144  *
48145  * Originally Released Under LGPL - original licence link has changed is not relivant.
48146  *
48147  * Fork - LGPL
48148  * <script type="text/javascript">
48149  */
48150  
48151 Roo.grid.AbstractGridView = function(){
48152         this.grid = null;
48153         
48154         this.events = {
48155             "beforerowremoved" : true,
48156             "beforerowsinserted" : true,
48157             "beforerefresh" : true,
48158             "rowremoved" : true,
48159             "rowsinserted" : true,
48160             "rowupdated" : true,
48161             "refresh" : true
48162         };
48163     Roo.grid.AbstractGridView.superclass.constructor.call(this);
48164 };
48165
48166 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
48167     rowClass : "x-grid-row",
48168     cellClass : "x-grid-cell",
48169     tdClass : "x-grid-td",
48170     hdClass : "x-grid-hd",
48171     splitClass : "x-grid-hd-split",
48172     
48173         init: function(grid){
48174         this.grid = grid;
48175                 var cid = this.grid.getGridEl().id;
48176         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48177         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48178         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48179         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48180         },
48181         
48182         getColumnRenderers : function(){
48183         var renderers = [];
48184         var cm = this.grid.colModel;
48185         var colCount = cm.getColumnCount();
48186         for(var i = 0; i < colCount; i++){
48187             renderers[i] = cm.getRenderer(i);
48188         }
48189         return renderers;
48190     },
48191     
48192     getColumnIds : function(){
48193         var ids = [];
48194         var cm = this.grid.colModel;
48195         var colCount = cm.getColumnCount();
48196         for(var i = 0; i < colCount; i++){
48197             ids[i] = cm.getColumnId(i);
48198         }
48199         return ids;
48200     },
48201     
48202     getDataIndexes : function(){
48203         if(!this.indexMap){
48204             this.indexMap = this.buildIndexMap();
48205         }
48206         return this.indexMap.colToData;
48207     },
48208     
48209     getColumnIndexByDataIndex : function(dataIndex){
48210         if(!this.indexMap){
48211             this.indexMap = this.buildIndexMap();
48212         }
48213         return this.indexMap.dataToCol[dataIndex];
48214     },
48215     
48216     /**
48217      * Set a css style for a column dynamically. 
48218      * @param {Number} colIndex The index of the column
48219      * @param {String} name The css property name
48220      * @param {String} value The css value
48221      */
48222     setCSSStyle : function(colIndex, name, value){
48223         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48224         Roo.util.CSS.updateRule(selector, name, value);
48225     },
48226     
48227     generateRules : function(cm){
48228         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48229         Roo.util.CSS.removeStyleSheet(rulesId);
48230         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48231             var cid = cm.getColumnId(i);
48232             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48233                          this.tdSelector, cid, " {\n}\n",
48234                          this.hdSelector, cid, " {\n}\n",
48235                          this.splitSelector, cid, " {\n}\n");
48236         }
48237         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48238     }
48239 });/*
48240  * Based on:
48241  * Ext JS Library 1.1.1
48242  * Copyright(c) 2006-2007, Ext JS, LLC.
48243  *
48244  * Originally Released Under LGPL - original licence link has changed is not relivant.
48245  *
48246  * Fork - LGPL
48247  * <script type="text/javascript">
48248  */
48249
48250 // private
48251 // This is a support class used internally by the Grid components
48252 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48253     this.grid = grid;
48254     this.view = grid.getView();
48255     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48256     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48257     if(hd2){
48258         this.setHandleElId(Roo.id(hd));
48259         this.setOuterHandleElId(Roo.id(hd2));
48260     }
48261     this.scroll = false;
48262 };
48263 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48264     maxDragWidth: 120,
48265     getDragData : function(e){
48266         var t = Roo.lib.Event.getTarget(e);
48267         var h = this.view.findHeaderCell(t);
48268         if(h){
48269             return {ddel: h.firstChild, header:h};
48270         }
48271         return false;
48272     },
48273
48274     onInitDrag : function(e){
48275         this.view.headersDisabled = true;
48276         var clone = this.dragData.ddel.cloneNode(true);
48277         clone.id = Roo.id();
48278         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48279         this.proxy.update(clone);
48280         return true;
48281     },
48282
48283     afterValidDrop : function(){
48284         var v = this.view;
48285         setTimeout(function(){
48286             v.headersDisabled = false;
48287         }, 50);
48288     },
48289
48290     afterInvalidDrop : function(){
48291         var v = this.view;
48292         setTimeout(function(){
48293             v.headersDisabled = false;
48294         }, 50);
48295     }
48296 });
48297 /*
48298  * Based on:
48299  * Ext JS Library 1.1.1
48300  * Copyright(c) 2006-2007, Ext JS, LLC.
48301  *
48302  * Originally Released Under LGPL - original licence link has changed is not relivant.
48303  *
48304  * Fork - LGPL
48305  * <script type="text/javascript">
48306  */
48307 // private
48308 // This is a support class used internally by the Grid components
48309 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48310     this.grid = grid;
48311     this.view = grid.getView();
48312     // split the proxies so they don't interfere with mouse events
48313     this.proxyTop = Roo.DomHelper.append(document.body, {
48314         cls:"col-move-top", html:"&#160;"
48315     }, true);
48316     this.proxyBottom = Roo.DomHelper.append(document.body, {
48317         cls:"col-move-bottom", html:"&#160;"
48318     }, true);
48319     this.proxyTop.hide = this.proxyBottom.hide = function(){
48320         this.setLeftTop(-100,-100);
48321         this.setStyle("visibility", "hidden");
48322     };
48323     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48324     // temporarily disabled
48325     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48326     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48327 };
48328 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48329     proxyOffsets : [-4, -9],
48330     fly: Roo.Element.fly,
48331
48332     getTargetFromEvent : function(e){
48333         var t = Roo.lib.Event.getTarget(e);
48334         var cindex = this.view.findCellIndex(t);
48335         if(cindex !== false){
48336             return this.view.getHeaderCell(cindex);
48337         }
48338         return null;
48339     },
48340
48341     nextVisible : function(h){
48342         var v = this.view, cm = this.grid.colModel;
48343         h = h.nextSibling;
48344         while(h){
48345             if(!cm.isHidden(v.getCellIndex(h))){
48346                 return h;
48347             }
48348             h = h.nextSibling;
48349         }
48350         return null;
48351     },
48352
48353     prevVisible : function(h){
48354         var v = this.view, cm = this.grid.colModel;
48355         h = h.prevSibling;
48356         while(h){
48357             if(!cm.isHidden(v.getCellIndex(h))){
48358                 return h;
48359             }
48360             h = h.prevSibling;
48361         }
48362         return null;
48363     },
48364
48365     positionIndicator : function(h, n, e){
48366         var x = Roo.lib.Event.getPageX(e);
48367         var r = Roo.lib.Dom.getRegion(n.firstChild);
48368         var px, pt, py = r.top + this.proxyOffsets[1];
48369         if((r.right - x) <= (r.right-r.left)/2){
48370             px = r.right+this.view.borderWidth;
48371             pt = "after";
48372         }else{
48373             px = r.left;
48374             pt = "before";
48375         }
48376         var oldIndex = this.view.getCellIndex(h);
48377         var newIndex = this.view.getCellIndex(n);
48378
48379         if(this.grid.colModel.isFixed(newIndex)){
48380             return false;
48381         }
48382
48383         var locked = this.grid.colModel.isLocked(newIndex);
48384
48385         if(pt == "after"){
48386             newIndex++;
48387         }
48388         if(oldIndex < newIndex){
48389             newIndex--;
48390         }
48391         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48392             return false;
48393         }
48394         px +=  this.proxyOffsets[0];
48395         this.proxyTop.setLeftTop(px, py);
48396         this.proxyTop.show();
48397         if(!this.bottomOffset){
48398             this.bottomOffset = this.view.mainHd.getHeight();
48399         }
48400         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48401         this.proxyBottom.show();
48402         return pt;
48403     },
48404
48405     onNodeEnter : function(n, dd, e, data){
48406         if(data.header != n){
48407             this.positionIndicator(data.header, n, e);
48408         }
48409     },
48410
48411     onNodeOver : function(n, dd, e, data){
48412         var result = false;
48413         if(data.header != n){
48414             result = this.positionIndicator(data.header, n, e);
48415         }
48416         if(!result){
48417             this.proxyTop.hide();
48418             this.proxyBottom.hide();
48419         }
48420         return result ? this.dropAllowed : this.dropNotAllowed;
48421     },
48422
48423     onNodeOut : function(n, dd, e, data){
48424         this.proxyTop.hide();
48425         this.proxyBottom.hide();
48426     },
48427
48428     onNodeDrop : function(n, dd, e, data){
48429         var h = data.header;
48430         if(h != n){
48431             var cm = this.grid.colModel;
48432             var x = Roo.lib.Event.getPageX(e);
48433             var r = Roo.lib.Dom.getRegion(n.firstChild);
48434             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48435             var oldIndex = this.view.getCellIndex(h);
48436             var newIndex = this.view.getCellIndex(n);
48437             var locked = cm.isLocked(newIndex);
48438             if(pt == "after"){
48439                 newIndex++;
48440             }
48441             if(oldIndex < newIndex){
48442                 newIndex--;
48443             }
48444             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48445                 return false;
48446             }
48447             cm.setLocked(oldIndex, locked, true);
48448             cm.moveColumn(oldIndex, newIndex);
48449             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48450             return true;
48451         }
48452         return false;
48453     }
48454 });
48455 /*
48456  * Based on:
48457  * Ext JS Library 1.1.1
48458  * Copyright(c) 2006-2007, Ext JS, LLC.
48459  *
48460  * Originally Released Under LGPL - original licence link has changed is not relivant.
48461  *
48462  * Fork - LGPL
48463  * <script type="text/javascript">
48464  */
48465   
48466 /**
48467  * @class Roo.grid.GridView
48468  * @extends Roo.util.Observable
48469  *
48470  * @constructor
48471  * @param {Object} config
48472  */
48473 Roo.grid.GridView = function(config){
48474     Roo.grid.GridView.superclass.constructor.call(this);
48475     this.el = null;
48476
48477     Roo.apply(this, config);
48478 };
48479
48480 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48481
48482     
48483     rowClass : "x-grid-row",
48484
48485     cellClass : "x-grid-col",
48486
48487     tdClass : "x-grid-td",
48488
48489     hdClass : "x-grid-hd",
48490
48491     splitClass : "x-grid-split",
48492
48493     sortClasses : ["sort-asc", "sort-desc"],
48494
48495     enableMoveAnim : false,
48496
48497     hlColor: "C3DAF9",
48498
48499     dh : Roo.DomHelper,
48500
48501     fly : Roo.Element.fly,
48502
48503     css : Roo.util.CSS,
48504
48505     borderWidth: 1,
48506
48507     splitOffset: 3,
48508
48509     scrollIncrement : 22,
48510
48511     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48512
48513     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48514
48515     bind : function(ds, cm){
48516         if(this.ds){
48517             this.ds.un("load", this.onLoad, this);
48518             this.ds.un("datachanged", this.onDataChange, this);
48519             this.ds.un("add", this.onAdd, this);
48520             this.ds.un("remove", this.onRemove, this);
48521             this.ds.un("update", this.onUpdate, this);
48522             this.ds.un("clear", this.onClear, this);
48523         }
48524         if(ds){
48525             ds.on("load", this.onLoad, this);
48526             ds.on("datachanged", this.onDataChange, this);
48527             ds.on("add", this.onAdd, this);
48528             ds.on("remove", this.onRemove, this);
48529             ds.on("update", this.onUpdate, this);
48530             ds.on("clear", this.onClear, this);
48531         }
48532         this.ds = ds;
48533
48534         if(this.cm){
48535             this.cm.un("widthchange", this.onColWidthChange, this);
48536             this.cm.un("headerchange", this.onHeaderChange, this);
48537             this.cm.un("hiddenchange", this.onHiddenChange, this);
48538             this.cm.un("columnmoved", this.onColumnMove, this);
48539             this.cm.un("columnlockchange", this.onColumnLock, this);
48540         }
48541         if(cm){
48542             this.generateRules(cm);
48543             cm.on("widthchange", this.onColWidthChange, this);
48544             cm.on("headerchange", this.onHeaderChange, this);
48545             cm.on("hiddenchange", this.onHiddenChange, this);
48546             cm.on("columnmoved", this.onColumnMove, this);
48547             cm.on("columnlockchange", this.onColumnLock, this);
48548         }
48549         this.cm = cm;
48550     },
48551
48552     init: function(grid){
48553         Roo.grid.GridView.superclass.init.call(this, grid);
48554
48555         this.bind(grid.dataSource, grid.colModel);
48556
48557         grid.on("headerclick", this.handleHeaderClick, this);
48558
48559         if(grid.trackMouseOver){
48560             grid.on("mouseover", this.onRowOver, this);
48561             grid.on("mouseout", this.onRowOut, this);
48562         }
48563         grid.cancelTextSelection = function(){};
48564         this.gridId = grid.id;
48565
48566         var tpls = this.templates || {};
48567
48568         if(!tpls.master){
48569             tpls.master = new Roo.Template(
48570                '<div class="x-grid" hidefocus="true">',
48571                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48572                   '<div class="x-grid-topbar"></div>',
48573                   '<div class="x-grid-scroller"><div></div></div>',
48574                   '<div class="x-grid-locked">',
48575                       '<div class="x-grid-header">{lockedHeader}</div>',
48576                       '<div class="x-grid-body">{lockedBody}</div>',
48577                   "</div>",
48578                   '<div class="x-grid-viewport">',
48579                       '<div class="x-grid-header">{header}</div>',
48580                       '<div class="x-grid-body">{body}</div>',
48581                   "</div>",
48582                   '<div class="x-grid-bottombar"></div>',
48583                  
48584                   '<div class="x-grid-resize-proxy">&#160;</div>',
48585                "</div>"
48586             );
48587             tpls.master.disableformats = true;
48588         }
48589
48590         if(!tpls.header){
48591             tpls.header = new Roo.Template(
48592                '<table border="0" cellspacing="0" cellpadding="0">',
48593                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48594                "</table>{splits}"
48595             );
48596             tpls.header.disableformats = true;
48597         }
48598         tpls.header.compile();
48599
48600         if(!tpls.hcell){
48601             tpls.hcell = new Roo.Template(
48602                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48603                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48604                 "</div></td>"
48605              );
48606              tpls.hcell.disableFormats = true;
48607         }
48608         tpls.hcell.compile();
48609
48610         if(!tpls.hsplit){
48611             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48612             tpls.hsplit.disableFormats = true;
48613         }
48614         tpls.hsplit.compile();
48615
48616         if(!tpls.body){
48617             tpls.body = new Roo.Template(
48618                '<table border="0" cellspacing="0" cellpadding="0">',
48619                "<tbody>{rows}</tbody>",
48620                "</table>"
48621             );
48622             tpls.body.disableFormats = true;
48623         }
48624         tpls.body.compile();
48625
48626         if(!tpls.row){
48627             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48628             tpls.row.disableFormats = true;
48629         }
48630         tpls.row.compile();
48631
48632         if(!tpls.cell){
48633             tpls.cell = new Roo.Template(
48634                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48635                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48636                 "</td>"
48637             );
48638             tpls.cell.disableFormats = true;
48639         }
48640         tpls.cell.compile();
48641
48642         this.templates = tpls;
48643     },
48644
48645     // remap these for backwards compat
48646     onColWidthChange : function(){
48647         this.updateColumns.apply(this, arguments);
48648     },
48649     onHeaderChange : function(){
48650         this.updateHeaders.apply(this, arguments);
48651     }, 
48652     onHiddenChange : function(){
48653         this.handleHiddenChange.apply(this, arguments);
48654     },
48655     onColumnMove : function(){
48656         this.handleColumnMove.apply(this, arguments);
48657     },
48658     onColumnLock : function(){
48659         this.handleLockChange.apply(this, arguments);
48660     },
48661
48662     onDataChange : function(){
48663         this.refresh();
48664         this.updateHeaderSortState();
48665     },
48666
48667     onClear : function(){
48668         this.refresh();
48669     },
48670
48671     onUpdate : function(ds, record){
48672         this.refreshRow(record);
48673     },
48674
48675     refreshRow : function(record){
48676         var ds = this.ds, index;
48677         if(typeof record == 'number'){
48678             index = record;
48679             record = ds.getAt(index);
48680         }else{
48681             index = ds.indexOf(record);
48682         }
48683         this.insertRows(ds, index, index, true);
48684         this.onRemove(ds, record, index+1, true);
48685         this.syncRowHeights(index, index);
48686         this.layout();
48687         this.fireEvent("rowupdated", this, index, record);
48688     },
48689
48690     onAdd : function(ds, records, index){
48691         this.insertRows(ds, index, index + (records.length-1));
48692     },
48693
48694     onRemove : function(ds, record, index, isUpdate){
48695         if(isUpdate !== true){
48696             this.fireEvent("beforerowremoved", this, index, record);
48697         }
48698         var bt = this.getBodyTable(), lt = this.getLockedTable();
48699         if(bt.rows[index]){
48700             bt.firstChild.removeChild(bt.rows[index]);
48701         }
48702         if(lt.rows[index]){
48703             lt.firstChild.removeChild(lt.rows[index]);
48704         }
48705         if(isUpdate !== true){
48706             this.stripeRows(index);
48707             this.syncRowHeights(index, index);
48708             this.layout();
48709             this.fireEvent("rowremoved", this, index, record);
48710         }
48711     },
48712
48713     onLoad : function(){
48714         this.scrollToTop();
48715     },
48716
48717     /**
48718      * Scrolls the grid to the top
48719      */
48720     scrollToTop : function(){
48721         if(this.scroller){
48722             this.scroller.dom.scrollTop = 0;
48723             this.syncScroll();
48724         }
48725     },
48726
48727     /**
48728      * Gets a panel in the header of the grid that can be used for toolbars etc.
48729      * After modifying the contents of this panel a call to grid.autoSize() may be
48730      * required to register any changes in size.
48731      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48732      * @return Roo.Element
48733      */
48734     getHeaderPanel : function(doShow){
48735         if(doShow){
48736             this.headerPanel.show();
48737         }
48738         return this.headerPanel;
48739     },
48740
48741     /**
48742      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48743      * After modifying the contents of this panel a call to grid.autoSize() may be
48744      * required to register any changes in size.
48745      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48746      * @return Roo.Element
48747      */
48748     getFooterPanel : function(doShow){
48749         if(doShow){
48750             this.footerPanel.show();
48751         }
48752         return this.footerPanel;
48753     },
48754
48755     initElements : function(){
48756         var E = Roo.Element;
48757         var el = this.grid.getGridEl().dom.firstChild;
48758         var cs = el.childNodes;
48759
48760         this.el = new E(el);
48761         
48762          this.focusEl = new E(el.firstChild);
48763         this.focusEl.swallowEvent("click", true);
48764         
48765         this.headerPanel = new E(cs[1]);
48766         this.headerPanel.enableDisplayMode("block");
48767
48768         this.scroller = new E(cs[2]);
48769         this.scrollSizer = new E(this.scroller.dom.firstChild);
48770
48771         this.lockedWrap = new E(cs[3]);
48772         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48773         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48774
48775         this.mainWrap = new E(cs[4]);
48776         this.mainHd = new E(this.mainWrap.dom.firstChild);
48777         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48778
48779         this.footerPanel = new E(cs[5]);
48780         this.footerPanel.enableDisplayMode("block");
48781
48782         this.resizeProxy = new E(cs[6]);
48783
48784         this.headerSelector = String.format(
48785            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48786            this.lockedHd.id, this.mainHd.id
48787         );
48788
48789         this.splitterSelector = String.format(
48790            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48791            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48792         );
48793     },
48794     idToCssName : function(s)
48795     {
48796         return s.replace(/[^a-z0-9]+/ig, '-');
48797     },
48798
48799     getHeaderCell : function(index){
48800         return Roo.DomQuery.select(this.headerSelector)[index];
48801     },
48802
48803     getHeaderCellMeasure : function(index){
48804         return this.getHeaderCell(index).firstChild;
48805     },
48806
48807     getHeaderCellText : function(index){
48808         return this.getHeaderCell(index).firstChild.firstChild;
48809     },
48810
48811     getLockedTable : function(){
48812         return this.lockedBody.dom.firstChild;
48813     },
48814
48815     getBodyTable : function(){
48816         return this.mainBody.dom.firstChild;
48817     },
48818
48819     getLockedRow : function(index){
48820         return this.getLockedTable().rows[index];
48821     },
48822
48823     getRow : function(index){
48824         return this.getBodyTable().rows[index];
48825     },
48826
48827     getRowComposite : function(index){
48828         if(!this.rowEl){
48829             this.rowEl = new Roo.CompositeElementLite();
48830         }
48831         var els = [], lrow, mrow;
48832         if(lrow = this.getLockedRow(index)){
48833             els.push(lrow);
48834         }
48835         if(mrow = this.getRow(index)){
48836             els.push(mrow);
48837         }
48838         this.rowEl.elements = els;
48839         return this.rowEl;
48840     },
48841     /**
48842      * Gets the 'td' of the cell
48843      * 
48844      * @param {Integer} rowIndex row to select
48845      * @param {Integer} colIndex column to select
48846      * 
48847      * @return {Object} 
48848      */
48849     getCell : function(rowIndex, colIndex){
48850         var locked = this.cm.getLockedCount();
48851         var source;
48852         if(colIndex < locked){
48853             source = this.lockedBody.dom.firstChild;
48854         }else{
48855             source = this.mainBody.dom.firstChild;
48856             colIndex -= locked;
48857         }
48858         return source.rows[rowIndex].childNodes[colIndex];
48859     },
48860
48861     getCellText : function(rowIndex, colIndex){
48862         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48863     },
48864
48865     getCellBox : function(cell){
48866         var b = this.fly(cell).getBox();
48867         if(Roo.isOpera){ // opera fails to report the Y
48868             b.y = cell.offsetTop + this.mainBody.getY();
48869         }
48870         return b;
48871     },
48872
48873     getCellIndex : function(cell){
48874         var id = String(cell.className).match(this.cellRE);
48875         if(id){
48876             return parseInt(id[1], 10);
48877         }
48878         return 0;
48879     },
48880
48881     findHeaderIndex : function(n){
48882         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48883         return r ? this.getCellIndex(r) : false;
48884     },
48885
48886     findHeaderCell : function(n){
48887         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48888         return r ? r : false;
48889     },
48890
48891     findRowIndex : function(n){
48892         if(!n){
48893             return false;
48894         }
48895         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48896         return r ? r.rowIndex : false;
48897     },
48898
48899     findCellIndex : function(node){
48900         var stop = this.el.dom;
48901         while(node && node != stop){
48902             if(this.findRE.test(node.className)){
48903                 return this.getCellIndex(node);
48904             }
48905             node = node.parentNode;
48906         }
48907         return false;
48908     },
48909
48910     getColumnId : function(index){
48911         return this.cm.getColumnId(index);
48912     },
48913
48914     getSplitters : function()
48915     {
48916         if(this.splitterSelector){
48917            return Roo.DomQuery.select(this.splitterSelector);
48918         }else{
48919             return null;
48920       }
48921     },
48922
48923     getSplitter : function(index){
48924         return this.getSplitters()[index];
48925     },
48926
48927     onRowOver : function(e, t){
48928         var row;
48929         if((row = this.findRowIndex(t)) !== false){
48930             this.getRowComposite(row).addClass("x-grid-row-over");
48931         }
48932     },
48933
48934     onRowOut : function(e, t){
48935         var row;
48936         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48937             this.getRowComposite(row).removeClass("x-grid-row-over");
48938         }
48939     },
48940
48941     renderHeaders : function(){
48942         var cm = this.cm;
48943         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48944         var cb = [], lb = [], sb = [], lsb = [], p = {};
48945         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48946             p.cellId = "x-grid-hd-0-" + i;
48947             p.splitId = "x-grid-csplit-0-" + i;
48948             p.id = cm.getColumnId(i);
48949             p.title = cm.getColumnTooltip(i) || "";
48950             p.value = cm.getColumnHeader(i) || "";
48951             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48952             if(!cm.isLocked(i)){
48953                 cb[cb.length] = ct.apply(p);
48954                 sb[sb.length] = st.apply(p);
48955             }else{
48956                 lb[lb.length] = ct.apply(p);
48957                 lsb[lsb.length] = st.apply(p);
48958             }
48959         }
48960         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48961                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48962     },
48963
48964     updateHeaders : function(){
48965         var html = this.renderHeaders();
48966         this.lockedHd.update(html[0]);
48967         this.mainHd.update(html[1]);
48968     },
48969
48970     /**
48971      * Focuses the specified row.
48972      * @param {Number} row The row index
48973      */
48974     focusRow : function(row)
48975     {
48976         //Roo.log('GridView.focusRow');
48977         var x = this.scroller.dom.scrollLeft;
48978         this.focusCell(row, 0, false);
48979         this.scroller.dom.scrollLeft = x;
48980     },
48981
48982     /**
48983      * Focuses the specified cell.
48984      * @param {Number} row The row index
48985      * @param {Number} col The column index
48986      * @param {Boolean} hscroll false to disable horizontal scrolling
48987      */
48988     focusCell : function(row, col, hscroll)
48989     {
48990         //Roo.log('GridView.focusCell');
48991         var el = this.ensureVisible(row, col, hscroll);
48992         this.focusEl.alignTo(el, "tl-tl");
48993         if(Roo.isGecko){
48994             this.focusEl.focus();
48995         }else{
48996             this.focusEl.focus.defer(1, this.focusEl);
48997         }
48998     },
48999
49000     /**
49001      * Scrolls the specified cell into view
49002      * @param {Number} row The row index
49003      * @param {Number} col The column index
49004      * @param {Boolean} hscroll false to disable horizontal scrolling
49005      */
49006     ensureVisible : function(row, col, hscroll)
49007     {
49008         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
49009         //return null; //disable for testing.
49010         if(typeof row != "number"){
49011             row = row.rowIndex;
49012         }
49013         if(row < 0 && row >= this.ds.getCount()){
49014             return  null;
49015         }
49016         col = (col !== undefined ? col : 0);
49017         var cm = this.grid.colModel;
49018         while(cm.isHidden(col)){
49019             col++;
49020         }
49021
49022         var el = this.getCell(row, col);
49023         if(!el){
49024             return null;
49025         }
49026         var c = this.scroller.dom;
49027
49028         var ctop = parseInt(el.offsetTop, 10);
49029         var cleft = parseInt(el.offsetLeft, 10);
49030         var cbot = ctop + el.offsetHeight;
49031         var cright = cleft + el.offsetWidth;
49032         
49033         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
49034         var stop = parseInt(c.scrollTop, 10);
49035         var sleft = parseInt(c.scrollLeft, 10);
49036         var sbot = stop + ch;
49037         var sright = sleft + c.clientWidth;
49038         /*
49039         Roo.log('GridView.ensureVisible:' +
49040                 ' ctop:' + ctop +
49041                 ' c.clientHeight:' + c.clientHeight +
49042                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
49043                 ' stop:' + stop +
49044                 ' cbot:' + cbot +
49045                 ' sbot:' + sbot +
49046                 ' ch:' + ch  
49047                 );
49048         */
49049         if(ctop < stop){
49050              c.scrollTop = ctop;
49051             //Roo.log("set scrolltop to ctop DISABLE?");
49052         }else if(cbot > sbot){
49053             //Roo.log("set scrolltop to cbot-ch");
49054             c.scrollTop = cbot-ch;
49055         }
49056         
49057         if(hscroll !== false){
49058             if(cleft < sleft){
49059                 c.scrollLeft = cleft;
49060             }else if(cright > sright){
49061                 c.scrollLeft = cright-c.clientWidth;
49062             }
49063         }
49064          
49065         return el;
49066     },
49067
49068     updateColumns : function(){
49069         this.grid.stopEditing();
49070         var cm = this.grid.colModel, colIds = this.getColumnIds();
49071         //var totalWidth = cm.getTotalWidth();
49072         var pos = 0;
49073         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49074             //if(cm.isHidden(i)) continue;
49075             var w = cm.getColumnWidth(i);
49076             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49077             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
49078         }
49079         this.updateSplitters();
49080     },
49081
49082     generateRules : function(cm){
49083         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
49084         Roo.util.CSS.removeStyleSheet(rulesId);
49085         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49086             var cid = cm.getColumnId(i);
49087             var align = '';
49088             if(cm.config[i].align){
49089                 align = 'text-align:'+cm.config[i].align+';';
49090             }
49091             var hidden = '';
49092             if(cm.isHidden(i)){
49093                 hidden = 'display:none;';
49094             }
49095             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
49096             ruleBuf.push(
49097                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
49098                     this.hdSelector, cid, " {\n", align, width, "}\n",
49099                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
49100                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
49101         }
49102         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
49103     },
49104
49105     updateSplitters : function(){
49106         var cm = this.cm, s = this.getSplitters();
49107         if(s){ // splitters not created yet
49108             var pos = 0, locked = true;
49109             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
49110                 if(cm.isHidden(i)) continue;
49111                 var w = cm.getColumnWidth(i); // make sure it's a number
49112                 if(!cm.isLocked(i) && locked){
49113                     pos = 0;
49114                     locked = false;
49115                 }
49116                 pos += w;
49117                 s[i].style.left = (pos-this.splitOffset) + "px";
49118             }
49119         }
49120     },
49121
49122     handleHiddenChange : function(colModel, colIndex, hidden){
49123         if(hidden){
49124             this.hideColumn(colIndex);
49125         }else{
49126             this.unhideColumn(colIndex);
49127         }
49128     },
49129
49130     hideColumn : function(colIndex){
49131         var cid = this.getColumnId(colIndex);
49132         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
49133         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
49134         if(Roo.isSafari){
49135             this.updateHeaders();
49136         }
49137         this.updateSplitters();
49138         this.layout();
49139     },
49140
49141     unhideColumn : function(colIndex){
49142         var cid = this.getColumnId(colIndex);
49143         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
49144         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
49145
49146         if(Roo.isSafari){
49147             this.updateHeaders();
49148         }
49149         this.updateSplitters();
49150         this.layout();
49151     },
49152
49153     insertRows : function(dm, firstRow, lastRow, isUpdate){
49154         if(firstRow == 0 && lastRow == dm.getCount()-1){
49155             this.refresh();
49156         }else{
49157             if(!isUpdate){
49158                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
49159             }
49160             var s = this.getScrollState();
49161             var markup = this.renderRows(firstRow, lastRow);
49162             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
49163             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
49164             this.restoreScroll(s);
49165             if(!isUpdate){
49166                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
49167                 this.syncRowHeights(firstRow, lastRow);
49168                 this.stripeRows(firstRow);
49169                 this.layout();
49170             }
49171         }
49172     },
49173
49174     bufferRows : function(markup, target, index){
49175         var before = null, trows = target.rows, tbody = target.tBodies[0];
49176         if(index < trows.length){
49177             before = trows[index];
49178         }
49179         var b = document.createElement("div");
49180         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49181         var rows = b.firstChild.rows;
49182         for(var i = 0, len = rows.length; i < len; i++){
49183             if(before){
49184                 tbody.insertBefore(rows[0], before);
49185             }else{
49186                 tbody.appendChild(rows[0]);
49187             }
49188         }
49189         b.innerHTML = "";
49190         b = null;
49191     },
49192
49193     deleteRows : function(dm, firstRow, lastRow){
49194         if(dm.getRowCount()<1){
49195             this.fireEvent("beforerefresh", this);
49196             this.mainBody.update("");
49197             this.lockedBody.update("");
49198             this.fireEvent("refresh", this);
49199         }else{
49200             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49201             var bt = this.getBodyTable();
49202             var tbody = bt.firstChild;
49203             var rows = bt.rows;
49204             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49205                 tbody.removeChild(rows[firstRow]);
49206             }
49207             this.stripeRows(firstRow);
49208             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49209         }
49210     },
49211
49212     updateRows : function(dataSource, firstRow, lastRow){
49213         var s = this.getScrollState();
49214         this.refresh();
49215         this.restoreScroll(s);
49216     },
49217
49218     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49219         if(!noRefresh){
49220            this.refresh();
49221         }
49222         this.updateHeaderSortState();
49223     },
49224
49225     getScrollState : function(){
49226         
49227         var sb = this.scroller.dom;
49228         return {left: sb.scrollLeft, top: sb.scrollTop};
49229     },
49230
49231     stripeRows : function(startRow){
49232         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49233             return;
49234         }
49235         startRow = startRow || 0;
49236         var rows = this.getBodyTable().rows;
49237         var lrows = this.getLockedTable().rows;
49238         var cls = ' x-grid-row-alt ';
49239         for(var i = startRow, len = rows.length; i < len; i++){
49240             var row = rows[i], lrow = lrows[i];
49241             var isAlt = ((i+1) % 2 == 0);
49242             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49243             if(isAlt == hasAlt){
49244                 continue;
49245             }
49246             if(isAlt){
49247                 row.className += " x-grid-row-alt";
49248             }else{
49249                 row.className = row.className.replace("x-grid-row-alt", "");
49250             }
49251             if(lrow){
49252                 lrow.className = row.className;
49253             }
49254         }
49255     },
49256
49257     restoreScroll : function(state){
49258         //Roo.log('GridView.restoreScroll');
49259         var sb = this.scroller.dom;
49260         sb.scrollLeft = state.left;
49261         sb.scrollTop = state.top;
49262         this.syncScroll();
49263     },
49264
49265     syncScroll : function(){
49266         //Roo.log('GridView.syncScroll');
49267         var sb = this.scroller.dom;
49268         var sh = this.mainHd.dom;
49269         var bs = this.mainBody.dom;
49270         var lv = this.lockedBody.dom;
49271         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49272         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49273     },
49274
49275     handleScroll : function(e){
49276         this.syncScroll();
49277         var sb = this.scroller.dom;
49278         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49279         e.stopEvent();
49280     },
49281
49282     handleWheel : function(e){
49283         var d = e.getWheelDelta();
49284         this.scroller.dom.scrollTop -= d*22;
49285         // set this here to prevent jumpy scrolling on large tables
49286         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49287         e.stopEvent();
49288     },
49289
49290     renderRows : function(startRow, endRow){
49291         // pull in all the crap needed to render rows
49292         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49293         var colCount = cm.getColumnCount();
49294
49295         if(ds.getCount() < 1){
49296             return ["", ""];
49297         }
49298
49299         // build a map for all the columns
49300         var cs = [];
49301         for(var i = 0; i < colCount; i++){
49302             var name = cm.getDataIndex(i);
49303             cs[i] = {
49304                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49305                 renderer : cm.getRenderer(i),
49306                 id : cm.getColumnId(i),
49307                 locked : cm.isLocked(i)
49308             };
49309         }
49310
49311         startRow = startRow || 0;
49312         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49313
49314         // records to render
49315         var rs = ds.getRange(startRow, endRow);
49316
49317         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49318     },
49319
49320     // As much as I hate to duplicate code, this was branched because FireFox really hates
49321     // [].join("") on strings. The performance difference was substantial enough to
49322     // branch this function
49323     doRender : Roo.isGecko ?
49324             function(cs, rs, ds, startRow, colCount, stripe){
49325                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49326                 // buffers
49327                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49328                 
49329                 var hasListener = this.grid.hasListener('rowclass');
49330                 var rowcfg = {};
49331                 for(var j = 0, len = rs.length; j < len; j++){
49332                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49333                     for(var i = 0; i < colCount; i++){
49334                         c = cs[i];
49335                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49336                         p.id = c.id;
49337                         p.css = p.attr = "";
49338                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49339                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49340                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49341                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49342                         }
49343                         var markup = ct.apply(p);
49344                         if(!c.locked){
49345                             cb+= markup;
49346                         }else{
49347                             lcb+= markup;
49348                         }
49349                     }
49350                     var alt = [];
49351                     if(stripe && ((rowIndex+1) % 2 == 0)){
49352                         alt.push("x-grid-row-alt")
49353                     }
49354                     if(r.dirty){
49355                         alt.push(  " x-grid-dirty-row");
49356                     }
49357                     rp.cells = lcb;
49358                     if(this.getRowClass){
49359                         alt.push(this.getRowClass(r, rowIndex));
49360                     }
49361                     if (hasListener) {
49362                         rowcfg = {
49363                              
49364                             record: r,
49365                             rowIndex : rowIndex,
49366                             rowClass : ''
49367                         }
49368                         this.grid.fireEvent('rowclass', this, rowcfg);
49369                         alt.push(rowcfg.rowClass);
49370                     }
49371                     rp.alt = alt.join(" ");
49372                     lbuf+= rt.apply(rp);
49373                     rp.cells = cb;
49374                     buf+=  rt.apply(rp);
49375                 }
49376                 return [lbuf, buf];
49377             } :
49378             function(cs, rs, ds, startRow, colCount, stripe){
49379                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49380                 // buffers
49381                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49382                 var hasListener = this.grid.hasListener('rowclass');
49383  
49384                 var rowcfg = {};
49385                 for(var j = 0, len = rs.length; j < len; j++){
49386                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49387                     for(var i = 0; i < colCount; i++){
49388                         c = cs[i];
49389                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49390                         p.id = c.id;
49391                         p.css = p.attr = "";
49392                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49393                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49394                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49395                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49396                         }
49397                         
49398                         var markup = ct.apply(p);
49399                         if(!c.locked){
49400                             cb[cb.length] = markup;
49401                         }else{
49402                             lcb[lcb.length] = markup;
49403                         }
49404                     }
49405                     var alt = [];
49406                     if(stripe && ((rowIndex+1) % 2 == 0)){
49407                         alt.push( "x-grid-row-alt");
49408                     }
49409                     if(r.dirty){
49410                         alt.push(" x-grid-dirty-row");
49411                     }
49412                     rp.cells = lcb;
49413                     if(this.getRowClass){
49414                         alt.push( this.getRowClass(r, rowIndex));
49415                     }
49416                     if (hasListener) {
49417                         rowcfg = {
49418                              
49419                             record: r,
49420                             rowIndex : rowIndex,
49421                             rowClass : ''
49422                         }
49423                         this.grid.fireEvent('rowclass', this, rowcfg);
49424                         alt.push(rowcfg.rowClass);
49425                     }
49426                     rp.alt = alt.join(" ");
49427                     rp.cells = lcb.join("");
49428                     lbuf[lbuf.length] = rt.apply(rp);
49429                     rp.cells = cb.join("");
49430                     buf[buf.length] =  rt.apply(rp);
49431                 }
49432                 return [lbuf.join(""), buf.join("")];
49433             },
49434
49435     renderBody : function(){
49436         var markup = this.renderRows();
49437         var bt = this.templates.body;
49438         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49439     },
49440
49441     /**
49442      * Refreshes the grid
49443      * @param {Boolean} headersToo
49444      */
49445     refresh : function(headersToo){
49446         this.fireEvent("beforerefresh", this);
49447         this.grid.stopEditing();
49448         var result = this.renderBody();
49449         this.lockedBody.update(result[0]);
49450         this.mainBody.update(result[1]);
49451         if(headersToo === true){
49452             this.updateHeaders();
49453             this.updateColumns();
49454             this.updateSplitters();
49455             this.updateHeaderSortState();
49456         }
49457         this.syncRowHeights();
49458         this.layout();
49459         this.fireEvent("refresh", this);
49460     },
49461
49462     handleColumnMove : function(cm, oldIndex, newIndex){
49463         this.indexMap = null;
49464         var s = this.getScrollState();
49465         this.refresh(true);
49466         this.restoreScroll(s);
49467         this.afterMove(newIndex);
49468     },
49469
49470     afterMove : function(colIndex){
49471         if(this.enableMoveAnim && Roo.enableFx){
49472             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49473         }
49474         // if multisort - fix sortOrder, and reload..
49475         if (this.grid.dataSource.multiSort) {
49476             // the we can call sort again..
49477             var dm = this.grid.dataSource;
49478             var cm = this.grid.colModel;
49479             var so = [];
49480             for(var i = 0; i < cm.config.length; i++ ) {
49481                 
49482                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49483                     continue; // dont' bother, it's not in sort list or being set.
49484                 }
49485                 
49486                 so.push(cm.config[i].dataIndex);
49487             };
49488             dm.sortOrder = so;
49489             dm.load(dm.lastOptions);
49490             
49491             
49492         }
49493         
49494     },
49495
49496     updateCell : function(dm, rowIndex, dataIndex){
49497         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49498         if(typeof colIndex == "undefined"){ // not present in grid
49499             return;
49500         }
49501         var cm = this.grid.colModel;
49502         var cell = this.getCell(rowIndex, colIndex);
49503         var cellText = this.getCellText(rowIndex, colIndex);
49504
49505         var p = {
49506             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49507             id : cm.getColumnId(colIndex),
49508             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49509         };
49510         var renderer = cm.getRenderer(colIndex);
49511         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49512         if(typeof val == "undefined" || val === "") val = "&#160;";
49513         cellText.innerHTML = val;
49514         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49515         this.syncRowHeights(rowIndex, rowIndex);
49516     },
49517
49518     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49519         var maxWidth = 0;
49520         if(this.grid.autoSizeHeaders){
49521             var h = this.getHeaderCellMeasure(colIndex);
49522             maxWidth = Math.max(maxWidth, h.scrollWidth);
49523         }
49524         var tb, index;
49525         if(this.cm.isLocked(colIndex)){
49526             tb = this.getLockedTable();
49527             index = colIndex;
49528         }else{
49529             tb = this.getBodyTable();
49530             index = colIndex - this.cm.getLockedCount();
49531         }
49532         if(tb && tb.rows){
49533             var rows = tb.rows;
49534             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49535             for(var i = 0; i < stopIndex; i++){
49536                 var cell = rows[i].childNodes[index].firstChild;
49537                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49538             }
49539         }
49540         return maxWidth + /*margin for error in IE*/ 5;
49541     },
49542     /**
49543      * Autofit a column to its content.
49544      * @param {Number} colIndex
49545      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49546      */
49547      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49548          if(this.cm.isHidden(colIndex)){
49549              return; // can't calc a hidden column
49550          }
49551         if(forceMinSize){
49552             var cid = this.cm.getColumnId(colIndex);
49553             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49554            if(this.grid.autoSizeHeaders){
49555                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49556            }
49557         }
49558         var newWidth = this.calcColumnWidth(colIndex);
49559         this.cm.setColumnWidth(colIndex,
49560             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49561         if(!suppressEvent){
49562             this.grid.fireEvent("columnresize", colIndex, newWidth);
49563         }
49564     },
49565
49566     /**
49567      * Autofits all columns to their content and then expands to fit any extra space in the grid
49568      */
49569      autoSizeColumns : function(){
49570         var cm = this.grid.colModel;
49571         var colCount = cm.getColumnCount();
49572         for(var i = 0; i < colCount; i++){
49573             this.autoSizeColumn(i, true, true);
49574         }
49575         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49576             this.fitColumns();
49577         }else{
49578             this.updateColumns();
49579             this.layout();
49580         }
49581     },
49582
49583     /**
49584      * Autofits all columns to the grid's width proportionate with their current size
49585      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49586      */
49587     fitColumns : function(reserveScrollSpace){
49588         var cm = this.grid.colModel;
49589         var colCount = cm.getColumnCount();
49590         var cols = [];
49591         var width = 0;
49592         var i, w;
49593         for (i = 0; i < colCount; i++){
49594             if(!cm.isHidden(i) && !cm.isFixed(i)){
49595                 w = cm.getColumnWidth(i);
49596                 cols.push(i);
49597                 cols.push(w);
49598                 width += w;
49599             }
49600         }
49601         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49602         if(reserveScrollSpace){
49603             avail -= 17;
49604         }
49605         var frac = (avail - cm.getTotalWidth())/width;
49606         while (cols.length){
49607             w = cols.pop();
49608             i = cols.pop();
49609             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49610         }
49611         this.updateColumns();
49612         this.layout();
49613     },
49614
49615     onRowSelect : function(rowIndex){
49616         var row = this.getRowComposite(rowIndex);
49617         row.addClass("x-grid-row-selected");
49618     },
49619
49620     onRowDeselect : function(rowIndex){
49621         var row = this.getRowComposite(rowIndex);
49622         row.removeClass("x-grid-row-selected");
49623     },
49624
49625     onCellSelect : function(row, col){
49626         var cell = this.getCell(row, col);
49627         if(cell){
49628             Roo.fly(cell).addClass("x-grid-cell-selected");
49629         }
49630     },
49631
49632     onCellDeselect : function(row, col){
49633         var cell = this.getCell(row, col);
49634         if(cell){
49635             Roo.fly(cell).removeClass("x-grid-cell-selected");
49636         }
49637     },
49638
49639     updateHeaderSortState : function(){
49640         
49641         // sort state can be single { field: xxx, direction : yyy}
49642         // or   { xxx=>ASC , yyy : DESC ..... }
49643         
49644         var mstate = {};
49645         if (!this.ds.multiSort) { 
49646             var state = this.ds.getSortState();
49647             if(!state){
49648                 return;
49649             }
49650             mstate[state.field] = state.direction;
49651             // FIXME... - this is not used here.. but might be elsewhere..
49652             this.sortState = state;
49653             
49654         } else {
49655             mstate = this.ds.sortToggle;
49656         }
49657         //remove existing sort classes..
49658         
49659         var sc = this.sortClasses;
49660         var hds = this.el.select(this.headerSelector).removeClass(sc);
49661         
49662         for(var f in mstate) {
49663         
49664             var sortColumn = this.cm.findColumnIndex(f);
49665             
49666             if(sortColumn != -1){
49667                 var sortDir = mstate[f];        
49668                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49669             }
49670         }
49671         
49672          
49673         
49674     },
49675
49676
49677     handleHeaderClick : function(g, index){
49678         if(this.headersDisabled){
49679             return;
49680         }
49681         var dm = g.dataSource, cm = g.colModel;
49682         if(!cm.isSortable(index)){
49683             return;
49684         }
49685         g.stopEditing();
49686         
49687         if (dm.multiSort) {
49688             // update the sortOrder
49689             var so = [];
49690             for(var i = 0; i < cm.config.length; i++ ) {
49691                 
49692                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49693                     continue; // dont' bother, it's not in sort list or being set.
49694                 }
49695                 
49696                 so.push(cm.config[i].dataIndex);
49697             };
49698             dm.sortOrder = so;
49699         }
49700         
49701         
49702         dm.sort(cm.getDataIndex(index));
49703     },
49704
49705
49706     destroy : function(){
49707         if(this.colMenu){
49708             this.colMenu.removeAll();
49709             Roo.menu.MenuMgr.unregister(this.colMenu);
49710             this.colMenu.getEl().remove();
49711             delete this.colMenu;
49712         }
49713         if(this.hmenu){
49714             this.hmenu.removeAll();
49715             Roo.menu.MenuMgr.unregister(this.hmenu);
49716             this.hmenu.getEl().remove();
49717             delete this.hmenu;
49718         }
49719         if(this.grid.enableColumnMove){
49720             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49721             if(dds){
49722                 for(var dd in dds){
49723                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49724                         var elid = dds[dd].dragElId;
49725                         dds[dd].unreg();
49726                         Roo.get(elid).remove();
49727                     } else if(dds[dd].config.isTarget){
49728                         dds[dd].proxyTop.remove();
49729                         dds[dd].proxyBottom.remove();
49730                         dds[dd].unreg();
49731                     }
49732                     if(Roo.dd.DDM.locationCache[dd]){
49733                         delete Roo.dd.DDM.locationCache[dd];
49734                     }
49735                 }
49736                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49737             }
49738         }
49739         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49740         this.bind(null, null);
49741         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49742     },
49743
49744     handleLockChange : function(){
49745         this.refresh(true);
49746     },
49747
49748     onDenyColumnLock : function(){
49749
49750     },
49751
49752     onDenyColumnHide : function(){
49753
49754     },
49755
49756     handleHdMenuClick : function(item){
49757         var index = this.hdCtxIndex;
49758         var cm = this.cm, ds = this.ds;
49759         switch(item.id){
49760             case "asc":
49761                 ds.sort(cm.getDataIndex(index), "ASC");
49762                 break;
49763             case "desc":
49764                 ds.sort(cm.getDataIndex(index), "DESC");
49765                 break;
49766             case "lock":
49767                 var lc = cm.getLockedCount();
49768                 if(cm.getColumnCount(true) <= lc+1){
49769                     this.onDenyColumnLock();
49770                     return;
49771                 }
49772                 if(lc != index){
49773                     cm.setLocked(index, true, true);
49774                     cm.moveColumn(index, lc);
49775                     this.grid.fireEvent("columnmove", index, lc);
49776                 }else{
49777                     cm.setLocked(index, true);
49778                 }
49779             break;
49780             case "unlock":
49781                 var lc = cm.getLockedCount();
49782                 if((lc-1) != index){
49783                     cm.setLocked(index, false, true);
49784                     cm.moveColumn(index, lc-1);
49785                     this.grid.fireEvent("columnmove", index, lc-1);
49786                 }else{
49787                     cm.setLocked(index, false);
49788                 }
49789             break;
49790             default:
49791                 index = cm.getIndexById(item.id.substr(4));
49792                 if(index != -1){
49793                     if(item.checked && cm.getColumnCount(true) <= 1){
49794                         this.onDenyColumnHide();
49795                         return false;
49796                     }
49797                     cm.setHidden(index, item.checked);
49798                 }
49799         }
49800         return true;
49801     },
49802
49803     beforeColMenuShow : function(){
49804         var cm = this.cm,  colCount = cm.getColumnCount();
49805         this.colMenu.removeAll();
49806         for(var i = 0; i < colCount; i++){
49807             this.colMenu.add(new Roo.menu.CheckItem({
49808                 id: "col-"+cm.getColumnId(i),
49809                 text: cm.getColumnHeader(i),
49810                 checked: !cm.isHidden(i),
49811                 hideOnClick:false
49812             }));
49813         }
49814     },
49815
49816     handleHdCtx : function(g, index, e){
49817         e.stopEvent();
49818         var hd = this.getHeaderCell(index);
49819         this.hdCtxIndex = index;
49820         var ms = this.hmenu.items, cm = this.cm;
49821         ms.get("asc").setDisabled(!cm.isSortable(index));
49822         ms.get("desc").setDisabled(!cm.isSortable(index));
49823         if(this.grid.enableColLock !== false){
49824             ms.get("lock").setDisabled(cm.isLocked(index));
49825             ms.get("unlock").setDisabled(!cm.isLocked(index));
49826         }
49827         this.hmenu.show(hd, "tl-bl");
49828     },
49829
49830     handleHdOver : function(e){
49831         var hd = this.findHeaderCell(e.getTarget());
49832         if(hd && !this.headersDisabled){
49833             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49834                this.fly(hd).addClass("x-grid-hd-over");
49835             }
49836         }
49837     },
49838
49839     handleHdOut : function(e){
49840         var hd = this.findHeaderCell(e.getTarget());
49841         if(hd){
49842             this.fly(hd).removeClass("x-grid-hd-over");
49843         }
49844     },
49845
49846     handleSplitDblClick : function(e, t){
49847         var i = this.getCellIndex(t);
49848         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49849             this.autoSizeColumn(i, true);
49850             this.layout();
49851         }
49852     },
49853
49854     render : function(){
49855
49856         var cm = this.cm;
49857         var colCount = cm.getColumnCount();
49858
49859         if(this.grid.monitorWindowResize === true){
49860             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49861         }
49862         var header = this.renderHeaders();
49863         var body = this.templates.body.apply({rows:""});
49864         var html = this.templates.master.apply({
49865             lockedBody: body,
49866             body: body,
49867             lockedHeader: header[0],
49868             header: header[1]
49869         });
49870
49871         //this.updateColumns();
49872
49873         this.grid.getGridEl().dom.innerHTML = html;
49874
49875         this.initElements();
49876         
49877         // a kludge to fix the random scolling effect in webkit
49878         this.el.on("scroll", function() {
49879             this.el.dom.scrollTop=0; // hopefully not recursive..
49880         },this);
49881
49882         this.scroller.on("scroll", this.handleScroll, this);
49883         this.lockedBody.on("mousewheel", this.handleWheel, this);
49884         this.mainBody.on("mousewheel", this.handleWheel, this);
49885
49886         this.mainHd.on("mouseover", this.handleHdOver, this);
49887         this.mainHd.on("mouseout", this.handleHdOut, this);
49888         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49889                 {delegate: "."+this.splitClass});
49890
49891         this.lockedHd.on("mouseover", this.handleHdOver, this);
49892         this.lockedHd.on("mouseout", this.handleHdOut, this);
49893         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49894                 {delegate: "."+this.splitClass});
49895
49896         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49897             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49898         }
49899
49900         this.updateSplitters();
49901
49902         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49903             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49904             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49905         }
49906
49907         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49908             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49909             this.hmenu.add(
49910                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49911                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49912             );
49913             if(this.grid.enableColLock !== false){
49914                 this.hmenu.add('-',
49915                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49916                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49917                 );
49918             }
49919             if(this.grid.enableColumnHide !== false){
49920
49921                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49922                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49923                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49924
49925                 this.hmenu.add('-',
49926                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49927                 );
49928             }
49929             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49930
49931             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49932         }
49933
49934         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49935             this.dd = new Roo.grid.GridDragZone(this.grid, {
49936                 ddGroup : this.grid.ddGroup || 'GridDD'
49937             });
49938         }
49939
49940         /*
49941         for(var i = 0; i < colCount; i++){
49942             if(cm.isHidden(i)){
49943                 this.hideColumn(i);
49944             }
49945             if(cm.config[i].align){
49946                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49947                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49948             }
49949         }*/
49950         
49951         this.updateHeaderSortState();
49952
49953         this.beforeInitialResize();
49954         this.layout(true);
49955
49956         // two part rendering gives faster view to the user
49957         this.renderPhase2.defer(1, this);
49958     },
49959
49960     renderPhase2 : function(){
49961         // render the rows now
49962         this.refresh();
49963         if(this.grid.autoSizeColumns){
49964             this.autoSizeColumns();
49965         }
49966     },
49967
49968     beforeInitialResize : function(){
49969
49970     },
49971
49972     onColumnSplitterMoved : function(i, w){
49973         this.userResized = true;
49974         var cm = this.grid.colModel;
49975         cm.setColumnWidth(i, w, true);
49976         var cid = cm.getColumnId(i);
49977         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49978         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49979         this.updateSplitters();
49980         this.layout();
49981         this.grid.fireEvent("columnresize", i, w);
49982     },
49983
49984     syncRowHeights : function(startIndex, endIndex){
49985         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49986             startIndex = startIndex || 0;
49987             var mrows = this.getBodyTable().rows;
49988             var lrows = this.getLockedTable().rows;
49989             var len = mrows.length-1;
49990             endIndex = Math.min(endIndex || len, len);
49991             for(var i = startIndex; i <= endIndex; i++){
49992                 var m = mrows[i], l = lrows[i];
49993                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49994                 m.style.height = l.style.height = h + "px";
49995             }
49996         }
49997     },
49998
49999     layout : function(initialRender, is2ndPass){
50000         var g = this.grid;
50001         var auto = g.autoHeight;
50002         var scrollOffset = 16;
50003         var c = g.getGridEl(), cm = this.cm,
50004                 expandCol = g.autoExpandColumn,
50005                 gv = this;
50006         //c.beginMeasure();
50007
50008         if(!c.dom.offsetWidth){ // display:none?
50009             if(initialRender){
50010                 this.lockedWrap.show();
50011                 this.mainWrap.show();
50012             }
50013             return;
50014         }
50015
50016         var hasLock = this.cm.isLocked(0);
50017
50018         var tbh = this.headerPanel.getHeight();
50019         var bbh = this.footerPanel.getHeight();
50020
50021         if(auto){
50022             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
50023             var newHeight = ch + c.getBorderWidth("tb");
50024             if(g.maxHeight){
50025                 newHeight = Math.min(g.maxHeight, newHeight);
50026             }
50027             c.setHeight(newHeight);
50028         }
50029
50030         if(g.autoWidth){
50031             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
50032         }
50033
50034         var s = this.scroller;
50035
50036         var csize = c.getSize(true);
50037
50038         this.el.setSize(csize.width, csize.height);
50039
50040         this.headerPanel.setWidth(csize.width);
50041         this.footerPanel.setWidth(csize.width);
50042
50043         var hdHeight = this.mainHd.getHeight();
50044         var vw = csize.width;
50045         var vh = csize.height - (tbh + bbh);
50046
50047         s.setSize(vw, vh);
50048
50049         var bt = this.getBodyTable();
50050         var ltWidth = hasLock ?
50051                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
50052
50053         var scrollHeight = bt.offsetHeight;
50054         var scrollWidth = ltWidth + bt.offsetWidth;
50055         var vscroll = false, hscroll = false;
50056
50057         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
50058
50059         var lw = this.lockedWrap, mw = this.mainWrap;
50060         var lb = this.lockedBody, mb = this.mainBody;
50061
50062         setTimeout(function(){
50063             var t = s.dom.offsetTop;
50064             var w = s.dom.clientWidth,
50065                 h = s.dom.clientHeight;
50066
50067             lw.setTop(t);
50068             lw.setSize(ltWidth, h);
50069
50070             mw.setLeftTop(ltWidth, t);
50071             mw.setSize(w-ltWidth, h);
50072
50073             lb.setHeight(h-hdHeight);
50074             mb.setHeight(h-hdHeight);
50075
50076             if(is2ndPass !== true && !gv.userResized && expandCol){
50077                 // high speed resize without full column calculation
50078                 
50079                 var ci = cm.getIndexById(expandCol);
50080                 if (ci < 0) {
50081                     ci = cm.findColumnIndex(expandCol);
50082                 }
50083                 ci = Math.max(0, ci); // make sure it's got at least the first col.
50084                 var expandId = cm.getColumnId(ci);
50085                 var  tw = cm.getTotalWidth(false);
50086                 var currentWidth = cm.getColumnWidth(ci);
50087                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
50088                 if(currentWidth != cw){
50089                     cm.setColumnWidth(ci, cw, true);
50090                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50091                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
50092                     gv.updateSplitters();
50093                     gv.layout(false, true);
50094                 }
50095             }
50096
50097             if(initialRender){
50098                 lw.show();
50099                 mw.show();
50100             }
50101             //c.endMeasure();
50102         }, 10);
50103     },
50104
50105     onWindowResize : function(){
50106         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
50107             return;
50108         }
50109         this.layout();
50110     },
50111
50112     appendFooter : function(parentEl){
50113         return null;
50114     },
50115
50116     sortAscText : "Sort Ascending",
50117     sortDescText : "Sort Descending",
50118     lockText : "Lock Column",
50119     unlockText : "Unlock Column",
50120     columnsText : "Columns"
50121 });
50122
50123
50124 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
50125     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
50126     this.proxy.el.addClass('x-grid3-col-dd');
50127 };
50128
50129 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
50130     handleMouseDown : function(e){
50131
50132     },
50133
50134     callHandleMouseDown : function(e){
50135         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
50136     }
50137 });
50138 /*
50139  * Based on:
50140  * Ext JS Library 1.1.1
50141  * Copyright(c) 2006-2007, Ext JS, LLC.
50142  *
50143  * Originally Released Under LGPL - original licence link has changed is not relivant.
50144  *
50145  * Fork - LGPL
50146  * <script type="text/javascript">
50147  */
50148  
50149 // private
50150 // This is a support class used internally by the Grid components
50151 Roo.grid.SplitDragZone = function(grid, hd, hd2){
50152     this.grid = grid;
50153     this.view = grid.getView();
50154     this.proxy = this.view.resizeProxy;
50155     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
50156         "gridSplitters" + this.grid.getGridEl().id, {
50157         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
50158     });
50159     this.setHandleElId(Roo.id(hd));
50160     this.setOuterHandleElId(Roo.id(hd2));
50161     this.scroll = false;
50162 };
50163 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
50164     fly: Roo.Element.fly,
50165
50166     b4StartDrag : function(x, y){
50167         this.view.headersDisabled = true;
50168         this.proxy.setHeight(this.view.mainWrap.getHeight());
50169         var w = this.cm.getColumnWidth(this.cellIndex);
50170         var minw = Math.max(w-this.grid.minColumnWidth, 0);
50171         this.resetConstraints();
50172         this.setXConstraint(minw, 1000);
50173         this.setYConstraint(0, 0);
50174         this.minX = x - minw;
50175         this.maxX = x + 1000;
50176         this.startPos = x;
50177         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50178     },
50179
50180
50181     handleMouseDown : function(e){
50182         ev = Roo.EventObject.setEvent(e);
50183         var t = this.fly(ev.getTarget());
50184         if(t.hasClass("x-grid-split")){
50185             this.cellIndex = this.view.getCellIndex(t.dom);
50186             this.split = t.dom;
50187             this.cm = this.grid.colModel;
50188             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50189                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50190             }
50191         }
50192     },
50193
50194     endDrag : function(e){
50195         this.view.headersDisabled = false;
50196         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50197         var diff = endX - this.startPos;
50198         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50199     },
50200
50201     autoOffset : function(){
50202         this.setDelta(0,0);
50203     }
50204 });/*
50205  * Based on:
50206  * Ext JS Library 1.1.1
50207  * Copyright(c) 2006-2007, Ext JS, LLC.
50208  *
50209  * Originally Released Under LGPL - original licence link has changed is not relivant.
50210  *
50211  * Fork - LGPL
50212  * <script type="text/javascript">
50213  */
50214  
50215 // private
50216 // This is a support class used internally by the Grid components
50217 Roo.grid.GridDragZone = function(grid, config){
50218     this.view = grid.getView();
50219     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50220     if(this.view.lockedBody){
50221         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50222         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50223     }
50224     this.scroll = false;
50225     this.grid = grid;
50226     this.ddel = document.createElement('div');
50227     this.ddel.className = 'x-grid-dd-wrap';
50228 };
50229
50230 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50231     ddGroup : "GridDD",
50232
50233     getDragData : function(e){
50234         var t = Roo.lib.Event.getTarget(e);
50235         var rowIndex = this.view.findRowIndex(t);
50236         if(rowIndex !== false){
50237             var sm = this.grid.selModel;
50238             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50239               //  sm.mouseDown(e, t);
50240             //}
50241             if (e.hasModifier()){
50242                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50243             }
50244             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50245         }
50246         return false;
50247     },
50248
50249     onInitDrag : function(e){
50250         var data = this.dragData;
50251         this.ddel.innerHTML = this.grid.getDragDropText();
50252         this.proxy.update(this.ddel);
50253         // fire start drag?
50254     },
50255
50256     afterRepair : function(){
50257         this.dragging = false;
50258     },
50259
50260     getRepairXY : function(e, data){
50261         return false;
50262     },
50263
50264     onEndDrag : function(data, e){
50265         // fire end drag?
50266     },
50267
50268     onValidDrop : function(dd, e, id){
50269         // fire drag drop?
50270         this.hideProxy();
50271     },
50272
50273     beforeInvalidDrop : function(e, id){
50274
50275     }
50276 });/*
50277  * Based on:
50278  * Ext JS Library 1.1.1
50279  * Copyright(c) 2006-2007, Ext JS, LLC.
50280  *
50281  * Originally Released Under LGPL - original licence link has changed is not relivant.
50282  *
50283  * Fork - LGPL
50284  * <script type="text/javascript">
50285  */
50286  
50287
50288 /**
50289  * @class Roo.grid.ColumnModel
50290  * @extends Roo.util.Observable
50291  * This is the default implementation of a ColumnModel used by the Grid. It defines
50292  * the columns in the grid.
50293  * <br>Usage:<br>
50294  <pre><code>
50295  var colModel = new Roo.grid.ColumnModel([
50296         {header: "Ticker", width: 60, sortable: true, locked: true},
50297         {header: "Company Name", width: 150, sortable: true},
50298         {header: "Market Cap.", width: 100, sortable: true},
50299         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50300         {header: "Employees", width: 100, sortable: true, resizable: false}
50301  ]);
50302  </code></pre>
50303  * <p>
50304  
50305  * The config options listed for this class are options which may appear in each
50306  * individual column definition.
50307  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50308  * @constructor
50309  * @param {Object} config An Array of column config objects. See this class's
50310  * config objects for details.
50311 */
50312 Roo.grid.ColumnModel = function(config){
50313         /**
50314      * The config passed into the constructor
50315      */
50316     this.config = config;
50317     this.lookup = {};
50318
50319     // if no id, create one
50320     // if the column does not have a dataIndex mapping,
50321     // map it to the order it is in the config
50322     for(var i = 0, len = config.length; i < len; i++){
50323         var c = config[i];
50324         if(typeof c.dataIndex == "undefined"){
50325             c.dataIndex = i;
50326         }
50327         if(typeof c.renderer == "string"){
50328             c.renderer = Roo.util.Format[c.renderer];
50329         }
50330         if(typeof c.id == "undefined"){
50331             c.id = Roo.id();
50332         }
50333         if(c.editor && c.editor.xtype){
50334             c.editor  = Roo.factory(c.editor, Roo.grid);
50335         }
50336         if(c.editor && c.editor.isFormField){
50337             c.editor = new Roo.grid.GridEditor(c.editor);
50338         }
50339         this.lookup[c.id] = c;
50340     }
50341
50342     /**
50343      * The width of columns which have no width specified (defaults to 100)
50344      * @type Number
50345      */
50346     this.defaultWidth = 100;
50347
50348     /**
50349      * Default sortable of columns which have no sortable specified (defaults to false)
50350      * @type Boolean
50351      */
50352     this.defaultSortable = false;
50353
50354     this.addEvents({
50355         /**
50356              * @event widthchange
50357              * Fires when the width of a column changes.
50358              * @param {ColumnModel} this
50359              * @param {Number} columnIndex The column index
50360              * @param {Number} newWidth The new width
50361              */
50362             "widthchange": true,
50363         /**
50364              * @event headerchange
50365              * Fires when the text of a header changes.
50366              * @param {ColumnModel} this
50367              * @param {Number} columnIndex The column index
50368              * @param {Number} newText The new header text
50369              */
50370             "headerchange": true,
50371         /**
50372              * @event hiddenchange
50373              * Fires when a column is hidden or "unhidden".
50374              * @param {ColumnModel} this
50375              * @param {Number} columnIndex The column index
50376              * @param {Boolean} hidden true if hidden, false otherwise
50377              */
50378             "hiddenchange": true,
50379             /**
50380          * @event columnmoved
50381          * Fires when a column is moved.
50382          * @param {ColumnModel} this
50383          * @param {Number} oldIndex
50384          * @param {Number} newIndex
50385          */
50386         "columnmoved" : true,
50387         /**
50388          * @event columlockchange
50389          * Fires when a column's locked state is changed
50390          * @param {ColumnModel} this
50391          * @param {Number} colIndex
50392          * @param {Boolean} locked true if locked
50393          */
50394         "columnlockchange" : true
50395     });
50396     Roo.grid.ColumnModel.superclass.constructor.call(this);
50397 };
50398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50399     /**
50400      * @cfg {String} header The header text to display in the Grid view.
50401      */
50402     /**
50403      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50404      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50405      * specified, the column's index is used as an index into the Record's data Array.
50406      */
50407     /**
50408      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50409      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50410      */
50411     /**
50412      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50413      * Defaults to the value of the {@link #defaultSortable} property.
50414      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50415      */
50416     /**
50417      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50418      */
50419     /**
50420      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50421      */
50422     /**
50423      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50424      */
50425     /**
50426      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50427      */
50428     /**
50429      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50430      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50431      * default renderer uses the raw data value.
50432      */
50433        /**
50434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50435      */
50436     /**
50437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50438      */
50439
50440     /**
50441      * Returns the id of the column at the specified index.
50442      * @param {Number} index The column index
50443      * @return {String} the id
50444      */
50445     getColumnId : function(index){
50446         return this.config[index].id;
50447     },
50448
50449     /**
50450      * Returns the column for a specified id.
50451      * @param {String} id The column id
50452      * @return {Object} the column
50453      */
50454     getColumnById : function(id){
50455         return this.lookup[id];
50456     },
50457
50458     
50459     /**
50460      * Returns the column for a specified dataIndex.
50461      * @param {String} dataIndex The column dataIndex
50462      * @return {Object|Boolean} the column or false if not found
50463      */
50464     getColumnByDataIndex: function(dataIndex){
50465         var index = this.findColumnIndex(dataIndex);
50466         return index > -1 ? this.config[index] : false;
50467     },
50468     
50469     /**
50470      * Returns the index for a specified column id.
50471      * @param {String} id The column id
50472      * @return {Number} the index, or -1 if not found
50473      */
50474     getIndexById : function(id){
50475         for(var i = 0, len = this.config.length; i < len; i++){
50476             if(this.config[i].id == id){
50477                 return i;
50478             }
50479         }
50480         return -1;
50481     },
50482     
50483     /**
50484      * Returns the index for a specified column dataIndex.
50485      * @param {String} dataIndex The column dataIndex
50486      * @return {Number} the index, or -1 if not found
50487      */
50488     
50489     findColumnIndex : function(dataIndex){
50490         for(var i = 0, len = this.config.length; i < len; i++){
50491             if(this.config[i].dataIndex == dataIndex){
50492                 return i;
50493             }
50494         }
50495         return -1;
50496     },
50497     
50498     
50499     moveColumn : function(oldIndex, newIndex){
50500         var c = this.config[oldIndex];
50501         this.config.splice(oldIndex, 1);
50502         this.config.splice(newIndex, 0, c);
50503         this.dataMap = null;
50504         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50505     },
50506
50507     isLocked : function(colIndex){
50508         return this.config[colIndex].locked === true;
50509     },
50510
50511     setLocked : function(colIndex, value, suppressEvent){
50512         if(this.isLocked(colIndex) == value){
50513             return;
50514         }
50515         this.config[colIndex].locked = value;
50516         if(!suppressEvent){
50517             this.fireEvent("columnlockchange", this, colIndex, value);
50518         }
50519     },
50520
50521     getTotalLockedWidth : function(){
50522         var totalWidth = 0;
50523         for(var i = 0; i < this.config.length; i++){
50524             if(this.isLocked(i) && !this.isHidden(i)){
50525                 this.totalWidth += this.getColumnWidth(i);
50526             }
50527         }
50528         return totalWidth;
50529     },
50530
50531     getLockedCount : function(){
50532         for(var i = 0, len = this.config.length; i < len; i++){
50533             if(!this.isLocked(i)){
50534                 return i;
50535             }
50536         }
50537     },
50538
50539     /**
50540      * Returns the number of columns.
50541      * @return {Number}
50542      */
50543     getColumnCount : function(visibleOnly){
50544         if(visibleOnly === true){
50545             var c = 0;
50546             for(var i = 0, len = this.config.length; i < len; i++){
50547                 if(!this.isHidden(i)){
50548                     c++;
50549                 }
50550             }
50551             return c;
50552         }
50553         return this.config.length;
50554     },
50555
50556     /**
50557      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50558      * @param {Function} fn
50559      * @param {Object} scope (optional)
50560      * @return {Array} result
50561      */
50562     getColumnsBy : function(fn, scope){
50563         var r = [];
50564         for(var i = 0, len = this.config.length; i < len; i++){
50565             var c = this.config[i];
50566             if(fn.call(scope||this, c, i) === true){
50567                 r[r.length] = c;
50568             }
50569         }
50570         return r;
50571     },
50572
50573     /**
50574      * Returns true if the specified column is sortable.
50575      * @param {Number} col The column index
50576      * @return {Boolean}
50577      */
50578     isSortable : function(col){
50579         if(typeof this.config[col].sortable == "undefined"){
50580             return this.defaultSortable;
50581         }
50582         return this.config[col].sortable;
50583     },
50584
50585     /**
50586      * Returns the rendering (formatting) function defined for the column.
50587      * @param {Number} col The column index.
50588      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50589      */
50590     getRenderer : function(col){
50591         if(!this.config[col].renderer){
50592             return Roo.grid.ColumnModel.defaultRenderer;
50593         }
50594         return this.config[col].renderer;
50595     },
50596
50597     /**
50598      * Sets the rendering (formatting) function for a column.
50599      * @param {Number} col The column index
50600      * @param {Function} fn The function to use to process the cell's raw data
50601      * to return HTML markup for the grid view. The render function is called with
50602      * the following parameters:<ul>
50603      * <li>Data value.</li>
50604      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50605      * <li>css A CSS style string to apply to the table cell.</li>
50606      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50607      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50608      * <li>Row index</li>
50609      * <li>Column index</li>
50610      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50611      */
50612     setRenderer : function(col, fn){
50613         this.config[col].renderer = fn;
50614     },
50615
50616     /**
50617      * Returns the width for the specified column.
50618      * @param {Number} col The column index
50619      * @return {Number}
50620      */
50621     getColumnWidth : function(col){
50622         return this.config[col].width * 1 || this.defaultWidth;
50623     },
50624
50625     /**
50626      * Sets the width for a column.
50627      * @param {Number} col The column index
50628      * @param {Number} width The new width
50629      */
50630     setColumnWidth : function(col, width, suppressEvent){
50631         this.config[col].width = width;
50632         this.totalWidth = null;
50633         if(!suppressEvent){
50634              this.fireEvent("widthchange", this, col, width);
50635         }
50636     },
50637
50638     /**
50639      * Returns the total width of all columns.
50640      * @param {Boolean} includeHidden True to include hidden column widths
50641      * @return {Number}
50642      */
50643     getTotalWidth : function(includeHidden){
50644         if(!this.totalWidth){
50645             this.totalWidth = 0;
50646             for(var i = 0, len = this.config.length; i < len; i++){
50647                 if(includeHidden || !this.isHidden(i)){
50648                     this.totalWidth += this.getColumnWidth(i);
50649                 }
50650             }
50651         }
50652         return this.totalWidth;
50653     },
50654
50655     /**
50656      * Returns the header for the specified column.
50657      * @param {Number} col The column index
50658      * @return {String}
50659      */
50660     getColumnHeader : function(col){
50661         return this.config[col].header;
50662     },
50663
50664     /**
50665      * Sets the header for a column.
50666      * @param {Number} col The column index
50667      * @param {String} header The new header
50668      */
50669     setColumnHeader : function(col, header){
50670         this.config[col].header = header;
50671         this.fireEvent("headerchange", this, col, header);
50672     },
50673
50674     /**
50675      * Returns the tooltip for the specified column.
50676      * @param {Number} col The column index
50677      * @return {String}
50678      */
50679     getColumnTooltip : function(col){
50680             return this.config[col].tooltip;
50681     },
50682     /**
50683      * Sets the tooltip for a column.
50684      * @param {Number} col The column index
50685      * @param {String} tooltip The new tooltip
50686      */
50687     setColumnTooltip : function(col, tooltip){
50688             this.config[col].tooltip = tooltip;
50689     },
50690
50691     /**
50692      * Returns the dataIndex for the specified column.
50693      * @param {Number} col The column index
50694      * @return {Number}
50695      */
50696     getDataIndex : function(col){
50697         return this.config[col].dataIndex;
50698     },
50699
50700     /**
50701      * Sets the dataIndex for a column.
50702      * @param {Number} col The column index
50703      * @param {Number} dataIndex The new dataIndex
50704      */
50705     setDataIndex : function(col, dataIndex){
50706         this.config[col].dataIndex = dataIndex;
50707     },
50708
50709     
50710     
50711     /**
50712      * Returns true if the cell is editable.
50713      * @param {Number} colIndex The column index
50714      * @param {Number} rowIndex The row index
50715      * @return {Boolean}
50716      */
50717     isCellEditable : function(colIndex, rowIndex){
50718         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50719     },
50720
50721     /**
50722      * Returns the editor defined for the cell/column.
50723      * return false or null to disable editing.
50724      * @param {Number} colIndex The column index
50725      * @param {Number} rowIndex The row index
50726      * @return {Object}
50727      */
50728     getCellEditor : function(colIndex, rowIndex){
50729         return this.config[colIndex].editor;
50730     },
50731
50732     /**
50733      * Sets if a column is editable.
50734      * @param {Number} col The column index
50735      * @param {Boolean} editable True if the column is editable
50736      */
50737     setEditable : function(col, editable){
50738         this.config[col].editable = editable;
50739     },
50740
50741
50742     /**
50743      * Returns true if the column is hidden.
50744      * @param {Number} colIndex The column index
50745      * @return {Boolean}
50746      */
50747     isHidden : function(colIndex){
50748         return this.config[colIndex].hidden;
50749     },
50750
50751
50752     /**
50753      * Returns true if the column width cannot be changed
50754      */
50755     isFixed : function(colIndex){
50756         return this.config[colIndex].fixed;
50757     },
50758
50759     /**
50760      * Returns true if the column can be resized
50761      * @return {Boolean}
50762      */
50763     isResizable : function(colIndex){
50764         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50765     },
50766     /**
50767      * Sets if a column is hidden.
50768      * @param {Number} colIndex The column index
50769      * @param {Boolean} hidden True if the column is hidden
50770      */
50771     setHidden : function(colIndex, hidden){
50772         this.config[colIndex].hidden = hidden;
50773         this.totalWidth = null;
50774         this.fireEvent("hiddenchange", this, colIndex, hidden);
50775     },
50776
50777     /**
50778      * Sets the editor for a column.
50779      * @param {Number} col The column index
50780      * @param {Object} editor The editor object
50781      */
50782     setEditor : function(col, editor){
50783         this.config[col].editor = editor;
50784     }
50785 });
50786
50787 Roo.grid.ColumnModel.defaultRenderer = function(value){
50788         if(typeof value == "string" && value.length < 1){
50789             return "&#160;";
50790         }
50791         return value;
50792 };
50793
50794 // Alias for backwards compatibility
50795 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50796 /*
50797  * Based on:
50798  * Ext JS Library 1.1.1
50799  * Copyright(c) 2006-2007, Ext JS, LLC.
50800  *
50801  * Originally Released Under LGPL - original licence link has changed is not relivant.
50802  *
50803  * Fork - LGPL
50804  * <script type="text/javascript">
50805  */
50806
50807 /**
50808  * @class Roo.grid.AbstractSelectionModel
50809  * @extends Roo.util.Observable
50810  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50811  * implemented by descendant classes.  This class should not be directly instantiated.
50812  * @constructor
50813  */
50814 Roo.grid.AbstractSelectionModel = function(){
50815     this.locked = false;
50816     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50817 };
50818
50819 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50820     /** @ignore Called by the grid automatically. Do not call directly. */
50821     init : function(grid){
50822         this.grid = grid;
50823         this.initEvents();
50824     },
50825
50826     /**
50827      * Locks the selections.
50828      */
50829     lock : function(){
50830         this.locked = true;
50831     },
50832
50833     /**
50834      * Unlocks the selections.
50835      */
50836     unlock : function(){
50837         this.locked = false;
50838     },
50839
50840     /**
50841      * Returns true if the selections are locked.
50842      * @return {Boolean}
50843      */
50844     isLocked : function(){
50845         return this.locked;
50846     }
50847 });/*
50848  * Based on:
50849  * Ext JS Library 1.1.1
50850  * Copyright(c) 2006-2007, Ext JS, LLC.
50851  *
50852  * Originally Released Under LGPL - original licence link has changed is not relivant.
50853  *
50854  * Fork - LGPL
50855  * <script type="text/javascript">
50856  */
50857 /**
50858  * @extends Roo.grid.AbstractSelectionModel
50859  * @class Roo.grid.RowSelectionModel
50860  * The default SelectionModel used by {@link Roo.grid.Grid}.
50861  * It supports multiple selections and keyboard selection/navigation. 
50862  * @constructor
50863  * @param {Object} config
50864  */
50865 Roo.grid.RowSelectionModel = function(config){
50866     Roo.apply(this, config);
50867     this.selections = new Roo.util.MixedCollection(false, function(o){
50868         return o.id;
50869     });
50870
50871     this.last = false;
50872     this.lastActive = false;
50873
50874     this.addEvents({
50875         /**
50876              * @event selectionchange
50877              * Fires when the selection changes
50878              * @param {SelectionModel} this
50879              */
50880             "selectionchange" : true,
50881         /**
50882              * @event afterselectionchange
50883              * Fires after the selection changes (eg. by key press or clicking)
50884              * @param {SelectionModel} this
50885              */
50886             "afterselectionchange" : true,
50887         /**
50888              * @event beforerowselect
50889              * Fires when a row is selected being selected, return false to cancel.
50890              * @param {SelectionModel} this
50891              * @param {Number} rowIndex The selected index
50892              * @param {Boolean} keepExisting False if other selections will be cleared
50893              */
50894             "beforerowselect" : true,
50895         /**
50896              * @event rowselect
50897              * Fires when a row is selected.
50898              * @param {SelectionModel} this
50899              * @param {Number} rowIndex The selected index
50900              * @param {Roo.data.Record} r The record
50901              */
50902             "rowselect" : true,
50903         /**
50904              * @event rowdeselect
50905              * Fires when a row is deselected.
50906              * @param {SelectionModel} this
50907              * @param {Number} rowIndex The selected index
50908              */
50909         "rowdeselect" : true
50910     });
50911     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50912     this.locked = false;
50913 };
50914
50915 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50916     /**
50917      * @cfg {Boolean} singleSelect
50918      * True to allow selection of only one row at a time (defaults to false)
50919      */
50920     singleSelect : false,
50921
50922     // private
50923     initEvents : function(){
50924
50925         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50926             this.grid.on("mousedown", this.handleMouseDown, this);
50927         }else{ // allow click to work like normal
50928             this.grid.on("rowclick", this.handleDragableRowClick, this);
50929         }
50930
50931         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50932             "up" : function(e){
50933                 if(!e.shiftKey){
50934                     this.selectPrevious(e.shiftKey);
50935                 }else if(this.last !== false && this.lastActive !== false){
50936                     var last = this.last;
50937                     this.selectRange(this.last,  this.lastActive-1);
50938                     this.grid.getView().focusRow(this.lastActive);
50939                     if(last !== false){
50940                         this.last = last;
50941                     }
50942                 }else{
50943                     this.selectFirstRow();
50944                 }
50945                 this.fireEvent("afterselectionchange", this);
50946             },
50947             "down" : function(e){
50948                 if(!e.shiftKey){
50949                     this.selectNext(e.shiftKey);
50950                 }else if(this.last !== false && this.lastActive !== false){
50951                     var last = this.last;
50952                     this.selectRange(this.last,  this.lastActive+1);
50953                     this.grid.getView().focusRow(this.lastActive);
50954                     if(last !== false){
50955                         this.last = last;
50956                     }
50957                 }else{
50958                     this.selectFirstRow();
50959                 }
50960                 this.fireEvent("afterselectionchange", this);
50961             },
50962             scope: this
50963         });
50964
50965         var view = this.grid.view;
50966         view.on("refresh", this.onRefresh, this);
50967         view.on("rowupdated", this.onRowUpdated, this);
50968         view.on("rowremoved", this.onRemove, this);
50969     },
50970
50971     // private
50972     onRefresh : function(){
50973         var ds = this.grid.dataSource, i, v = this.grid.view;
50974         var s = this.selections;
50975         s.each(function(r){
50976             if((i = ds.indexOfId(r.id)) != -1){
50977                 v.onRowSelect(i);
50978             }else{
50979                 s.remove(r);
50980             }
50981         });
50982     },
50983
50984     // private
50985     onRemove : function(v, index, r){
50986         this.selections.remove(r);
50987     },
50988
50989     // private
50990     onRowUpdated : function(v, index, r){
50991         if(this.isSelected(r)){
50992             v.onRowSelect(index);
50993         }
50994     },
50995
50996     /**
50997      * Select records.
50998      * @param {Array} records The records to select
50999      * @param {Boolean} keepExisting (optional) True to keep existing selections
51000      */
51001     selectRecords : function(records, keepExisting){
51002         if(!keepExisting){
51003             this.clearSelections();
51004         }
51005         var ds = this.grid.dataSource;
51006         for(var i = 0, len = records.length; i < len; i++){
51007             this.selectRow(ds.indexOf(records[i]), true);
51008         }
51009     },
51010
51011     /**
51012      * Gets the number of selected rows.
51013      * @return {Number}
51014      */
51015     getCount : function(){
51016         return this.selections.length;
51017     },
51018
51019     /**
51020      * Selects the first row in the grid.
51021      */
51022     selectFirstRow : function(){
51023         this.selectRow(0);
51024     },
51025
51026     /**
51027      * Select the last row.
51028      * @param {Boolean} keepExisting (optional) True to keep existing selections
51029      */
51030     selectLastRow : function(keepExisting){
51031         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
51032     },
51033
51034     /**
51035      * Selects the row immediately following the last selected row.
51036      * @param {Boolean} keepExisting (optional) True to keep existing selections
51037      */
51038     selectNext : function(keepExisting){
51039         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
51040             this.selectRow(this.last+1, keepExisting);
51041             this.grid.getView().focusRow(this.last);
51042         }
51043     },
51044
51045     /**
51046      * Selects the row that precedes the last selected row.
51047      * @param {Boolean} keepExisting (optional) True to keep existing selections
51048      */
51049     selectPrevious : function(keepExisting){
51050         if(this.last){
51051             this.selectRow(this.last-1, keepExisting);
51052             this.grid.getView().focusRow(this.last);
51053         }
51054     },
51055
51056     /**
51057      * Returns the selected records
51058      * @return {Array} Array of selected records
51059      */
51060     getSelections : function(){
51061         return [].concat(this.selections.items);
51062     },
51063
51064     /**
51065      * Returns the first selected record.
51066      * @return {Record}
51067      */
51068     getSelected : function(){
51069         return this.selections.itemAt(0);
51070     },
51071
51072
51073     /**
51074      * Clears all selections.
51075      */
51076     clearSelections : function(fast){
51077         if(this.locked) return;
51078         if(fast !== true){
51079             var ds = this.grid.dataSource;
51080             var s = this.selections;
51081             s.each(function(r){
51082                 this.deselectRow(ds.indexOfId(r.id));
51083             }, this);
51084             s.clear();
51085         }else{
51086             this.selections.clear();
51087         }
51088         this.last = false;
51089     },
51090
51091
51092     /**
51093      * Selects all rows.
51094      */
51095     selectAll : function(){
51096         if(this.locked) return;
51097         this.selections.clear();
51098         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
51099             this.selectRow(i, true);
51100         }
51101     },
51102
51103     /**
51104      * Returns True if there is a selection.
51105      * @return {Boolean}
51106      */
51107     hasSelection : function(){
51108         return this.selections.length > 0;
51109     },
51110
51111     /**
51112      * Returns True if the specified row is selected.
51113      * @param {Number/Record} record The record or index of the record to check
51114      * @return {Boolean}
51115      */
51116     isSelected : function(index){
51117         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
51118         return (r && this.selections.key(r.id) ? true : false);
51119     },
51120
51121     /**
51122      * Returns True if the specified record id is selected.
51123      * @param {String} id The id of record to check
51124      * @return {Boolean}
51125      */
51126     isIdSelected : function(id){
51127         return (this.selections.key(id) ? true : false);
51128     },
51129
51130     // private
51131     handleMouseDown : function(e, t){
51132         var view = this.grid.getView(), rowIndex;
51133         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
51134             return;
51135         };
51136         if(e.shiftKey && this.last !== false){
51137             var last = this.last;
51138             this.selectRange(last, rowIndex, e.ctrlKey);
51139             this.last = last; // reset the last
51140             view.focusRow(rowIndex);
51141         }else{
51142             var isSelected = this.isSelected(rowIndex);
51143             if(e.button !== 0 && isSelected){
51144                 view.focusRow(rowIndex);
51145             }else if(e.ctrlKey && isSelected){
51146                 this.deselectRow(rowIndex);
51147             }else if(!isSelected){
51148                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
51149                 view.focusRow(rowIndex);
51150             }
51151         }
51152         this.fireEvent("afterselectionchange", this);
51153     },
51154     // private
51155     handleDragableRowClick :  function(grid, rowIndex, e) 
51156     {
51157         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
51158             this.selectRow(rowIndex, false);
51159             grid.view.focusRow(rowIndex);
51160              this.fireEvent("afterselectionchange", this);
51161         }
51162     },
51163     
51164     /**
51165      * Selects multiple rows.
51166      * @param {Array} rows Array of the indexes of the row to select
51167      * @param {Boolean} keepExisting (optional) True to keep existing selections
51168      */
51169     selectRows : function(rows, keepExisting){
51170         if(!keepExisting){
51171             this.clearSelections();
51172         }
51173         for(var i = 0, len = rows.length; i < len; i++){
51174             this.selectRow(rows[i], true);
51175         }
51176     },
51177
51178     /**
51179      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51180      * @param {Number} startRow The index of the first row in the range
51181      * @param {Number} endRow The index of the last row in the range
51182      * @param {Boolean} keepExisting (optional) True to retain existing selections
51183      */
51184     selectRange : function(startRow, endRow, keepExisting){
51185         if(this.locked) return;
51186         if(!keepExisting){
51187             this.clearSelections();
51188         }
51189         if(startRow <= endRow){
51190             for(var i = startRow; i <= endRow; i++){
51191                 this.selectRow(i, true);
51192             }
51193         }else{
51194             for(var i = startRow; i >= endRow; i--){
51195                 this.selectRow(i, true);
51196             }
51197         }
51198     },
51199
51200     /**
51201      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51202      * @param {Number} startRow The index of the first row in the range
51203      * @param {Number} endRow The index of the last row in the range
51204      */
51205     deselectRange : function(startRow, endRow, preventViewNotify){
51206         if(this.locked) return;
51207         for(var i = startRow; i <= endRow; i++){
51208             this.deselectRow(i, preventViewNotify);
51209         }
51210     },
51211
51212     /**
51213      * Selects a row.
51214      * @param {Number} row The index of the row to select
51215      * @param {Boolean} keepExisting (optional) True to keep existing selections
51216      */
51217     selectRow : function(index, keepExisting, preventViewNotify){
51218         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51219         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51220             if(!keepExisting || this.singleSelect){
51221                 this.clearSelections();
51222             }
51223             var r = this.grid.dataSource.getAt(index);
51224             this.selections.add(r);
51225             this.last = this.lastActive = index;
51226             if(!preventViewNotify){
51227                 this.grid.getView().onRowSelect(index);
51228             }
51229             this.fireEvent("rowselect", this, index, r);
51230             this.fireEvent("selectionchange", this);
51231         }
51232     },
51233
51234     /**
51235      * Deselects a row.
51236      * @param {Number} row The index of the row to deselect
51237      */
51238     deselectRow : function(index, preventViewNotify){
51239         if(this.locked) return;
51240         if(this.last == index){
51241             this.last = false;
51242         }
51243         if(this.lastActive == index){
51244             this.lastActive = false;
51245         }
51246         var r = this.grid.dataSource.getAt(index);
51247         this.selections.remove(r);
51248         if(!preventViewNotify){
51249             this.grid.getView().onRowDeselect(index);
51250         }
51251         this.fireEvent("rowdeselect", this, index);
51252         this.fireEvent("selectionchange", this);
51253     },
51254
51255     // private
51256     restoreLast : function(){
51257         if(this._last){
51258             this.last = this._last;
51259         }
51260     },
51261
51262     // private
51263     acceptsNav : function(row, col, cm){
51264         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51265     },
51266
51267     // private
51268     onEditorKey : function(field, e){
51269         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51270         if(k == e.TAB){
51271             e.stopEvent();
51272             ed.completeEdit();
51273             if(e.shiftKey){
51274                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51275             }else{
51276                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51277             }
51278         }else if(k == e.ENTER && !e.ctrlKey){
51279             e.stopEvent();
51280             ed.completeEdit();
51281             if(e.shiftKey){
51282                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51283             }else{
51284                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51285             }
51286         }else if(k == e.ESC){
51287             ed.cancelEdit();
51288         }
51289         if(newCell){
51290             g.startEditing(newCell[0], newCell[1]);
51291         }
51292     }
51293 });/*
51294  * Based on:
51295  * Ext JS Library 1.1.1
51296  * Copyright(c) 2006-2007, Ext JS, LLC.
51297  *
51298  * Originally Released Under LGPL - original licence link has changed is not relivant.
51299  *
51300  * Fork - LGPL
51301  * <script type="text/javascript">
51302  */
51303 /**
51304  * @class Roo.grid.CellSelectionModel
51305  * @extends Roo.grid.AbstractSelectionModel
51306  * This class provides the basic implementation for cell selection in a grid.
51307  * @constructor
51308  * @param {Object} config The object containing the configuration of this model.
51309  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
51310  */
51311 Roo.grid.CellSelectionModel = function(config){
51312     Roo.apply(this, config);
51313
51314     this.selection = null;
51315
51316     this.addEvents({
51317         /**
51318              * @event beforerowselect
51319              * Fires before a cell is selected.
51320              * @param {SelectionModel} this
51321              * @param {Number} rowIndex The selected row index
51322              * @param {Number} colIndex The selected cell index
51323              */
51324             "beforecellselect" : true,
51325         /**
51326              * @event cellselect
51327              * Fires when a cell is selected.
51328              * @param {SelectionModel} this
51329              * @param {Number} rowIndex The selected row index
51330              * @param {Number} colIndex The selected cell index
51331              */
51332             "cellselect" : true,
51333         /**
51334              * @event selectionchange
51335              * Fires when the active selection changes.
51336              * @param {SelectionModel} this
51337              * @param {Object} selection null for no selection or an object (o) with two properties
51338                 <ul>
51339                 <li>o.record: the record object for the row the selection is in</li>
51340                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51341                 </ul>
51342              */
51343             "selectionchange" : true,
51344         /**
51345              * @event tabend
51346              * Fires when the tab (or enter) was pressed on the last editable cell
51347              * You can use this to trigger add new row.
51348              * @param {SelectionModel} this
51349              */
51350             "tabend" : true
51351     });
51352     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51353 };
51354
51355 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51356     
51357     enter_is_tab: false,
51358
51359     /** @ignore */
51360     initEvents : function(){
51361         this.grid.on("mousedown", this.handleMouseDown, this);
51362         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51363         var view = this.grid.view;
51364         view.on("refresh", this.onViewChange, this);
51365         view.on("rowupdated", this.onRowUpdated, this);
51366         view.on("beforerowremoved", this.clearSelections, this);
51367         view.on("beforerowsinserted", this.clearSelections, this);
51368         if(this.grid.isEditor){
51369             this.grid.on("beforeedit", this.beforeEdit,  this);
51370         }
51371     },
51372
51373         //private
51374     beforeEdit : function(e){
51375         this.select(e.row, e.column, false, true, e.record);
51376     },
51377
51378         //private
51379     onRowUpdated : function(v, index, r){
51380         if(this.selection && this.selection.record == r){
51381             v.onCellSelect(index, this.selection.cell[1]);
51382         }
51383     },
51384
51385         //private
51386     onViewChange : function(){
51387         this.clearSelections(true);
51388     },
51389
51390         /**
51391          * Returns the currently selected cell,.
51392          * @return {Array} The selected cell (row, column) or null if none selected.
51393          */
51394     getSelectedCell : function(){
51395         return this.selection ? this.selection.cell : null;
51396     },
51397
51398     /**
51399      * Clears all selections.
51400      * @param {Boolean} true to prevent the gridview from being notified about the change.
51401      */
51402     clearSelections : function(preventNotify){
51403         var s = this.selection;
51404         if(s){
51405             if(preventNotify !== true){
51406                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51407             }
51408             this.selection = null;
51409             this.fireEvent("selectionchange", this, null);
51410         }
51411     },
51412
51413     /**
51414      * Returns true if there is a selection.
51415      * @return {Boolean}
51416      */
51417     hasSelection : function(){
51418         return this.selection ? true : false;
51419     },
51420
51421     /** @ignore */
51422     handleMouseDown : function(e, t){
51423         var v = this.grid.getView();
51424         if(this.isLocked()){
51425             return;
51426         };
51427         var row = v.findRowIndex(t);
51428         var cell = v.findCellIndex(t);
51429         if(row !== false && cell !== false){
51430             this.select(row, cell);
51431         }
51432     },
51433
51434     /**
51435      * Selects a cell.
51436      * @param {Number} rowIndex
51437      * @param {Number} collIndex
51438      */
51439     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51440         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51441             this.clearSelections();
51442             r = r || this.grid.dataSource.getAt(rowIndex);
51443             this.selection = {
51444                 record : r,
51445                 cell : [rowIndex, colIndex]
51446             };
51447             if(!preventViewNotify){
51448                 var v = this.grid.getView();
51449                 v.onCellSelect(rowIndex, colIndex);
51450                 if(preventFocus !== true){
51451                     v.focusCell(rowIndex, colIndex);
51452                 }
51453             }
51454             this.fireEvent("cellselect", this, rowIndex, colIndex);
51455             this.fireEvent("selectionchange", this, this.selection);
51456         }
51457     },
51458
51459         //private
51460     isSelectable : function(rowIndex, colIndex, cm){
51461         return !cm.isHidden(colIndex);
51462     },
51463
51464     /** @ignore */
51465     handleKeyDown : function(e){
51466         //Roo.log('Cell Sel Model handleKeyDown');
51467         if(!e.isNavKeyPress()){
51468             return;
51469         }
51470         var g = this.grid, s = this.selection;
51471         if(!s){
51472             e.stopEvent();
51473             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51474             if(cell){
51475                 this.select(cell[0], cell[1]);
51476             }
51477             return;
51478         }
51479         var sm = this;
51480         var walk = function(row, col, step){
51481             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51482         };
51483         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51484         var newCell;
51485
51486       
51487
51488         switch(k){
51489             case e.TAB:
51490                 // handled by onEditorKey
51491                 if (g.isEditor && g.editing) {
51492                     return;
51493                 }
51494                 if(e.shiftKey) {
51495                     newCell = walk(r, c-1, -1);
51496                 } else {
51497                     newCell = walk(r, c+1, 1);
51498                 }
51499                 break;
51500             
51501             case e.DOWN:
51502                newCell = walk(r+1, c, 1);
51503                 break;
51504             
51505             case e.UP:
51506                 newCell = walk(r-1, c, -1);
51507                 break;
51508             
51509             case e.RIGHT:
51510                 newCell = walk(r, c+1, 1);
51511                 break;
51512             
51513             case e.LEFT:
51514                 newCell = walk(r, c-1, -1);
51515                 break;
51516             
51517             case e.ENTER:
51518                 
51519                 if(g.isEditor && !g.editing){
51520                    g.startEditing(r, c);
51521                    e.stopEvent();
51522                    return;
51523                 }
51524                 
51525                 
51526              break;
51527         };
51528         if(newCell){
51529             this.select(newCell[0], newCell[1]);
51530             e.stopEvent();
51531             
51532         }
51533     },
51534
51535     acceptsNav : function(row, col, cm){
51536         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51537     },
51538     /**
51539      * Selects a cell.
51540      * @param {Number} field (not used) - as it's normally used as a listener
51541      * @param {Number} e - event - fake it by using
51542      *
51543      * var e = Roo.EventObjectImpl.prototype;
51544      * e.keyCode = e.TAB
51545      *
51546      * 
51547      */
51548     onEditorKey : function(field, e){
51549         
51550         var k = e.getKey(),
51551             newCell,
51552             g = this.grid,
51553             ed = g.activeEditor,
51554             forward = false;
51555         ///Roo.log('onEditorKey' + k);
51556         
51557         
51558         if (this.enter_is_tab && k == e.ENTER) {
51559             k = e.TAB;
51560         }
51561         
51562         if(k == e.TAB){
51563             if(e.shiftKey){
51564                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51565             }else{
51566                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51567                 forward = true;
51568             }
51569             
51570             e.stopEvent();
51571             
51572         }else if(k == e.ENTER &&  !e.ctrlKey){
51573             ed.completeEdit();
51574             e.stopEvent();
51575             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51576         }else if(k == e.ESC){
51577             ed.cancelEdit();
51578         }
51579         
51580         
51581         if(newCell){
51582             //Roo.log('next cell after edit');
51583             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51584         } else if (forward) {
51585             // tabbed past last
51586             this.fireEvent.defer(100, this, ['tabend',this]);
51587         }
51588     }
51589 });/*
51590  * Based on:
51591  * Ext JS Library 1.1.1
51592  * Copyright(c) 2006-2007, Ext JS, LLC.
51593  *
51594  * Originally Released Under LGPL - original licence link has changed is not relivant.
51595  *
51596  * Fork - LGPL
51597  * <script type="text/javascript">
51598  */
51599  
51600 /**
51601  * @class Roo.grid.EditorGrid
51602  * @extends Roo.grid.Grid
51603  * Class for creating and editable grid.
51604  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51605  * The container MUST have some type of size defined for the grid to fill. The container will be 
51606  * automatically set to position relative if it isn't already.
51607  * @param {Object} dataSource The data model to bind to
51608  * @param {Object} colModel The column model with info about this grid's columns
51609  */
51610 Roo.grid.EditorGrid = function(container, config){
51611     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51612     this.getGridEl().addClass("xedit-grid");
51613
51614     if(!this.selModel){
51615         this.selModel = new Roo.grid.CellSelectionModel();
51616     }
51617
51618     this.activeEditor = null;
51619
51620         this.addEvents({
51621             /**
51622              * @event beforeedit
51623              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51624              * <ul style="padding:5px;padding-left:16px;">
51625              * <li>grid - This grid</li>
51626              * <li>record - The record being edited</li>
51627              * <li>field - The field name being edited</li>
51628              * <li>value - The value for the field being edited.</li>
51629              * <li>row - The grid row index</li>
51630              * <li>column - The grid column index</li>
51631              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51632              * </ul>
51633              * @param {Object} e An edit event (see above for description)
51634              */
51635             "beforeedit" : true,
51636             /**
51637              * @event afteredit
51638              * Fires after a cell is edited. <br />
51639              * <ul style="padding:5px;padding-left:16px;">
51640              * <li>grid - This grid</li>
51641              * <li>record - The record being edited</li>
51642              * <li>field - The field name being edited</li>
51643              * <li>value - The value being set</li>
51644              * <li>originalValue - The original value for the field, before the edit.</li>
51645              * <li>row - The grid row index</li>
51646              * <li>column - The grid column index</li>
51647              * </ul>
51648              * @param {Object} e An edit event (see above for description)
51649              */
51650             "afteredit" : true,
51651             /**
51652              * @event validateedit
51653              * Fires after a cell is edited, but before the value is set in the record. 
51654          * You can use this to modify the value being set in the field, Return false
51655              * to cancel the change. The edit event object has the following properties <br />
51656              * <ul style="padding:5px;padding-left:16px;">
51657          * <li>editor - This editor</li>
51658              * <li>grid - This grid</li>
51659              * <li>record - The record being edited</li>
51660              * <li>field - The field name being edited</li>
51661              * <li>value - The value being set</li>
51662              * <li>originalValue - The original value for the field, before the edit.</li>
51663              * <li>row - The grid row index</li>
51664              * <li>column - The grid column index</li>
51665              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51666              * </ul>
51667              * @param {Object} e An edit event (see above for description)
51668              */
51669             "validateedit" : true
51670         });
51671     this.on("bodyscroll", this.stopEditing,  this);
51672     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51673 };
51674
51675 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51676     /**
51677      * @cfg {Number} clicksToEdit
51678      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51679      */
51680     clicksToEdit: 2,
51681
51682     // private
51683     isEditor : true,
51684     // private
51685     trackMouseOver: false, // causes very odd FF errors
51686
51687     onCellDblClick : function(g, row, col){
51688         this.startEditing(row, col);
51689     },
51690
51691     onEditComplete : function(ed, value, startValue){
51692         this.editing = false;
51693         this.activeEditor = null;
51694         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51695         var r = ed.record;
51696         var field = this.colModel.getDataIndex(ed.col);
51697         var e = {
51698             grid: this,
51699             record: r,
51700             field: field,
51701             originalValue: startValue,
51702             value: value,
51703             row: ed.row,
51704             column: ed.col,
51705             cancel:false,
51706             editor: ed
51707         };
51708         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51709         cell.show();
51710           
51711         if(String(value) !== String(startValue)){
51712             
51713             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51714                 r.set(field, e.value);
51715                 // if we are dealing with a combo box..
51716                 // then we also set the 'name' colum to be the displayField
51717                 if (ed.field.displayField && ed.field.name) {
51718                     r.set(ed.field.name, ed.field.el.dom.value);
51719                 }
51720                 
51721                 delete e.cancel; //?? why!!!
51722                 this.fireEvent("afteredit", e);
51723             }
51724         } else {
51725             this.fireEvent("afteredit", e); // always fire it!
51726         }
51727         this.view.focusCell(ed.row, ed.col);
51728     },
51729
51730     /**
51731      * Starts editing the specified for the specified row/column
51732      * @param {Number} rowIndex
51733      * @param {Number} colIndex
51734      */
51735     startEditing : function(row, col){
51736         this.stopEditing();
51737         if(this.colModel.isCellEditable(col, row)){
51738             this.view.ensureVisible(row, col, true);
51739           
51740             var r = this.dataSource.getAt(row);
51741             var field = this.colModel.getDataIndex(col);
51742             var cell = Roo.get(this.view.getCell(row,col));
51743             var e = {
51744                 grid: this,
51745                 record: r,
51746                 field: field,
51747                 value: r.data[field],
51748                 row: row,
51749                 column: col,
51750                 cancel:false 
51751             };
51752             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51753                 this.editing = true;
51754                 var ed = this.colModel.getCellEditor(col, row);
51755                 
51756                 if (!ed) {
51757                     return;
51758                 }
51759                 if(!ed.rendered){
51760                     ed.render(ed.parentEl || document.body);
51761                 }
51762                 ed.field.reset();
51763                
51764                 cell.hide();
51765                 
51766                 (function(){ // complex but required for focus issues in safari, ie and opera
51767                     ed.row = row;
51768                     ed.col = col;
51769                     ed.record = r;
51770                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51771                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51772                     this.activeEditor = ed;
51773                     var v = r.data[field];
51774                     ed.startEdit(this.view.getCell(row, col), v);
51775                     // combo's with 'displayField and name set
51776                     if (ed.field.displayField && ed.field.name) {
51777                         ed.field.el.dom.value = r.data[ed.field.name];
51778                     }
51779                     
51780                     
51781                 }).defer(50, this);
51782             }
51783         }
51784     },
51785         
51786     /**
51787      * Stops any active editing
51788      */
51789     stopEditing : function(){
51790         if(this.activeEditor){
51791             this.activeEditor.completeEdit();
51792         }
51793         this.activeEditor = null;
51794     }
51795 });/*
51796  * Based on:
51797  * Ext JS Library 1.1.1
51798  * Copyright(c) 2006-2007, Ext JS, LLC.
51799  *
51800  * Originally Released Under LGPL - original licence link has changed is not relivant.
51801  *
51802  * Fork - LGPL
51803  * <script type="text/javascript">
51804  */
51805
51806 // private - not really -- you end up using it !
51807 // This is a support class used internally by the Grid components
51808
51809 /**
51810  * @class Roo.grid.GridEditor
51811  * @extends Roo.Editor
51812  * Class for creating and editable grid elements.
51813  * @param {Object} config any settings (must include field)
51814  */
51815 Roo.grid.GridEditor = function(field, config){
51816     if (!config && field.field) {
51817         config = field;
51818         field = Roo.factory(config.field, Roo.form);
51819     }
51820     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51821     field.monitorTab = false;
51822 };
51823
51824 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51825     
51826     /**
51827      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51828      */
51829     
51830     alignment: "tl-tl",
51831     autoSize: "width",
51832     hideEl : false,
51833     cls: "x-small-editor x-grid-editor",
51834     shim:false,
51835     shadow:"frame"
51836 });/*
51837  * Based on:
51838  * Ext JS Library 1.1.1
51839  * Copyright(c) 2006-2007, Ext JS, LLC.
51840  *
51841  * Originally Released Under LGPL - original licence link has changed is not relivant.
51842  *
51843  * Fork - LGPL
51844  * <script type="text/javascript">
51845  */
51846   
51847
51848   
51849 Roo.grid.PropertyRecord = Roo.data.Record.create([
51850     {name:'name',type:'string'},  'value'
51851 ]);
51852
51853
51854 Roo.grid.PropertyStore = function(grid, source){
51855     this.grid = grid;
51856     this.store = new Roo.data.Store({
51857         recordType : Roo.grid.PropertyRecord
51858     });
51859     this.store.on('update', this.onUpdate,  this);
51860     if(source){
51861         this.setSource(source);
51862     }
51863     Roo.grid.PropertyStore.superclass.constructor.call(this);
51864 };
51865
51866
51867
51868 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51869     setSource : function(o){
51870         this.source = o;
51871         this.store.removeAll();
51872         var data = [];
51873         for(var k in o){
51874             if(this.isEditableValue(o[k])){
51875                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51876             }
51877         }
51878         this.store.loadRecords({records: data}, {}, true);
51879     },
51880
51881     onUpdate : function(ds, record, type){
51882         if(type == Roo.data.Record.EDIT){
51883             var v = record.data['value'];
51884             var oldValue = record.modified['value'];
51885             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51886                 this.source[record.id] = v;
51887                 record.commit();
51888                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51889             }else{
51890                 record.reject();
51891             }
51892         }
51893     },
51894
51895     getProperty : function(row){
51896        return this.store.getAt(row);
51897     },
51898
51899     isEditableValue: function(val){
51900         if(val && val instanceof Date){
51901             return true;
51902         }else if(typeof val == 'object' || typeof val == 'function'){
51903             return false;
51904         }
51905         return true;
51906     },
51907
51908     setValue : function(prop, value){
51909         this.source[prop] = value;
51910         this.store.getById(prop).set('value', value);
51911     },
51912
51913     getSource : function(){
51914         return this.source;
51915     }
51916 });
51917
51918 Roo.grid.PropertyColumnModel = function(grid, store){
51919     this.grid = grid;
51920     var g = Roo.grid;
51921     g.PropertyColumnModel.superclass.constructor.call(this, [
51922         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51923         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51924     ]);
51925     this.store = store;
51926     this.bselect = Roo.DomHelper.append(document.body, {
51927         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51928             {tag: 'option', value: 'true', html: 'true'},
51929             {tag: 'option', value: 'false', html: 'false'}
51930         ]
51931     });
51932     Roo.id(this.bselect);
51933     var f = Roo.form;
51934     this.editors = {
51935         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51936         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51937         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51938         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51939         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51940     };
51941     this.renderCellDelegate = this.renderCell.createDelegate(this);
51942     this.renderPropDelegate = this.renderProp.createDelegate(this);
51943 };
51944
51945 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51946     
51947     
51948     nameText : 'Name',
51949     valueText : 'Value',
51950     
51951     dateFormat : 'm/j/Y',
51952     
51953     
51954     renderDate : function(dateVal){
51955         return dateVal.dateFormat(this.dateFormat);
51956     },
51957
51958     renderBool : function(bVal){
51959         return bVal ? 'true' : 'false';
51960     },
51961
51962     isCellEditable : function(colIndex, rowIndex){
51963         return colIndex == 1;
51964     },
51965
51966     getRenderer : function(col){
51967         return col == 1 ?
51968             this.renderCellDelegate : this.renderPropDelegate;
51969     },
51970
51971     renderProp : function(v){
51972         return this.getPropertyName(v);
51973     },
51974
51975     renderCell : function(val){
51976         var rv = val;
51977         if(val instanceof Date){
51978             rv = this.renderDate(val);
51979         }else if(typeof val == 'boolean'){
51980             rv = this.renderBool(val);
51981         }
51982         return Roo.util.Format.htmlEncode(rv);
51983     },
51984
51985     getPropertyName : function(name){
51986         var pn = this.grid.propertyNames;
51987         return pn && pn[name] ? pn[name] : name;
51988     },
51989
51990     getCellEditor : function(colIndex, rowIndex){
51991         var p = this.store.getProperty(rowIndex);
51992         var n = p.data['name'], val = p.data['value'];
51993         
51994         if(typeof(this.grid.customEditors[n]) == 'string'){
51995             return this.editors[this.grid.customEditors[n]];
51996         }
51997         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51998             return this.grid.customEditors[n];
51999         }
52000         if(val instanceof Date){
52001             return this.editors['date'];
52002         }else if(typeof val == 'number'){
52003             return this.editors['number'];
52004         }else if(typeof val == 'boolean'){
52005             return this.editors['boolean'];
52006         }else{
52007             return this.editors['string'];
52008         }
52009     }
52010 });
52011
52012 /**
52013  * @class Roo.grid.PropertyGrid
52014  * @extends Roo.grid.EditorGrid
52015  * This class represents the  interface of a component based property grid control.
52016  * <br><br>Usage:<pre><code>
52017  var grid = new Roo.grid.PropertyGrid("my-container-id", {
52018       
52019  });
52020  // set any options
52021  grid.render();
52022  * </code></pre>
52023   
52024  * @constructor
52025  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52026  * The container MUST have some type of size defined for the grid to fill. The container will be
52027  * automatically set to position relative if it isn't already.
52028  * @param {Object} config A config object that sets properties on this grid.
52029  */
52030 Roo.grid.PropertyGrid = function(container, config){
52031     config = config || {};
52032     var store = new Roo.grid.PropertyStore(this);
52033     this.store = store;
52034     var cm = new Roo.grid.PropertyColumnModel(this, store);
52035     store.store.sort('name', 'ASC');
52036     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
52037         ds: store.store,
52038         cm: cm,
52039         enableColLock:false,
52040         enableColumnMove:false,
52041         stripeRows:false,
52042         trackMouseOver: false,
52043         clicksToEdit:1
52044     }, config));
52045     this.getGridEl().addClass('x-props-grid');
52046     this.lastEditRow = null;
52047     this.on('columnresize', this.onColumnResize, this);
52048     this.addEvents({
52049          /**
52050              * @event beforepropertychange
52051              * Fires before a property changes (return false to stop?)
52052              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52053              * @param {String} id Record Id
52054              * @param {String} newval New Value
52055          * @param {String} oldval Old Value
52056              */
52057         "beforepropertychange": true,
52058         /**
52059              * @event propertychange
52060              * Fires after a property changes
52061              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
52062              * @param {String} id Record Id
52063              * @param {String} newval New Value
52064          * @param {String} oldval Old Value
52065              */
52066         "propertychange": true
52067     });
52068     this.customEditors = this.customEditors || {};
52069 };
52070 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
52071     
52072      /**
52073      * @cfg {Object} customEditors map of colnames=> custom editors.
52074      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
52075      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
52076      * false disables editing of the field.
52077          */
52078     
52079       /**
52080      * @cfg {Object} propertyNames map of property Names to their displayed value
52081          */
52082     
52083     render : function(){
52084         Roo.grid.PropertyGrid.superclass.render.call(this);
52085         this.autoSize.defer(100, this);
52086     },
52087
52088     autoSize : function(){
52089         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
52090         if(this.view){
52091             this.view.fitColumns();
52092         }
52093     },
52094
52095     onColumnResize : function(){
52096         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
52097         this.autoSize();
52098     },
52099     /**
52100      * Sets the data for the Grid
52101      * accepts a Key => Value object of all the elements avaiable.
52102      * @param {Object} data  to appear in grid.
52103      */
52104     setSource : function(source){
52105         this.store.setSource(source);
52106         //this.autoSize();
52107     },
52108     /**
52109      * Gets all the data from the grid.
52110      * @return {Object} data  data stored in grid
52111      */
52112     getSource : function(){
52113         return this.store.getSource();
52114     }
52115 });/*
52116  * Based on:
52117  * Ext JS Library 1.1.1
52118  * Copyright(c) 2006-2007, Ext JS, LLC.
52119  *
52120  * Originally Released Under LGPL - original licence link has changed is not relivant.
52121  *
52122  * Fork - LGPL
52123  * <script type="text/javascript">
52124  */
52125  
52126 /**
52127  * @class Roo.LoadMask
52128  * A simple utility class for generically masking elements while loading data.  If the element being masked has
52129  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
52130  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
52131  * element's UpdateManager load indicator and will be destroyed after the initial load.
52132  * @constructor
52133  * Create a new LoadMask
52134  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
52135  * @param {Object} config The config object
52136  */
52137 Roo.LoadMask = function(el, config){
52138     this.el = Roo.get(el);
52139     Roo.apply(this, config);
52140     if(this.store){
52141         this.store.on('beforeload', this.onBeforeLoad, this);
52142         this.store.on('load', this.onLoad, this);
52143         this.store.on('loadexception', this.onLoadException, this);
52144         this.removeMask = false;
52145     }else{
52146         var um = this.el.getUpdateManager();
52147         um.showLoadIndicator = false; // disable the default indicator
52148         um.on('beforeupdate', this.onBeforeLoad, this);
52149         um.on('update', this.onLoad, this);
52150         um.on('failure', this.onLoad, this);
52151         this.removeMask = true;
52152     }
52153 };
52154
52155 Roo.LoadMask.prototype = {
52156     /**
52157      * @cfg {Boolean} removeMask
52158      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
52159      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
52160      */
52161     /**
52162      * @cfg {String} msg
52163      * The text to display in a centered loading message box (defaults to 'Loading...')
52164      */
52165     msg : 'Loading...',
52166     /**
52167      * @cfg {String} msgCls
52168      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
52169      */
52170     msgCls : 'x-mask-loading',
52171
52172     /**
52173      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
52174      * @type Boolean
52175      */
52176     disabled: false,
52177
52178     /**
52179      * Disables the mask to prevent it from being displayed
52180      */
52181     disable : function(){
52182        this.disabled = true;
52183     },
52184
52185     /**
52186      * Enables the mask so that it can be displayed
52187      */
52188     enable : function(){
52189         this.disabled = false;
52190     },
52191     
52192     onLoadException : function()
52193     {
52194         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52195             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52196         }
52197         this.el.unmask(this.removeMask);
52198     },
52199     // private
52200     onLoad : function()
52201     {
52202         this.el.unmask(this.removeMask);
52203     },
52204
52205     // private
52206     onBeforeLoad : function(){
52207         if(!this.disabled){
52208             this.el.mask(this.msg, this.msgCls);
52209         }
52210     },
52211
52212     // private
52213     destroy : function(){
52214         if(this.store){
52215             this.store.un('beforeload', this.onBeforeLoad, this);
52216             this.store.un('load', this.onLoad, this);
52217             this.store.un('loadexception', this.onLoadException, this);
52218         }else{
52219             var um = this.el.getUpdateManager();
52220             um.un('beforeupdate', this.onBeforeLoad, this);
52221             um.un('update', this.onLoad, this);
52222             um.un('failure', this.onLoad, this);
52223         }
52224     }
52225 };/*
52226  * Based on:
52227  * Ext JS Library 1.1.1
52228  * Copyright(c) 2006-2007, Ext JS, LLC.
52229  *
52230  * Originally Released Under LGPL - original licence link has changed is not relivant.
52231  *
52232  * Fork - LGPL
52233  * <script type="text/javascript">
52234  */
52235 Roo.XTemplate = function(){
52236     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52237     var s = this.html;
52238
52239     s = ['<tpl>', s, '</tpl>'].join('');
52240
52241     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52242
52243     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52244     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52245     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52246     var m, id = 0;
52247     var tpls = [];
52248
52249     while(m = s.match(re)){
52250        var m2 = m[0].match(nameRe);
52251        var m3 = m[0].match(ifRe);
52252        var m4 = m[0].match(execRe);
52253        var exp = null, fn = null, exec = null;
52254        var name = m2 && m2[1] ? m2[1] : '';
52255        if(m3){
52256            exp = m3 && m3[1] ? m3[1] : null;
52257            if(exp){
52258                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52259            }
52260        }
52261        if(m4){
52262            exp = m4 && m4[1] ? m4[1] : null;
52263            if(exp){
52264                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52265            }
52266        }
52267        if(name){
52268            switch(name){
52269                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52270                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52271                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52272            }
52273        }
52274        tpls.push({
52275             id: id,
52276             target: name,
52277             exec: exec,
52278             test: fn,
52279             body: m[1]||''
52280         });
52281        s = s.replace(m[0], '{xtpl'+ id + '}');
52282        ++id;
52283     }
52284     for(var i = tpls.length-1; i >= 0; --i){
52285         this.compileTpl(tpls[i]);
52286     }
52287     this.master = tpls[tpls.length-1];
52288     this.tpls = tpls;
52289 };
52290 Roo.extend(Roo.XTemplate, Roo.Template, {
52291
52292     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52293
52294     applySubTemplate : function(id, values, parent){
52295         var t = this.tpls[id];
52296         if(t.test && !t.test.call(this, values, parent)){
52297             return '';
52298         }
52299         if(t.exec && t.exec.call(this, values, parent)){
52300             return '';
52301         }
52302         var vs = t.target ? t.target.call(this, values, parent) : values;
52303         parent = t.target ? values : parent;
52304         if(t.target && vs instanceof Array){
52305             var buf = [];
52306             for(var i = 0, len = vs.length; i < len; i++){
52307                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52308             }
52309             return buf.join('');
52310         }
52311         return t.compiled.call(this, vs, parent);
52312     },
52313
52314     compileTpl : function(tpl){
52315         var fm = Roo.util.Format;
52316         var useF = this.disableFormats !== true;
52317         var sep = Roo.isGecko ? "+" : ",";
52318         var fn = function(m, name, format, args){
52319             if(name.substr(0, 4) == 'xtpl'){
52320                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52321             }
52322             var v;
52323             if(name.indexOf('.') != -1){
52324                 v = name;
52325             }else{
52326                 v = "values['" + name + "']";
52327             }
52328             if(format && useF){
52329                 args = args ? ',' + args : "";
52330                 if(format.substr(0, 5) != "this."){
52331                     format = "fm." + format + '(';
52332                 }else{
52333                     format = 'this.call("'+ format.substr(5) + '", ';
52334                     args = ", values";
52335                 }
52336             }else{
52337                 args= ''; format = "("+v+" === undefined ? '' : ";
52338             }
52339             return "'"+ sep + format + v + args + ")"+sep+"'";
52340         };
52341         var body;
52342         // branched to use + in gecko and [].join() in others
52343         if(Roo.isGecko){
52344             body = "tpl.compiled = function(values, parent){ return '" +
52345                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52346                     "';};";
52347         }else{
52348             body = ["tpl.compiled = function(values, parent){ return ['"];
52349             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52350             body.push("'].join('');};");
52351             body = body.join('');
52352         }
52353         /** eval:var:zzzzzzz */
52354         eval(body);
52355         return this;
52356     },
52357
52358     applyTemplate : function(values){
52359         return this.master.compiled.call(this, values, {});
52360         var s = this.subs;
52361     },
52362
52363     apply : function(){
52364         return this.applyTemplate.apply(this, arguments);
52365     },
52366
52367     compile : function(){return this;}
52368 });
52369
52370 Roo.XTemplate.from = function(el){
52371     el = Roo.getDom(el);
52372     return new Roo.XTemplate(el.value || el.innerHTML);
52373 };/*
52374  * Original code for Roojs - LGPL
52375  * <script type="text/javascript">
52376  */
52377  
52378 /**
52379  * @class Roo.XComponent
52380  * A delayed Element creator...
52381  * Or a way to group chunks of interface together.
52382  * 
52383  * Mypart.xyx = new Roo.XComponent({
52384
52385     parent : 'Mypart.xyz', // empty == document.element.!!
52386     order : '001',
52387     name : 'xxxx'
52388     region : 'xxxx'
52389     disabled : function() {} 
52390      
52391     tree : function() { // return an tree of xtype declared components
52392         var MODULE = this;
52393         return 
52394         {
52395             xtype : 'NestedLayoutPanel',
52396             // technicall
52397         }
52398      ]
52399  *})
52400  *
52401  *
52402  * It can be used to build a big heiracy, with parent etc.
52403  * or you can just use this to render a single compoent to a dom element
52404  * MYPART.render(Roo.Element | String(id) | dom_element )
52405  * 
52406  * @extends Roo.util.Observable
52407  * @constructor
52408  * @param cfg {Object} configuration of component
52409  * 
52410  */
52411 Roo.XComponent = function(cfg) {
52412     Roo.apply(this, cfg);
52413     this.addEvents({ 
52414         /**
52415              * @event built
52416              * Fires when this the componnt is built
52417              * @param {Roo.XComponent} c the component
52418              */
52419         'built' : true,
52420         /**
52421              * @event buildcomplete
52422              * Fires on the top level element when all elements have been built
52423              * @param {Roo.XComponent} c the top level component.
52424          */
52425         'buildcomplete' : true
52426         
52427     });
52428     this.region = this.region || 'center'; // default..
52429     Roo.XComponent.register(this);
52430     this.modules = false;
52431     this.el = false; // where the layout goes..
52432     
52433     
52434 }
52435 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52436     /**
52437      * @property el
52438      * The created element (with Roo.factory())
52439      * @type {Roo.Layout}
52440      */
52441     el  : false,
52442     
52443     /**
52444      * @property el
52445      * for BC  - use el in new code
52446      * @type {Roo.Layout}
52447      */
52448     panel : false,
52449     
52450     /**
52451      * @property layout
52452      * for BC  - use el in new code
52453      * @type {Roo.Layout}
52454      */
52455     layout : false,
52456     
52457      /**
52458      * @cfg {Function|boolean} disabled
52459      * If this module is disabled by some rule, return true from the funtion
52460      */
52461     disabled : false,
52462     
52463     /**
52464      * @cfg {String} parent 
52465      * Name of parent element which it get xtype added to..
52466      */
52467     parent: false,
52468     
52469     /**
52470      * @cfg {String} order
52471      * Used to set the order in which elements are created (usefull for multiple tabs)
52472      */
52473     
52474     order : false,
52475     /**
52476      * @cfg {String} name
52477      * String to display while loading.
52478      */
52479     name : false,
52480     /**
52481      * @cfg {String} region
52482      * Region to render component to (defaults to center)
52483      */
52484     region : 'center',
52485     
52486     /**
52487      * @cfg {Array} items
52488      * A single item array - the first element is the root of the tree..
52489      * It's done this way to stay compatible with the Xtype system...
52490      */
52491     items : false,
52492     
52493     
52494      /**
52495      * render
52496      * render element to dom or tree
52497      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52498      */
52499     
52500     render : function(el)
52501     {
52502         
52503         el = el || false;
52504         var hp = this.parent ? 1 : 0;
52505         
52506         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52507             // if parent is a '#.....' string, then let's use that..
52508             var ename = this.parent.substr(1)
52509             this.parent = false;
52510             el = Roo.get(ename);
52511             if (!el) {
52512                 Roo.log("Warning - element can not be found :#" + ename );
52513                 return;
52514             }
52515         }
52516         
52517         
52518         if (!this.parent) {
52519             
52520             el = el ? Roo.get(el) : false;
52521             
52522             // it's a top level one..
52523             this.parent =  {
52524                 el : new Roo.BorderLayout(el || document.body, {
52525                 
52526                      center: {
52527                          titlebar: false,
52528                          autoScroll:false,
52529                          closeOnTab: true,
52530                          tabPosition: 'top',
52531                           //resizeTabs: true,
52532                          alwaysShowTabs: el && hp? false :  true,
52533                          hideTabs: el || !hp ? true :  false,
52534                          minTabWidth: 140
52535                      }
52536                  })
52537             }
52538         }
52539         
52540         
52541             
52542         var tree = this.tree();
52543         tree.region = tree.region || this.region;
52544         this.el = this.parent.el.addxtype(tree);
52545         this.fireEvent('built', this);
52546         
52547         this.panel = this.el;
52548         this.layout = this.panel.layout;    
52549          
52550     }
52551     
52552 });
52553
52554 Roo.apply(Roo.XComponent, {
52555     
52556     /**
52557      * @property  buildCompleted
52558      * True when the builder has completed building the interface.
52559      * @type Boolean
52560      */
52561     buildCompleted : false,
52562      
52563     /**
52564      * @property  topModule
52565      * the upper most module - uses document.element as it's constructor.
52566      * @type Object
52567      */
52568      
52569     topModule  : false,
52570       
52571     /**
52572      * @property  modules
52573      * array of modules to be created by registration system.
52574      * @type {Array} of Roo.XComponent
52575      */
52576     
52577     modules : [],
52578     /**
52579      * @property  elmodules
52580      * array of modules to be created by which use #ID 
52581      * @type {Array} of Roo.XComponent
52582      */
52583      
52584     elmodules : [],
52585
52586     
52587     /**
52588      * Register components to be built later.
52589      *
52590      * This solves the following issues
52591      * - Building is not done on page load, but after an authentication process has occured.
52592      * - Interface elements are registered on page load
52593      * - Parent Interface elements may not be loaded before child, so this handles that..
52594      * 
52595      *
52596      * example:
52597      * 
52598      * MyApp.register({
52599           order : '000001',
52600           module : 'Pman.Tab.projectMgr',
52601           region : 'center',
52602           parent : 'Pman.layout',
52603           disabled : false,  // or use a function..
52604         })
52605      
52606      * * @param {Object} details about module
52607      */
52608     register : function(obj) {
52609         this.modules.push(obj);
52610          
52611     },
52612     /**
52613      * convert a string to an object..
52614      * eg. 'AAA.BBB' -> finds AAA.BBB
52615
52616      */
52617     
52618     toObject : function(str)
52619     {
52620         if (!str || typeof(str) == 'object') {
52621             return str;
52622         }
52623         if (str.substring(0,1) == '#') {
52624             return str;
52625         }
52626
52627         var ar = str.split('.');
52628         var rt, o;
52629         rt = ar.shift();
52630             /** eval:var:o */
52631         try {
52632             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52633         } catch (e) {
52634             throw "Module not found : " + str;
52635         }
52636         
52637         if (o === false) {
52638             throw "Module not found : " + str;
52639         }
52640         Roo.each(ar, function(e) {
52641             if (typeof(o[e]) == 'undefined') {
52642                 throw "Module not found : " + str;
52643             }
52644             o = o[e];
52645         });
52646         
52647         return o;
52648         
52649     },
52650     
52651     
52652     /**
52653      * move modules into their correct place in the tree..
52654      * 
52655      */
52656     preBuild : function ()
52657     {
52658         var _t = this;
52659         Roo.each(this.modules , function (obj)
52660         {
52661             var opar = obj.parent;
52662             try { 
52663                 obj.parent = this.toObject(opar);
52664             } catch(e) {
52665                 Roo.log(e.toString());
52666                 return;
52667             }
52668             
52669             if (!obj.parent) {
52670                 this.topModule = obj;
52671                 return;
52672             }
52673             if (typeof(obj.parent) == 'string') {
52674                 this.elmodules.push(obj);
52675                 return;
52676             }
52677             if (obj.parent.constructor != Roo.XComponent) {
52678                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52679             }
52680             if (!obj.parent.modules) {
52681                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52682                     function(o) { return o.order + '' }
52683                 );
52684             }
52685             
52686             obj.parent.modules.add(obj);
52687         }, this);
52688     },
52689     
52690      /**
52691      * make a list of modules to build.
52692      * @return {Array} list of modules. 
52693      */ 
52694     
52695     buildOrder : function()
52696     {
52697         var _this = this;
52698         var cmp = function(a,b) {   
52699             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52700         };
52701         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52702             throw "No top level modules to build";
52703         }
52704         
52705         // make a flat list in order of modules to build.
52706         var mods = this.topModule ? [ this.topModule ] : [];
52707         Roo.each(this.elmodules,function(e) { mods.push(e) });
52708
52709         
52710         // add modules to their parents..
52711         var addMod = function(m) {
52712            // Roo.debug && Roo.log(m.modKey);
52713             
52714             mods.push(m);
52715             if (m.modules) {
52716                 m.modules.keySort('ASC',  cmp );
52717                 m.modules.each(addMod);
52718             }
52719             // not sure if this is used any more..
52720             if (m.finalize) {
52721                 m.finalize.name = m.name + " (clean up) ";
52722                 mods.push(m.finalize);
52723             }
52724             
52725         }
52726         if (this.topModule) { 
52727             this.topModule.modules.keySort('ASC',  cmp );
52728             this.topModule.modules.each(addMod);
52729         }
52730         return mods;
52731     },
52732     
52733      /**
52734      * Build the registered modules.
52735      * @param {Object} parent element.
52736      * @param {Function} optional method to call after module has been added.
52737      * 
52738      */ 
52739    
52740     build : function() 
52741     {
52742         
52743         this.preBuild();
52744         var mods = this.buildOrder();
52745       
52746         //this.allmods = mods;
52747         //Roo.debug && Roo.log(mods);
52748         //return;
52749         if (!mods.length) { // should not happen
52750             throw "NO modules!!!";
52751         }
52752         
52753         
52754         
52755         // flash it up as modal - so we store the mask!?
52756         Roo.MessageBox.show({ title: 'loading' });
52757         Roo.MessageBox.show({
52758            title: "Please wait...",
52759            msg: "Building Interface...",
52760            width:450,
52761            progress:true,
52762            closable:false,
52763            modal: false
52764           
52765         });
52766         var total = mods.length;
52767         
52768         var _this = this;
52769         var progressRun = function() {
52770             if (!mods.length) {
52771                 Roo.debug && Roo.log('hide?');
52772                 Roo.MessageBox.hide();
52773                 if (_this.topModule) { 
52774                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52775                 }
52776                 // THE END...
52777                 return false;   
52778             }
52779             
52780             var m = mods.shift();
52781             
52782             
52783             Roo.debug && Roo.log(m);
52784             // not sure if this is supported any more.. - modules that are are just function
52785             if (typeof(m) == 'function') { 
52786                 m.call(this);
52787                 return progressRun.defer(10, _this);
52788             } 
52789             
52790             
52791             
52792             Roo.MessageBox.updateProgress(
52793                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52794                     " of " + total + 
52795                     (m.name ? (' - ' + m.name) : '')
52796                     );
52797             
52798          
52799             // is the module disabled?
52800             var disabled = (typeof(m.disabled) == 'function') ?
52801                 m.disabled.call(m.module.disabled) : m.disabled;    
52802             
52803             
52804             if (disabled) {
52805                 return progressRun(); // we do not update the display!
52806             }
52807             
52808             // now build 
52809             
52810             m.render();
52811             // it's 10 on top level, and 1 on others??? why...
52812             return progressRun.defer(10, _this);
52813              
52814         }
52815         progressRun.defer(1, _this);
52816      
52817         
52818         
52819     }
52820     
52821      
52822    
52823     
52824     
52825 });
52826  //<script type="text/javascript">
52827
52828
52829 /**
52830  * @class Roo.Login
52831  * @extends Roo.LayoutDialog
52832  * A generic Login Dialog..... - only one needed in theory!?!?
52833  *
52834  * Fires XComponent builder on success...
52835  * 
52836  * Sends 
52837  *    username,password, lang = for login actions.
52838  *    check = 1 for periodic checking that sesion is valid.
52839  *    passwordRequest = email request password
52840  *    logout = 1 = to logout
52841  * 
52842  * Affects: (this id="????" elements)
52843  *   loading  (removed) (used to indicate application is loading)
52844  *   loading-mask (hides) (used to hide application when it's building loading)
52845  *   
52846  * 
52847  * Usage: 
52848  *    
52849  * 
52850  * Myapp.login = Roo.Login({
52851      url: xxxx,
52852    
52853      realm : 'Myapp', 
52854      
52855      
52856      method : 'POST',
52857      
52858      
52859      * 
52860  })
52861  * 
52862  * 
52863  * 
52864  **/
52865  
52866 Roo.Login = function(cfg)
52867 {
52868     this.addEvents({
52869         'refreshed' : true
52870     });
52871     
52872     Roo.apply(this,cfg);
52873     
52874     Roo.onReady(function() {
52875         this.onLoad();
52876     }, this);
52877     // call parent..
52878     
52879    
52880     Roo.Login.superclass.constructor.call(this, this);
52881     //this.addxtype(this.items[0]);
52882     
52883     
52884 }
52885
52886
52887 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52888     
52889     /**
52890      * @cfg {String} method
52891      * Method used to query for login details.
52892      */
52893     
52894     method : 'POST',
52895     /**
52896      * @cfg {String} url
52897      * URL to query login data. - eg. baseURL + '/Login.php'
52898      */
52899     url : '',
52900     
52901     /**
52902      * @property user
52903      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52904      * @type {Object} 
52905      */
52906     user : false,
52907     /**
52908      * @property checkFails
52909      * Number of times we have attempted to get authentication check, and failed.
52910      * @type {Number} 
52911      */
52912     checkFails : 0,
52913       /**
52914      * @property intervalID
52915      * The window interval that does the constant login checking.
52916      * @type {Number} 
52917      */
52918     intervalID : 0,
52919     
52920     
52921     onLoad : function() // called on page load...
52922     {
52923         // load 
52924          
52925         if (Roo.get('loading')) { // clear any loading indicator..
52926             Roo.get('loading').remove();
52927         }
52928         
52929         //this.switchLang('en'); // set the language to english..
52930        
52931         this.check({
52932             success:  function(response, opts)  {  // check successfull...
52933             
52934                 var res = this.processResponse(response);
52935                 this.checkFails =0;
52936                 if (!res.success) { // error!
52937                     this.checkFails = 5;
52938                     //console.log('call failure');
52939                     return this.failure(response,opts);
52940                 }
52941                 
52942                 if (!res.data.id) { // id=0 == login failure.
52943                     return this.show();
52944                 }
52945                 
52946                               
52947                         //console.log(success);
52948                 this.fillAuth(res.data);   
52949                 this.checkFails =0;
52950                 Roo.XComponent.build();
52951             },
52952             failure : this.show
52953         });
52954         
52955     }, 
52956     
52957     
52958     check: function(cfg) // called every so often to refresh cookie etc..
52959     {
52960         if (cfg.again) { // could be undefined..
52961             this.checkFails++;
52962         } else {
52963             this.checkFails = 0;
52964         }
52965         var _this = this;
52966         if (this.sending) {
52967             if ( this.checkFails > 4) {
52968                 Roo.MessageBox.alert("Error",  
52969                     "Error getting authentication status. - try reloading, or wait a while", function() {
52970                         _this.sending = false;
52971                     }); 
52972                 return;
52973             }
52974             cfg.again = true;
52975             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52976             return;
52977         }
52978         this.sending = true;
52979         
52980         Roo.Ajax.request({  
52981             url: this.url,
52982             params: {
52983                 getAuthUser: true
52984             },  
52985             method: this.method,
52986             success:  cfg.success || this.success,
52987             failure : cfg.failure || this.failure,
52988             scope : this,
52989             callCfg : cfg
52990               
52991         });  
52992     }, 
52993     
52994     
52995     logout: function()
52996     {
52997         window.onbeforeunload = function() { }; // false does not work for IE..
52998         this.user = false;
52999         var _this = this;
53000         
53001         Roo.Ajax.request({  
53002             url: this.url,
53003             params: {
53004                 logout: 1
53005             },  
53006             method: 'GET',
53007             failure : function() {
53008                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
53009                     document.location = document.location.toString() + '?ts=' + Math.random();
53010                 });
53011                 
53012             },
53013             success : function() {
53014                 _this.user = false;
53015                 this.checkFails =0;
53016                 // fixme..
53017                 document.location = document.location.toString() + '?ts=' + Math.random();
53018             }
53019               
53020               
53021         }); 
53022     },
53023     
53024     processResponse : function (response)
53025     {
53026         var res = '';
53027         try {
53028             res = Roo.decode(response.responseText);
53029             // oops...
53030             if (typeof(res) != 'object') {
53031                 res = { success : false, errorMsg : res, errors : true };
53032             }
53033             if (typeof(res.success) == 'undefined') {
53034                 res.success = false;
53035             }
53036             
53037         } catch(e) {
53038             res = { success : false,  errorMsg : response.responseText, errors : true };
53039         }
53040         return res;
53041     },
53042     
53043     success : function(response, opts)  // check successfull...
53044     {  
53045         this.sending = false;
53046         var res = this.processResponse(response);
53047         if (!res.success) {
53048             return this.failure(response, opts);
53049         }
53050         if (!res.data || !res.data.id) {
53051             return this.failure(response,opts);
53052         }
53053         //console.log(res);
53054         this.fillAuth(res.data);
53055         
53056         this.checkFails =0;
53057         
53058     },
53059     
53060     
53061     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
53062     {
53063         this.authUser = -1;
53064         this.sending = false;
53065         var res = this.processResponse(response);
53066         //console.log(res);
53067         if ( this.checkFails > 2) {
53068         
53069             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
53070                 "Error getting authentication status. - try reloading"); 
53071             return;
53072         }
53073         opts.callCfg.again = true;
53074         this.check.defer(1000, this, [ opts.callCfg ]);
53075         return;  
53076     },
53077     
53078     
53079     
53080     fillAuth: function(au) {
53081         this.startAuthCheck();
53082         this.authUserId = au.id;
53083         this.authUser = au;
53084         this.lastChecked = new Date();
53085         this.fireEvent('refreshed', au);
53086         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
53087         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
53088         au.lang = au.lang || 'en';
53089         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
53090         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
53091         this.switchLang(au.lang );
53092         
53093      
53094         // open system... - -on setyp..
53095         if (this.authUserId  < 0) {
53096             Roo.MessageBox.alert("Warning", 
53097                 "This is an open system - please set up a admin user with a password.");  
53098         }
53099          
53100         //Pman.onload(); // which should do nothing if it's a re-auth result...
53101         
53102              
53103     },
53104     
53105     startAuthCheck : function() // starter for timeout checking..
53106     {
53107         if (this.intervalID) { // timer already in place...
53108             return false;
53109         }
53110         var _this = this;
53111         this.intervalID =  window.setInterval(function() {
53112               _this.check(false);
53113             }, 120000); // every 120 secs = 2mins..
53114         
53115         
53116     },
53117          
53118     
53119     switchLang : function (lang) 
53120     {
53121         _T = typeof(_T) == 'undefined' ? false : _T;
53122           if (!_T || !lang.length) {
53123             return;
53124         }
53125         
53126         if (!_T && lang != 'en') {
53127             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53128             return;
53129         }
53130         
53131         if (typeof(_T.en) == 'undefined') {
53132             _T.en = {};
53133             Roo.apply(_T.en, _T);
53134         }
53135         
53136         if (typeof(_T[lang]) == 'undefined') {
53137             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
53138             return;
53139         }
53140         
53141         
53142         Roo.apply(_T, _T[lang]);
53143         // just need to set the text values for everything...
53144         var _this = this;
53145         /* this will not work ...
53146         if (this.form) { 
53147             
53148                
53149             function formLabel(name, val) {
53150                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
53151             }
53152             
53153             formLabel('password', "Password"+':');
53154             formLabel('username', "Email Address"+':');
53155             formLabel('lang', "Language"+':');
53156             this.dialog.setTitle("Login");
53157             this.dialog.buttons[0].setText("Forgot Password");
53158             this.dialog.buttons[1].setText("Login");
53159         }
53160         */
53161         
53162         
53163     },
53164     
53165     
53166     title: "Login",
53167     modal: true,
53168     width:  350,
53169     //height: 230,
53170     height: 180,
53171     shadow: true,
53172     minWidth:200,
53173     minHeight:180,
53174     //proxyDrag: true,
53175     closable: false,
53176     draggable: false,
53177     collapsible: false,
53178     resizable: false,
53179     center: {  // needed??
53180         autoScroll:false,
53181         titlebar: false,
53182        // tabPosition: 'top',
53183         hideTabs: true,
53184         closeOnTab: true,
53185         alwaysShowTabs: false
53186     } ,
53187     listeners : {
53188         
53189         show  : function(dlg)
53190         {
53191             //console.log(this);
53192             this.form = this.layout.getRegion('center').activePanel.form;
53193             this.form.dialog = dlg;
53194             this.buttons[0].form = this.form;
53195             this.buttons[0].dialog = dlg;
53196             this.buttons[1].form = this.form;
53197             this.buttons[1].dialog = dlg;
53198            
53199            //this.resizeToLogo.defer(1000,this);
53200             // this is all related to resizing for logos..
53201             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
53202            //// if (!sz) {
53203              //   this.resizeToLogo.defer(1000,this);
53204              //   return;
53205            // }
53206             //var w = Ext.lib.Dom.getViewWidth() - 100;
53207             //var h = Ext.lib.Dom.getViewHeight() - 100;
53208             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
53209             //this.center();
53210             if (this.disabled) {
53211                 this.hide();
53212                 return;
53213             }
53214             
53215             if (this.user.id < 0) { // used for inital setup situations.
53216                 return;
53217             }
53218             
53219             if (this.intervalID) {
53220                 // remove the timer
53221                 window.clearInterval(this.intervalID);
53222                 this.intervalID = false;
53223             }
53224             
53225             
53226             if (Roo.get('loading')) {
53227                 Roo.get('loading').remove();
53228             }
53229             if (Roo.get('loading-mask')) {
53230                 Roo.get('loading-mask').hide();
53231             }
53232             
53233             //incomming._node = tnode;
53234             this.form.reset();
53235             //this.dialog.modal = !modal;
53236             //this.dialog.show();
53237             this.el.unmask(); 
53238             
53239             
53240             this.form.setValues({
53241                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53242                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53243             });
53244             
53245             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53246             if (this.form.findField('username').getValue().length > 0 ){
53247                 this.form.findField('password').focus();
53248             } else {
53249                this.form.findField('username').focus();
53250             }
53251     
53252         }
53253     },
53254     items : [
53255          {
53256        
53257             xtype : 'ContentPanel',
53258             xns : Roo,
53259             region: 'center',
53260             fitToFrame : true,
53261             
53262             items : [
53263     
53264                 {
53265                
53266                     xtype : 'Form',
53267                     xns : Roo.form,
53268                     labelWidth: 100,
53269                     style : 'margin: 10px;',
53270                     
53271                     listeners : {
53272                         actionfailed : function(f, act) {
53273                             // form can return { errors: .... }
53274                                 
53275                             //act.result.errors // invalid form element list...
53276                             //act.result.errorMsg// invalid form element list...
53277                             
53278                             this.dialog.el.unmask();
53279                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53280                                         "Login failed - communication error - try again.");
53281                                       
53282                         },
53283                         actioncomplete: function(re, act) {
53284                              
53285                             Roo.state.Manager.set(
53286                                 this.dialog.realm + '.username',  
53287                                     this.findField('username').getValue()
53288                             );
53289                             Roo.state.Manager.set(
53290                                 this.dialog.realm + '.lang',  
53291                                 this.findField('lang').getValue() 
53292                             );
53293                             
53294                             this.dialog.fillAuth(act.result.data);
53295                               
53296                             this.dialog.hide();
53297                             
53298                             if (Roo.get('loading-mask')) {
53299                                 Roo.get('loading-mask').show();
53300                             }
53301                             Roo.XComponent.build();
53302                             
53303                              
53304                             
53305                         }
53306                     },
53307                     items : [
53308                         {
53309                             xtype : 'TextField',
53310                             xns : Roo.form,
53311                             fieldLabel: "Email Address",
53312                             name: 'username',
53313                             width:200,
53314                             autoCreate : {tag: "input", type: "text", size: "20"}
53315                         },
53316                         {
53317                             xtype : 'TextField',
53318                             xns : Roo.form,
53319                             fieldLabel: "Password",
53320                             inputType: 'password',
53321                             name: 'password',
53322                             width:200,
53323                             autoCreate : {tag: "input", type: "text", size: "20"},
53324                             listeners : {
53325                                 specialkey : function(e,ev) {
53326                                     if (ev.keyCode == 13) {
53327                                         this.form.dialog.el.mask("Logging in");
53328                                         this.form.doAction('submit', {
53329                                             url: this.form.dialog.url,
53330                                             method: this.form.dialog.method
53331                                         });
53332                                     }
53333                                 }
53334                             }  
53335                         },
53336                         {
53337                             xtype : 'ComboBox',
53338                             xns : Roo.form,
53339                             fieldLabel: "Language",
53340                             name : 'langdisp',
53341                             store: {
53342                                 xtype : 'SimpleStore',
53343                                 fields: ['lang', 'ldisp'],
53344                                 data : [
53345                                     [ 'en', 'English' ],
53346                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53347                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53348                                 ]
53349                             },
53350                             
53351                             valueField : 'lang',
53352                             hiddenName:  'lang',
53353                             width: 200,
53354                             displayField:'ldisp',
53355                             typeAhead: false,
53356                             editable: false,
53357                             mode: 'local',
53358                             triggerAction: 'all',
53359                             emptyText:'Select a Language...',
53360                             selectOnFocus:true,
53361                             listeners : {
53362                                 select :  function(cb, rec, ix) {
53363                                     this.form.switchLang(rec.data.lang);
53364                                 }
53365                             }
53366                         
53367                         }
53368                     ]
53369                 }
53370                   
53371                 
53372             ]
53373         }
53374     ],
53375     buttons : [
53376         {
53377             xtype : 'Button',
53378             xns : 'Roo',
53379             text : "Forgot Password",
53380             listeners : {
53381                 click : function() {
53382                     //console.log(this);
53383                     var n = this.form.findField('username').getValue();
53384                     if (!n.length) {
53385                         Roo.MessageBox.alert("Error", "Fill in your email address");
53386                         return;
53387                     }
53388                     Roo.Ajax.request({
53389                         url: this.dialog.url,
53390                         params: {
53391                             passwordRequest: n
53392                         },
53393                         method: this.dialog.method,
53394                         success:  function(response, opts)  {  // check successfull...
53395                         
53396                             var res = this.dialog.processResponse(response);
53397                             if (!res.success) { // error!
53398                                Roo.MessageBox.alert("Error" ,
53399                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53400                                return;
53401                             }
53402                             Roo.MessageBox.alert("Notice" ,
53403                                 "Please check you email for the Password Reset message");
53404                         },
53405                         failure : function() {
53406                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53407                         }
53408                         
53409                     });
53410                 }
53411             }
53412         },
53413         {
53414             xtype : 'Button',
53415             xns : 'Roo',
53416             text : "Login",
53417             listeners : {
53418                 
53419                 click : function () {
53420                         
53421                     this.dialog.el.mask("Logging in");
53422                     this.form.doAction('submit', {
53423                             url: this.dialog.url,
53424                             method: this.dialog.method
53425                     });
53426                 }
53427             }
53428         }
53429     ]
53430   
53431   
53432 })
53433  
53434
53435
53436