IE9 - refine createContextualFragment fix
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   T      CST        Timezone setting of the machine running the code
1011   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1012 </pre>
1013  *
1014  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1015  * <pre><code>
1016 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1017 document.write(dt.format('Y-m-d'));                         //2007-01-10
1018 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1019 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1020  </code></pre>
1021  *
1022  * Here are some standard date/time patterns that you might find helpful.  They
1023  * are not part of the source of Date.js, but to use them you can simply copy this
1024  * block of code into any script that is included after Date.js and they will also become
1025  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1026  * <pre><code>
1027 Date.patterns = {
1028     ISO8601Long:"Y-m-d H:i:s",
1029     ISO8601Short:"Y-m-d",
1030     ShortDate: "n/j/Y",
1031     LongDate: "l, F d, Y",
1032     FullDateTime: "l, F d, Y g:i:s A",
1033     MonthDay: "F d",
1034     ShortTime: "g:i A",
1035     LongTime: "g:i:s A",
1036     SortableDateTime: "Y-m-d\\TH:i:s",
1037     UniversalSortableDateTime: "Y-m-d H:i:sO",
1038     YearMonth: "F, Y"
1039 };
1040 </code></pre>
1041  *
1042  * Example usage:
1043  * <pre><code>
1044 var dt = new Date();
1045 document.write(dt.format(Date.patterns.ShortDate));
1046  </code></pre>
1047  */
1048
1049 /*
1050  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1051  * They generate precompiled functions from date formats instead of parsing and
1052  * processing the pattern every time you format a date.  These functions are available
1053  * on every Date object (any javascript function).
1054  *
1055  * The original article and download are here:
1056  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1057  *
1058  */
1059  
1060  
1061  // was in core
1062 /**
1063  Returns the number of milliseconds between this date and date
1064  @param {Date} date (optional) Defaults to now
1065  @return {Number} The diff in milliseconds
1066  @member Date getElapsed
1067  */
1068 Date.prototype.getElapsed = function(date) {
1069         return Math.abs((date || new Date()).getTime()-this.getTime());
1070 };
1071 // was in date file..
1072
1073
1074 // private
1075 Date.parseFunctions = {count:0};
1076 // private
1077 Date.parseRegexes = [];
1078 // private
1079 Date.formatFunctions = {count:0};
1080
1081 // private
1082 Date.prototype.dateFormat = function(format) {
1083     if (Date.formatFunctions[format] == null) {
1084         Date.createNewFormat(format);
1085     }
1086     var func = Date.formatFunctions[format];
1087     return this[func]();
1088 };
1089
1090
1091 /**
1092  * Formats a date given the supplied format string
1093  * @param {String} format The format string
1094  * @return {String} The formatted date
1095  * @method
1096  */
1097 Date.prototype.format = Date.prototype.dateFormat;
1098
1099 // private
1100 Date.createNewFormat = function(format) {
1101     var funcName = "format" + Date.formatFunctions.count++;
1102     Date.formatFunctions[format] = funcName;
1103     var code = "Date.prototype." + funcName + " = function(){return ";
1104     var special = false;
1105     var ch = '';
1106     for (var i = 0; i < format.length; ++i) {
1107         ch = format.charAt(i);
1108         if (!special && ch == "\\") {
1109             special = true;
1110         }
1111         else if (special) {
1112             special = false;
1113             code += "'" + String.escape(ch) + "' + ";
1114         }
1115         else {
1116             code += Date.getFormatCode(ch);
1117         }
1118     }
1119     /** eval:var:zzzzzzzzzzzzz */
1120     eval(code.substring(0, code.length - 3) + ";}");
1121 };
1122
1123 // private
1124 Date.getFormatCode = function(character) {
1125     switch (character) {
1126     case "d":
1127         return "String.leftPad(this.getDate(), 2, '0') + ";
1128     case "D":
1129         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1130     case "j":
1131         return "this.getDate() + ";
1132     case "l":
1133         return "Date.dayNames[this.getDay()] + ";
1134     case "S":
1135         return "this.getSuffix() + ";
1136     case "w":
1137         return "this.getDay() + ";
1138     case "z":
1139         return "this.getDayOfYear() + ";
1140     case "W":
1141         return "this.getWeekOfYear() + ";
1142     case "F":
1143         return "Date.monthNames[this.getMonth()] + ";
1144     case "m":
1145         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1146     case "M":
1147         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1148     case "n":
1149         return "(this.getMonth() + 1) + ";
1150     case "t":
1151         return "this.getDaysInMonth() + ";
1152     case "L":
1153         return "(this.isLeapYear() ? 1 : 0) + ";
1154     case "Y":
1155         return "this.getFullYear() + ";
1156     case "y":
1157         return "('' + this.getFullYear()).substring(2, 4) + ";
1158     case "a":
1159         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1160     case "A":
1161         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1162     case "g":
1163         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1164     case "G":
1165         return "this.getHours() + ";
1166     case "h":
1167         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1168     case "H":
1169         return "String.leftPad(this.getHours(), 2, '0') + ";
1170     case "i":
1171         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1172     case "s":
1173         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1174     case "O":
1175         return "this.getGMTOffset() + ";
1176     case "T":
1177         return "this.getTimezone() + ";
1178     case "Z":
1179         return "(this.getTimezoneOffset() * -60) + ";
1180     default:
1181         return "'" + String.escape(character) + "' + ";
1182     }
1183 };
1184
1185 /**
1186  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1187  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1188  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1189  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1190  * string or the parse operation will fail.
1191  * Example Usage:
1192 <pre><code>
1193 //dt = Fri May 25 2007 (current date)
1194 var dt = new Date();
1195
1196 //dt = Thu May 25 2006 (today's month/day in 2006)
1197 dt = Date.parseDate("2006", "Y");
1198
1199 //dt = Sun Jan 15 2006 (all date parts specified)
1200 dt = Date.parseDate("2006-1-15", "Y-m-d");
1201
1202 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1203 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1204 </code></pre>
1205  * @param {String} input The unparsed date as a string
1206  * @param {String} format The format the date is in
1207  * @return {Date} The parsed date
1208  * @static
1209  */
1210 Date.parseDate = function(input, format) {
1211     if (Date.parseFunctions[format] == null) {
1212         Date.createParser(format);
1213     }
1214     var func = Date.parseFunctions[format];
1215     return Date[func](input);
1216 };
1217 /**
1218  * @private
1219  */
1220 Date.createParser = function(format) {
1221     var funcName = "parse" + Date.parseFunctions.count++;
1222     var regexNum = Date.parseRegexes.length;
1223     var currentGroup = 1;
1224     Date.parseFunctions[format] = funcName;
1225
1226     var code = "Date." + funcName + " = function(input){\n"
1227         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1228         + "var d = new Date();\n"
1229         + "y = d.getFullYear();\n"
1230         + "m = d.getMonth();\n"
1231         + "d = d.getDate();\n"
1232         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1233         + "if (results && results.length > 0) {";
1234     var regex = "";
1235
1236     var special = false;
1237     var ch = '';
1238     for (var i = 0; i < format.length; ++i) {
1239         ch = format.charAt(i);
1240         if (!special && ch == "\\") {
1241             special = true;
1242         }
1243         else if (special) {
1244             special = false;
1245             regex += String.escape(ch);
1246         }
1247         else {
1248             var obj = Date.formatCodeToRegex(ch, currentGroup);
1249             currentGroup += obj.g;
1250             regex += obj.s;
1251             if (obj.g && obj.c) {
1252                 code += obj.c;
1253             }
1254         }
1255     }
1256
1257     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1258         + "{v = new Date(y, m, d, h, i, s);}\n"
1259         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1260         + "{v = new Date(y, m, d, h, i);}\n"
1261         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1262         + "{v = new Date(y, m, d, h);}\n"
1263         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1264         + "{v = new Date(y, m, d);}\n"
1265         + "else if (y >= 0 && m >= 0)\n"
1266         + "{v = new Date(y, m);}\n"
1267         + "else if (y >= 0)\n"
1268         + "{v = new Date(y);}\n"
1269         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1270         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1271         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1272         + ";}";
1273
1274     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1275     /** eval:var:zzzzzzzzzzzzz */
1276     eval(code);
1277 };
1278
1279 // private
1280 Date.formatCodeToRegex = function(character, currentGroup) {
1281     switch (character) {
1282     case "D":
1283         return {g:0,
1284         c:null,
1285         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1286     case "j":
1287         return {g:1,
1288             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1289             s:"(\\d{1,2})"}; // day of month without leading zeroes
1290     case "d":
1291         return {g:1,
1292             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{2})"}; // day of month with leading zeroes
1294     case "l":
1295         return {g:0,
1296             c:null,
1297             s:"(?:" + Date.dayNames.join("|") + ")"};
1298     case "S":
1299         return {g:0,
1300             c:null,
1301             s:"(?:st|nd|rd|th)"};
1302     case "w":
1303         return {g:0,
1304             c:null,
1305             s:"\\d"};
1306     case "z":
1307         return {g:0,
1308             c:null,
1309             s:"(?:\\d{1,3})"};
1310     case "W":
1311         return {g:0,
1312             c:null,
1313             s:"(?:\\d{2})"};
1314     case "F":
1315         return {g:1,
1316             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1317             s:"(" + Date.monthNames.join("|") + ")"};
1318     case "M":
1319         return {g:1,
1320             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1321             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1322     case "n":
1323         return {g:1,
1324             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1325             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1326     case "m":
1327         return {g:1,
1328             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1329             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1330     case "t":
1331         return {g:0,
1332             c:null,
1333             s:"\\d{1,2}"};
1334     case "L":
1335         return {g:0,
1336             c:null,
1337             s:"(?:1|0)"};
1338     case "Y":
1339         return {g:1,
1340             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1341             s:"(\\d{4})"};
1342     case "y":
1343         return {g:1,
1344             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1345                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1346             s:"(\\d{1,2})"};
1347     case "a":
1348         return {g:1,
1349             c:"if (results[" + currentGroup + "] == 'am') {\n"
1350                 + "if (h == 12) { h = 0; }\n"
1351                 + "} else { if (h < 12) { h += 12; }}",
1352             s:"(am|pm)"};
1353     case "A":
1354         return {g:1,
1355             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1356                 + "if (h == 12) { h = 0; }\n"
1357                 + "} else { if (h < 12) { h += 12; }}",
1358             s:"(AM|PM)"};
1359     case "g":
1360     case "G":
1361         return {g:1,
1362             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1364     case "h":
1365     case "H":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1369     case "i":
1370         return {g:1,
1371             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{2})"};
1373     case "s":
1374         return {g:1,
1375             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"};
1377     case "O":
1378         return {g:1,
1379             c:[
1380                 "o = results[", currentGroup, "];\n",
1381                 "var sn = o.substring(0,1);\n", // get + / - sign
1382                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1383                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1384                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1385                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1386             ].join(""),
1387             s:"([+\-]\\d{4})"};
1388     case "T":
1389         return {g:0,
1390             c:null,
1391             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1392     case "Z":
1393         return {g:1,
1394             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1395                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1396             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1397     default:
1398         return {g:0,
1399             c:null,
1400             s:String.escape(character)};
1401     }
1402 };
1403
1404 /**
1405  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1406  * @return {String} The abbreviated timezone name (e.g. 'CST')
1407  */
1408 Date.prototype.getTimezone = function() {
1409     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1410 };
1411
1412 /**
1413  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1414  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1415  */
1416 Date.prototype.getGMTOffset = function() {
1417     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1418         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1419         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1420 };
1421
1422 /**
1423  * Get the numeric day number of the year, adjusted for leap year.
1424  * @return {Number} 0 through 364 (365 in leap years)
1425  */
1426 Date.prototype.getDayOfYear = function() {
1427     var num = 0;
1428     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1429     for (var i = 0; i < this.getMonth(); ++i) {
1430         num += Date.daysInMonth[i];
1431     }
1432     return num + this.getDate() - 1;
1433 };
1434
1435 /**
1436  * Get the string representation of the numeric week number of the year
1437  * (equivalent to the format specifier 'W').
1438  * @return {String} '00' through '52'
1439  */
1440 Date.prototype.getWeekOfYear = function() {
1441     // Skip to Thursday of this week
1442     var now = this.getDayOfYear() + (4 - this.getDay());
1443     // Find the first Thursday of the year
1444     var jan1 = new Date(this.getFullYear(), 0, 1);
1445     var then = (7 - jan1.getDay() + 4);
1446     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1447 };
1448
1449 /**
1450  * Whether or not the current date is in a leap year.
1451  * @return {Boolean} True if the current date is in a leap year, else false
1452  */
1453 Date.prototype.isLeapYear = function() {
1454     var year = this.getFullYear();
1455     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1456 };
1457
1458 /**
1459  * Get the first day of the current month, adjusted for leap year.  The returned value
1460  * is the numeric day index within the week (0-6) which can be used in conjunction with
1461  * the {@link #monthNames} array to retrieve the textual day name.
1462  * Example:
1463  *<pre><code>
1464 var dt = new Date('1/10/2007');
1465 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1466 </code></pre>
1467  * @return {Number} The day number (0-6)
1468  */
1469 Date.prototype.getFirstDayOfMonth = function() {
1470     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1471     return (day < 0) ? (day + 7) : day;
1472 };
1473
1474 /**
1475  * Get the last day of the current month, adjusted for leap year.  The returned value
1476  * is the numeric day index within the week (0-6) which can be used in conjunction with
1477  * the {@link #monthNames} array to retrieve the textual day name.
1478  * Example:
1479  *<pre><code>
1480 var dt = new Date('1/10/2007');
1481 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1482 </code></pre>
1483  * @return {Number} The day number (0-6)
1484  */
1485 Date.prototype.getLastDayOfMonth = function() {
1486     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1487     return (day < 0) ? (day + 7) : day;
1488 };
1489
1490
1491 /**
1492  * Get the first date of this date's month
1493  * @return {Date}
1494  */
1495 Date.prototype.getFirstDateOfMonth = function() {
1496     return new Date(this.getFullYear(), this.getMonth(), 1);
1497 };
1498
1499 /**
1500  * Get the last date of this date's month
1501  * @return {Date}
1502  */
1503 Date.prototype.getLastDateOfMonth = function() {
1504     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1505 };
1506 /**
1507  * Get the number of days in the current month, adjusted for leap year.
1508  * @return {Number} The number of days in the month
1509  */
1510 Date.prototype.getDaysInMonth = function() {
1511     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1512     return Date.daysInMonth[this.getMonth()];
1513 };
1514
1515 /**
1516  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1517  * @return {String} 'st, 'nd', 'rd' or 'th'
1518  */
1519 Date.prototype.getSuffix = function() {
1520     switch (this.getDate()) {
1521         case 1:
1522         case 21:
1523         case 31:
1524             return "st";
1525         case 2:
1526         case 22:
1527             return "nd";
1528         case 3:
1529         case 23:
1530             return "rd";
1531         default:
1532             return "th";
1533     }
1534 };
1535
1536 // private
1537 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1538
1539 /**
1540  * An array of textual month names.
1541  * Override these values for international dates, for example...
1542  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1543  * @type Array
1544  * @static
1545  */
1546 Date.monthNames =
1547    ["January",
1548     "February",
1549     "March",
1550     "April",
1551     "May",
1552     "June",
1553     "July",
1554     "August",
1555     "September",
1556     "October",
1557     "November",
1558     "December"];
1559
1560 /**
1561  * An array of textual day names.
1562  * Override these values for international dates, for example...
1563  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1564  * @type Array
1565  * @static
1566  */
1567 Date.dayNames =
1568    ["Sunday",
1569     "Monday",
1570     "Tuesday",
1571     "Wednesday",
1572     "Thursday",
1573     "Friday",
1574     "Saturday"];
1575
1576 // private
1577 Date.y2kYear = 50;
1578 // private
1579 Date.monthNumbers = {
1580     Jan:0,
1581     Feb:1,
1582     Mar:2,
1583     Apr:3,
1584     May:4,
1585     Jun:5,
1586     Jul:6,
1587     Aug:7,
1588     Sep:8,
1589     Oct:9,
1590     Nov:10,
1591     Dec:11};
1592
1593 /**
1594  * Creates and returns a new Date instance with the exact same date value as the called instance.
1595  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1596  * variable will also be changed.  When the intention is to create a new variable that will not
1597  * modify the original instance, you should create a clone.
1598  *
1599  * Example of correctly cloning a date:
1600  * <pre><code>
1601 //wrong way:
1602 var orig = new Date('10/1/2006');
1603 var copy = orig;
1604 copy.setDate(5);
1605 document.write(orig);  //returns 'Thu Oct 05 2006'!
1606
1607 //correct way:
1608 var orig = new Date('10/1/2006');
1609 var copy = orig.clone();
1610 copy.setDate(5);
1611 document.write(orig);  //returns 'Thu Oct 01 2006'
1612 </code></pre>
1613  * @return {Date} The new Date instance
1614  */
1615 Date.prototype.clone = function() {
1616         return new Date(this.getTime());
1617 };
1618
1619 /**
1620  * Clears any time information from this date
1621  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1622  @return {Date} this or the clone
1623  */
1624 Date.prototype.clearTime = function(clone){
1625     if(clone){
1626         return this.clone().clearTime();
1627     }
1628     this.setHours(0);
1629     this.setMinutes(0);
1630     this.setSeconds(0);
1631     this.setMilliseconds(0);
1632     return this;
1633 };
1634
1635 // private
1636 // safari setMonth is broken
1637 if(Roo.isSafari){
1638     Date.brokenSetMonth = Date.prototype.setMonth;
1639         Date.prototype.setMonth = function(num){
1640                 if(num <= -1){
1641                         var n = Math.ceil(-num);
1642                         var back_year = Math.ceil(n/12);
1643                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1644                         this.setFullYear(this.getFullYear() - back_year);
1645                         return Date.brokenSetMonth.call(this, month);
1646                 } else {
1647                         return Date.brokenSetMonth.apply(this, arguments);
1648                 }
1649         };
1650 }
1651
1652 /** Date interval constant 
1653 * @static 
1654 * @type String */
1655 Date.MILLI = "ms";
1656 /** Date interval constant 
1657 * @static 
1658 * @type String */
1659 Date.SECOND = "s";
1660 /** Date interval constant 
1661 * @static 
1662 * @type String */
1663 Date.MINUTE = "mi";
1664 /** Date interval constant 
1665 * @static 
1666 * @type String */
1667 Date.HOUR = "h";
1668 /** Date interval constant 
1669 * @static 
1670 * @type String */
1671 Date.DAY = "d";
1672 /** Date interval constant 
1673 * @static 
1674 * @type String */
1675 Date.MONTH = "mo";
1676 /** Date interval constant 
1677 * @static 
1678 * @type String */
1679 Date.YEAR = "y";
1680
1681 /**
1682  * Provides a convenient method of performing basic date arithmetic.  This method
1683  * does not modify the Date instance being called - it creates and returns
1684  * a new Date instance containing the resulting date value.
1685  *
1686  * Examples:
1687  * <pre><code>
1688 //Basic usage:
1689 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1690 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1691
1692 //Negative values will subtract correctly:
1693 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1694 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1695
1696 //You can even chain several calls together in one line!
1697 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1698 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1699  </code></pre>
1700  *
1701  * @param {String} interval   A valid date interval enum value
1702  * @param {Number} value      The amount to add to the current date
1703  * @return {Date} The new Date instance
1704  */
1705 Date.prototype.add = function(interval, value){
1706   var d = this.clone();
1707   if (!interval || value === 0) return d;
1708   switch(interval.toLowerCase()){
1709     case Date.MILLI:
1710       d.setMilliseconds(this.getMilliseconds() + value);
1711       break;
1712     case Date.SECOND:
1713       d.setSeconds(this.getSeconds() + value);
1714       break;
1715     case Date.MINUTE:
1716       d.setMinutes(this.getMinutes() + value);
1717       break;
1718     case Date.HOUR:
1719       d.setHours(this.getHours() + value);
1720       break;
1721     case Date.DAY:
1722       d.setDate(this.getDate() + value);
1723       break;
1724     case Date.MONTH:
1725       var day = this.getDate();
1726       if(day > 28){
1727           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1728       }
1729       d.setDate(day);
1730       d.setMonth(this.getMonth() + value);
1731       break;
1732     case Date.YEAR:
1733       d.setFullYear(this.getFullYear() + value);
1734       break;
1735   }
1736   return d;
1737 };/*
1738  * Based on:
1739  * Ext JS Library 1.1.1
1740  * Copyright(c) 2006-2007, Ext JS, LLC.
1741  *
1742  * Originally Released Under LGPL - original licence link has changed is not relivant.
1743  *
1744  * Fork - LGPL
1745  * <script type="text/javascript">
1746  */
1747
1748 Roo.lib.Dom = {
1749     getViewWidth : function(full) {
1750         return full ? this.getDocumentWidth() : this.getViewportWidth();
1751     },
1752
1753     getViewHeight : function(full) {
1754         return full ? this.getDocumentHeight() : this.getViewportHeight();
1755     },
1756
1757     getDocumentHeight: function() {
1758         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1759         return Math.max(scrollHeight, this.getViewportHeight());
1760     },
1761
1762     getDocumentWidth: function() {
1763         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1764         return Math.max(scrollWidth, this.getViewportWidth());
1765     },
1766
1767     getViewportHeight: function() {
1768         var height = self.innerHeight;
1769         var mode = document.compatMode;
1770
1771         if ((mode || Roo.isIE) && !Roo.isOpera) {
1772             height = (mode == "CSS1Compat") ?
1773                      document.documentElement.clientHeight :
1774                      document.body.clientHeight;
1775         }
1776
1777         return height;
1778     },
1779
1780     getViewportWidth: function() {
1781         var width = self.innerWidth;
1782         var mode = document.compatMode;
1783
1784         if (mode || Roo.isIE) {
1785             width = (mode == "CSS1Compat") ?
1786                     document.documentElement.clientWidth :
1787                     document.body.clientWidth;
1788         }
1789         return width;
1790     },
1791
1792     isAncestor : function(p, c) {
1793         p = Roo.getDom(p);
1794         c = Roo.getDom(c);
1795         if (!p || !c) {
1796             return false;
1797         }
1798
1799         if (p.contains && !Roo.isSafari) {
1800             return p.contains(c);
1801         } else if (p.compareDocumentPosition) {
1802             return !!(p.compareDocumentPosition(c) & 16);
1803         } else {
1804             var parent = c.parentNode;
1805             while (parent) {
1806                 if (parent == p) {
1807                     return true;
1808                 }
1809                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1810                     return false;
1811                 }
1812                 parent = parent.parentNode;
1813             }
1814             return false;
1815         }
1816     },
1817
1818     getRegion : function(el) {
1819         return Roo.lib.Region.getRegion(el);
1820     },
1821
1822     getY : function(el) {
1823         return this.getXY(el)[1];
1824     },
1825
1826     getX : function(el) {
1827         return this.getXY(el)[0];
1828     },
1829
1830     getXY : function(el) {
1831         var p, pe, b, scroll, bd = document.body;
1832         el = Roo.getDom(el);
1833         var fly = Roo.lib.AnimBase.fly;
1834         if (el.getBoundingClientRect) {
1835             b = el.getBoundingClientRect();
1836             scroll = fly(document).getScroll();
1837             return [b.left + scroll.left, b.top + scroll.top];
1838         }
1839         var x = 0, y = 0;
1840
1841         p = el;
1842
1843         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1844
1845         while (p) {
1846
1847             x += p.offsetLeft;
1848             y += p.offsetTop;
1849
1850             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1851                 hasAbsolute = true;
1852             }
1853
1854             if (Roo.isGecko) {
1855                 pe = fly(p);
1856
1857                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1858                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1859
1860
1861                 x += bl;
1862                 y += bt;
1863
1864
1865                 if (p != el && pe.getStyle('overflow') != 'visible') {
1866                     x += bl;
1867                     y += bt;
1868                 }
1869             }
1870             p = p.offsetParent;
1871         }
1872
1873         if (Roo.isSafari && hasAbsolute) {
1874             x -= bd.offsetLeft;
1875             y -= bd.offsetTop;
1876         }
1877
1878         if (Roo.isGecko && !hasAbsolute) {
1879             var dbd = fly(bd);
1880             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1881             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1882         }
1883
1884         p = el.parentNode;
1885         while (p && p != bd) {
1886             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1887                 x -= p.scrollLeft;
1888                 y -= p.scrollTop;
1889             }
1890             p = p.parentNode;
1891         }
1892         return [x, y];
1893     },
1894  
1895   
1896
1897
1898     setXY : function(el, xy) {
1899         el = Roo.fly(el, '_setXY');
1900         el.position();
1901         var pts = el.translatePoints(xy);
1902         if (xy[0] !== false) {
1903             el.dom.style.left = pts.left + "px";
1904         }
1905         if (xy[1] !== false) {
1906             el.dom.style.top = pts.top + "px";
1907         }
1908     },
1909
1910     setX : function(el, x) {
1911         this.setXY(el, [x, false]);
1912     },
1913
1914     setY : function(el, y) {
1915         this.setXY(el, [false, y]);
1916     }
1917 };
1918 /*
1919  * Portions of this file are based on pieces of Yahoo User Interface Library
1920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1921  * YUI licensed under the BSD License:
1922  * http://developer.yahoo.net/yui/license.txt
1923  * <script type="text/javascript">
1924  *
1925  */
1926
1927 Roo.lib.Event = function() {
1928     var loadComplete = false;
1929     var listeners = [];
1930     var unloadListeners = [];
1931     var retryCount = 0;
1932     var onAvailStack = [];
1933     var counter = 0;
1934     var lastError = null;
1935
1936     return {
1937         POLL_RETRYS: 200,
1938         POLL_INTERVAL: 20,
1939         EL: 0,
1940         TYPE: 1,
1941         FN: 2,
1942         WFN: 3,
1943         OBJ: 3,
1944         ADJ_SCOPE: 4,
1945         _interval: null,
1946
1947         startInterval: function() {
1948             if (!this._interval) {
1949                 var self = this;
1950                 var callback = function() {
1951                     self._tryPreloadAttach();
1952                 };
1953                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1954
1955             }
1956         },
1957
1958         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1959             onAvailStack.push({ id:         p_id,
1960                 fn:         p_fn,
1961                 obj:        p_obj,
1962                 override:   p_override,
1963                 checkReady: false    });
1964
1965             retryCount = this.POLL_RETRYS;
1966             this.startInterval();
1967         },
1968
1969
1970         addListener: function(el, eventName, fn) {
1971             el = Roo.getDom(el);
1972             if (!el || !fn) {
1973                 return false;
1974             }
1975
1976             if ("unload" == eventName) {
1977                 unloadListeners[unloadListeners.length] =
1978                 [el, eventName, fn];
1979                 return true;
1980             }
1981
1982             var wrappedFn = function(e) {
1983                 return fn(Roo.lib.Event.getEvent(e));
1984             };
1985
1986             var li = [el, eventName, fn, wrappedFn];
1987
1988             var index = listeners.length;
1989             listeners[index] = li;
1990
1991             this.doAdd(el, eventName, wrappedFn, false);
1992             return true;
1993
1994         },
1995
1996
1997         removeListener: function(el, eventName, fn) {
1998             var i, len;
1999
2000             el = Roo.getDom(el);
2001
2002             if(!fn) {
2003                 return this.purgeElement(el, false, eventName);
2004             }
2005
2006
2007             if ("unload" == eventName) {
2008
2009                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2010                     var li = unloadListeners[i];
2011                     if (li &&
2012                         li[0] == el &&
2013                         li[1] == eventName &&
2014                         li[2] == fn) {
2015                         unloadListeners.splice(i, 1);
2016                         return true;
2017                     }
2018                 }
2019
2020                 return false;
2021             }
2022
2023             var cacheItem = null;
2024
2025
2026             var index = arguments[3];
2027
2028             if ("undefined" == typeof index) {
2029                 index = this._getCacheIndex(el, eventName, fn);
2030             }
2031
2032             if (index >= 0) {
2033                 cacheItem = listeners[index];
2034             }
2035
2036             if (!el || !cacheItem) {
2037                 return false;
2038             }
2039
2040             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2041
2042             delete listeners[index][this.WFN];
2043             delete listeners[index][this.FN];
2044             listeners.splice(index, 1);
2045
2046             return true;
2047
2048         },
2049
2050
2051         getTarget: function(ev, resolveTextNode) {
2052             ev = ev.browserEvent || ev;
2053             var t = ev.target || ev.srcElement;
2054             return this.resolveTextNode(t);
2055         },
2056
2057
2058         resolveTextNode: function(node) {
2059             if (Roo.isSafari && node && 3 == node.nodeType) {
2060                 return node.parentNode;
2061             } else {
2062                 return node;
2063             }
2064         },
2065
2066
2067         getPageX: function(ev) {
2068             ev = ev.browserEvent || ev;
2069             var x = ev.pageX;
2070             if (!x && 0 !== x) {
2071                 x = ev.clientX || 0;
2072
2073                 if (Roo.isIE) {
2074                     x += this.getScroll()[1];
2075                 }
2076             }
2077
2078             return x;
2079         },
2080
2081
2082         getPageY: function(ev) {
2083             ev = ev.browserEvent || ev;
2084             var y = ev.pageY;
2085             if (!y && 0 !== y) {
2086                 y = ev.clientY || 0;
2087
2088                 if (Roo.isIE) {
2089                     y += this.getScroll()[0];
2090                 }
2091             }
2092
2093
2094             return y;
2095         },
2096
2097
2098         getXY: function(ev) {
2099             ev = ev.browserEvent || ev;
2100             return [this.getPageX(ev), this.getPageY(ev)];
2101         },
2102
2103
2104         getRelatedTarget: function(ev) {
2105             ev = ev.browserEvent || ev;
2106             var t = ev.relatedTarget;
2107             if (!t) {
2108                 if (ev.type == "mouseout") {
2109                     t = ev.toElement;
2110                 } else if (ev.type == "mouseover") {
2111                     t = ev.fromElement;
2112                 }
2113             }
2114
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         getTime: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             if (!ev.time) {
2122                 var t = new Date().getTime();
2123                 try {
2124                     ev.time = t;
2125                 } catch(ex) {
2126                     this.lastError = ex;
2127                     return t;
2128                 }
2129             }
2130
2131             return ev.time;
2132         },
2133
2134
2135         stopEvent: function(ev) {
2136             this.stopPropagation(ev);
2137             this.preventDefault(ev);
2138         },
2139
2140
2141         stopPropagation: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             if (ev.stopPropagation) {
2144                 ev.stopPropagation();
2145             } else {
2146                 ev.cancelBubble = true;
2147             }
2148         },
2149
2150
2151         preventDefault: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             if(ev.preventDefault) {
2154                 ev.preventDefault();
2155             } else {
2156                 ev.returnValue = false;
2157             }
2158         },
2159
2160
2161         getEvent: function(e) {
2162             var ev = e || window.event;
2163             if (!ev) {
2164                 var c = this.getEvent.caller;
2165                 while (c) {
2166                     ev = c.arguments[0];
2167                     if (ev && Event == ev.constructor) {
2168                         break;
2169                     }
2170                     c = c.caller;
2171                 }
2172             }
2173             return ev;
2174         },
2175
2176
2177         getCharCode: function(ev) {
2178             ev = ev.browserEvent || ev;
2179             return ev.charCode || ev.keyCode || 0;
2180         },
2181
2182
2183         _getCacheIndex: function(el, eventName, fn) {
2184             for (var i = 0,len = listeners.length; i < len; ++i) {
2185                 var li = listeners[i];
2186                 if (li &&
2187                     li[this.FN] == fn &&
2188                     li[this.EL] == el &&
2189                     li[this.TYPE] == eventName) {
2190                     return i;
2191                 }
2192             }
2193
2194             return -1;
2195         },
2196
2197
2198         elCache: {},
2199
2200
2201         getEl: function(id) {
2202             return document.getElementById(id);
2203         },
2204
2205
2206         clearCache: function() {
2207         },
2208
2209
2210         _load: function(e) {
2211             loadComplete = true;
2212             var EU = Roo.lib.Event;
2213
2214
2215             if (Roo.isIE) {
2216                 EU.doRemove(window, "load", EU._load);
2217             }
2218         },
2219
2220
2221         _tryPreloadAttach: function() {
2222
2223             if (this.locked) {
2224                 return false;
2225             }
2226
2227             this.locked = true;
2228
2229
2230             var tryAgain = !loadComplete;
2231             if (!tryAgain) {
2232                 tryAgain = (retryCount > 0);
2233             }
2234
2235
2236             var notAvail = [];
2237             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2238                 var item = onAvailStack[i];
2239                 if (item) {
2240                     var el = this.getEl(item.id);
2241
2242                     if (el) {
2243                         if (!item.checkReady ||
2244                             loadComplete ||
2245                             el.nextSibling ||
2246                             (document && document.body)) {
2247
2248                             var scope = el;
2249                             if (item.override) {
2250                                 if (item.override === true) {
2251                                     scope = item.obj;
2252                                 } else {
2253                                     scope = item.override;
2254                                 }
2255                             }
2256                             item.fn.call(scope, item.obj);
2257                             onAvailStack[i] = null;
2258                         }
2259                     } else {
2260                         notAvail.push(item);
2261                     }
2262                 }
2263             }
2264
2265             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2266
2267             if (tryAgain) {
2268
2269                 this.startInterval();
2270             } else {
2271                 clearInterval(this._interval);
2272                 this._interval = null;
2273             }
2274
2275             this.locked = false;
2276
2277             return true;
2278
2279         },
2280
2281
2282         purgeElement: function(el, recurse, eventName) {
2283             var elListeners = this.getListeners(el, eventName);
2284             if (elListeners) {
2285                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2286                     var l = elListeners[i];
2287                     this.removeListener(el, l.type, l.fn);
2288                 }
2289             }
2290
2291             if (recurse && el && el.childNodes) {
2292                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2293                     this.purgeElement(el.childNodes[i], recurse, eventName);
2294                 }
2295             }
2296         },
2297
2298
2299         getListeners: function(el, eventName) {
2300             var results = [], searchLists;
2301             if (!eventName) {
2302                 searchLists = [listeners, unloadListeners];
2303             } else if (eventName == "unload") {
2304                 searchLists = [unloadListeners];
2305             } else {
2306                 searchLists = [listeners];
2307             }
2308
2309             for (var j = 0; j < searchLists.length; ++j) {
2310                 var searchList = searchLists[j];
2311                 if (searchList && searchList.length > 0) {
2312                     for (var i = 0,len = searchList.length; i < len; ++i) {
2313                         var l = searchList[i];
2314                         if (l && l[this.EL] === el &&
2315                             (!eventName || eventName === l[this.TYPE])) {
2316                             results.push({
2317                                 type:   l[this.TYPE],
2318                                 fn:     l[this.FN],
2319                                 obj:    l[this.OBJ],
2320                                 adjust: l[this.ADJ_SCOPE],
2321                                 index:  i
2322                             });
2323                         }
2324                     }
2325                 }
2326             }
2327
2328             return (results.length) ? results : null;
2329         },
2330
2331
2332         _unload: function(e) {
2333
2334             var EU = Roo.lib.Event, i, j, l, len, index;
2335
2336             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2337                 l = unloadListeners[i];
2338                 if (l) {
2339                     var scope = window;
2340                     if (l[EU.ADJ_SCOPE]) {
2341                         if (l[EU.ADJ_SCOPE] === true) {
2342                             scope = l[EU.OBJ];
2343                         } else {
2344                             scope = l[EU.ADJ_SCOPE];
2345                         }
2346                     }
2347                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2348                     unloadListeners[i] = null;
2349                     l = null;
2350                     scope = null;
2351                 }
2352             }
2353
2354             unloadListeners = null;
2355
2356             if (listeners && listeners.length > 0) {
2357                 j = listeners.length;
2358                 while (j) {
2359                     index = j - 1;
2360                     l = listeners[index];
2361                     if (l) {
2362                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2363                                 l[EU.FN], index);
2364                     }
2365                     j = j - 1;
2366                 }
2367                 l = null;
2368
2369                 EU.clearCache();
2370             }
2371
2372             EU.doRemove(window, "unload", EU._unload);
2373
2374         },
2375
2376
2377         getScroll: function() {
2378             var dd = document.documentElement, db = document.body;
2379             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2380                 return [dd.scrollTop, dd.scrollLeft];
2381             } else if (db) {
2382                 return [db.scrollTop, db.scrollLeft];
2383             } else {
2384                 return [0, 0];
2385             }
2386         },
2387
2388
2389         doAdd: function () {
2390             if (window.addEventListener) {
2391                 return function(el, eventName, fn, capture) {
2392                     el.addEventListener(eventName, fn, (capture));
2393                 };
2394             } else if (window.attachEvent) {
2395                 return function(el, eventName, fn, capture) {
2396                     el.attachEvent("on" + eventName, fn);
2397                 };
2398             } else {
2399                 return function() {
2400                 };
2401             }
2402         }(),
2403
2404
2405         doRemove: function() {
2406             if (window.removeEventListener) {
2407                 return function (el, eventName, fn, capture) {
2408                     el.removeEventListener(eventName, fn, (capture));
2409                 };
2410             } else if (window.detachEvent) {
2411                 return function (el, eventName, fn) {
2412                     el.detachEvent("on" + eventName, fn);
2413                 };
2414             } else {
2415                 return function() {
2416                 };
2417             }
2418         }()
2419     };
2420     
2421 }();
2422 (function() {     
2423    
2424     var E = Roo.lib.Event;
2425     E.on = E.addListener;
2426     E.un = E.removeListener;
2427
2428     if (document && document.body) {
2429         E._load();
2430     } else {
2431         E.doAdd(window, "load", E._load);
2432     }
2433     E.doAdd(window, "unload", E._unload);
2434     E._tryPreloadAttach();
2435 })();
2436
2437 /*
2438  * Portions of this file are based on pieces of Yahoo User Interface Library
2439  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2440  * YUI licensed under the BSD License:
2441  * http://developer.yahoo.net/yui/license.txt
2442  * <script type="text/javascript">
2443  *
2444  */
2445
2446 (function() {
2447     
2448     Roo.lib.Ajax = {
2449         request : function(method, uri, cb, data, options) {
2450             if(options){
2451                 var hs = options.headers;
2452                 if(hs){
2453                     for(var h in hs){
2454                         if(hs.hasOwnProperty(h)){
2455                             this.initHeader(h, hs[h], false);
2456                         }
2457                     }
2458                 }
2459                 if(options.xmlData){
2460                     this.initHeader('Content-Type', 'text/xml', false);
2461                     method = 'POST';
2462                     data = options.xmlData;
2463                 }
2464             }
2465
2466             return this.asyncRequest(method, uri, cb, data);
2467         },
2468
2469         serializeForm : function(form) {
2470             if(typeof form == 'string') {
2471                 form = (document.getElementById(form) || document.forms[form]);
2472             }
2473
2474             var el, name, val, disabled, data = '', hasSubmit = false;
2475             for (var i = 0; i < form.elements.length; i++) {
2476                 el = form.elements[i];
2477                 disabled = form.elements[i].disabled;
2478                 name = form.elements[i].name;
2479                 val = form.elements[i].value;
2480
2481                 if (!disabled && name){
2482                     switch (el.type)
2483                             {
2484                         case 'select-one':
2485                         case 'select-multiple':
2486                             for (var j = 0; j < el.options.length; j++) {
2487                                 if (el.options[j].selected) {
2488                                     if (Roo.isIE) {
2489                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2490                                     }
2491                                     else {
2492                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2493                                     }
2494                                 }
2495                             }
2496                             break;
2497                         case 'radio':
2498                         case 'checkbox':
2499                             if (el.checked) {
2500                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2501                             }
2502                             break;
2503                         case 'file':
2504
2505                         case undefined:
2506
2507                         case 'reset':
2508
2509                         case 'button':
2510
2511                             break;
2512                         case 'submit':
2513                             if(hasSubmit == false) {
2514                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2515                                 hasSubmit = true;
2516                             }
2517                             break;
2518                         default:
2519                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2520                             break;
2521                     }
2522                 }
2523             }
2524             data = data.substr(0, data.length - 1);
2525             return data;
2526         },
2527
2528         headers:{},
2529
2530         hasHeaders:false,
2531
2532         useDefaultHeader:true,
2533
2534         defaultPostHeader:'application/x-www-form-urlencoded',
2535
2536         useDefaultXhrHeader:true,
2537
2538         defaultXhrHeader:'XMLHttpRequest',
2539
2540         hasDefaultHeaders:true,
2541
2542         defaultHeaders:{},
2543
2544         poll:{},
2545
2546         timeout:{},
2547
2548         pollInterval:50,
2549
2550         transactionId:0,
2551
2552         setProgId:function(id)
2553         {
2554             this.activeX.unshift(id);
2555         },
2556
2557         setDefaultPostHeader:function(b)
2558         {
2559             this.useDefaultHeader = b;
2560         },
2561
2562         setDefaultXhrHeader:function(b)
2563         {
2564             this.useDefaultXhrHeader = b;
2565         },
2566
2567         setPollingInterval:function(i)
2568         {
2569             if (typeof i == 'number' && isFinite(i)) {
2570                 this.pollInterval = i;
2571             }
2572         },
2573
2574         createXhrObject:function(transactionId)
2575         {
2576             var obj,http;
2577             try
2578             {
2579
2580                 http = new XMLHttpRequest();
2581
2582                 obj = { conn:http, tId:transactionId };
2583             }
2584             catch(e)
2585             {
2586                 for (var i = 0; i < this.activeX.length; ++i) {
2587                     try
2588                     {
2589
2590                         http = new ActiveXObject(this.activeX[i]);
2591
2592                         obj = { conn:http, tId:transactionId };
2593                         break;
2594                     }
2595                     catch(e) {
2596                     }
2597                 }
2598             }
2599             finally
2600             {
2601                 return obj;
2602             }
2603         },
2604
2605         getConnectionObject:function()
2606         {
2607             var o;
2608             var tId = this.transactionId;
2609
2610             try
2611             {
2612                 o = this.createXhrObject(tId);
2613                 if (o) {
2614                     this.transactionId++;
2615                 }
2616             }
2617             catch(e) {
2618             }
2619             finally
2620             {
2621                 return o;
2622             }
2623         },
2624
2625         asyncRequest:function(method, uri, callback, postData)
2626         {
2627             var o = this.getConnectionObject();
2628
2629             if (!o) {
2630                 return null;
2631             }
2632             else {
2633                 o.conn.open(method, uri, true);
2634
2635                 if (this.useDefaultXhrHeader) {
2636                     if (!this.defaultHeaders['X-Requested-With']) {
2637                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2638                     }
2639                 }
2640
2641                 if(postData && this.useDefaultHeader){
2642                     this.initHeader('Content-Type', this.defaultPostHeader);
2643                 }
2644
2645                  if (this.hasDefaultHeaders || this.hasHeaders) {
2646                     this.setHeader(o);
2647                 }
2648
2649                 this.handleReadyState(o, callback);
2650                 o.conn.send(postData || null);
2651
2652                 return o;
2653             }
2654         },
2655
2656         handleReadyState:function(o, callback)
2657         {
2658             var oConn = this;
2659
2660             if (callback && callback.timeout) {
2661                 this.timeout[o.tId] = window.setTimeout(function() {
2662                     oConn.abort(o, callback, true);
2663                 }, callback.timeout);
2664             }
2665
2666             this.poll[o.tId] = window.setInterval(
2667                     function() {
2668                         if (o.conn && o.conn.readyState == 4) {
2669                             window.clearInterval(oConn.poll[o.tId]);
2670                             delete oConn.poll[o.tId];
2671
2672                             if(callback && callback.timeout) {
2673                                 window.clearTimeout(oConn.timeout[o.tId]);
2674                                 delete oConn.timeout[o.tId];
2675                             }
2676
2677                             oConn.handleTransactionResponse(o, callback);
2678                         }
2679                     }
2680                     , this.pollInterval);
2681         },
2682
2683         handleTransactionResponse:function(o, callback, isAbort)
2684         {
2685
2686             if (!callback) {
2687                 this.releaseObject(o);
2688                 return;
2689             }
2690
2691             var httpStatus, responseObject;
2692
2693             try
2694             {
2695                 if (o.conn.status !== undefined && o.conn.status != 0) {
2696                     httpStatus = o.conn.status;
2697                 }
2698                 else {
2699                     httpStatus = 13030;
2700                 }
2701             }
2702             catch(e) {
2703
2704
2705                 httpStatus = 13030;
2706             }
2707
2708             if (httpStatus >= 200 && httpStatus < 300) {
2709                 responseObject = this.createResponseObject(o, callback.argument);
2710                 if (callback.success) {
2711                     if (!callback.scope) {
2712                         callback.success(responseObject);
2713                     }
2714                     else {
2715
2716
2717                         callback.success.apply(callback.scope, [responseObject]);
2718                     }
2719                 }
2720             }
2721             else {
2722                 switch (httpStatus) {
2723
2724                     case 12002:
2725                     case 12029:
2726                     case 12030:
2727                     case 12031:
2728                     case 12152:
2729                     case 13030:
2730                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2731                         if (callback.failure) {
2732                             if (!callback.scope) {
2733                                 callback.failure(responseObject);
2734                             }
2735                             else {
2736                                 callback.failure.apply(callback.scope, [responseObject]);
2737                             }
2738                         }
2739                         break;
2740                     default:
2741                         responseObject = this.createResponseObject(o, callback.argument);
2742                         if (callback.failure) {
2743                             if (!callback.scope) {
2744                                 callback.failure(responseObject);
2745                             }
2746                             else {
2747                                 callback.failure.apply(callback.scope, [responseObject]);
2748                             }
2749                         }
2750                 }
2751             }
2752
2753             this.releaseObject(o);
2754             responseObject = null;
2755         },
2756
2757         createResponseObject:function(o, callbackArg)
2758         {
2759             var obj = {};
2760             var headerObj = {};
2761
2762             try
2763             {
2764                 var headerStr = o.conn.getAllResponseHeaders();
2765                 var header = headerStr.split('\n');
2766                 for (var i = 0; i < header.length; i++) {
2767                     var delimitPos = header[i].indexOf(':');
2768                     if (delimitPos != -1) {
2769                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2770                     }
2771                 }
2772             }
2773             catch(e) {
2774             }
2775
2776             obj.tId = o.tId;
2777             obj.status = o.conn.status;
2778             obj.statusText = o.conn.statusText;
2779             obj.getResponseHeader = headerObj;
2780             obj.getAllResponseHeaders = headerStr;
2781             obj.responseText = o.conn.responseText;
2782             obj.responseXML = o.conn.responseXML;
2783
2784             if (typeof callbackArg !== undefined) {
2785                 obj.argument = callbackArg;
2786             }
2787
2788             return obj;
2789         },
2790
2791         createExceptionObject:function(tId, callbackArg, isAbort)
2792         {
2793             var COMM_CODE = 0;
2794             var COMM_ERROR = 'communication failure';
2795             var ABORT_CODE = -1;
2796             var ABORT_ERROR = 'transaction aborted';
2797
2798             var obj = {};
2799
2800             obj.tId = tId;
2801             if (isAbort) {
2802                 obj.status = ABORT_CODE;
2803                 obj.statusText = ABORT_ERROR;
2804             }
2805             else {
2806                 obj.status = COMM_CODE;
2807                 obj.statusText = COMM_ERROR;
2808             }
2809
2810             if (callbackArg) {
2811                 obj.argument = callbackArg;
2812             }
2813
2814             return obj;
2815         },
2816
2817         initHeader:function(label, value, isDefault)
2818         {
2819             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2820
2821             if (headerObj[label] === undefined) {
2822                 headerObj[label] = value;
2823             }
2824             else {
2825
2826
2827                 headerObj[label] = value + "," + headerObj[label];
2828             }
2829
2830             if (isDefault) {
2831                 this.hasDefaultHeaders = true;
2832             }
2833             else {
2834                 this.hasHeaders = true;
2835             }
2836         },
2837
2838
2839         setHeader:function(o)
2840         {
2841             if (this.hasDefaultHeaders) {
2842                 for (var prop in this.defaultHeaders) {
2843                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2844                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2845                     }
2846                 }
2847             }
2848
2849             if (this.hasHeaders) {
2850                 for (var prop in this.headers) {
2851                     if (this.headers.hasOwnProperty(prop)) {
2852                         o.conn.setRequestHeader(prop, this.headers[prop]);
2853                     }
2854                 }
2855                 this.headers = {};
2856                 this.hasHeaders = false;
2857             }
2858         },
2859
2860         resetDefaultHeaders:function() {
2861             delete this.defaultHeaders;
2862             this.defaultHeaders = {};
2863             this.hasDefaultHeaders = false;
2864         },
2865
2866         abort:function(o, callback, isTimeout)
2867         {
2868             if(this.isCallInProgress(o)) {
2869                 o.conn.abort();
2870                 window.clearInterval(this.poll[o.tId]);
2871                 delete this.poll[o.tId];
2872                 if (isTimeout) {
2873                     delete this.timeout[o.tId];
2874                 }
2875
2876                 this.handleTransactionResponse(o, callback, true);
2877
2878                 return true;
2879             }
2880             else {
2881                 return false;
2882             }
2883         },
2884
2885
2886         isCallInProgress:function(o)
2887         {
2888             if (o && o.conn) {
2889                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2890             }
2891             else {
2892
2893                 return false;
2894             }
2895         },
2896
2897
2898         releaseObject:function(o)
2899         {
2900
2901             o.conn = null;
2902
2903             o = null;
2904         },
2905
2906         activeX:[
2907         'MSXML2.XMLHTTP.3.0',
2908         'MSXML2.XMLHTTP',
2909         'Microsoft.XMLHTTP'
2910         ]
2911
2912
2913     };
2914 })();/*
2915  * Portions of this file are based on pieces of Yahoo User Interface Library
2916  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2917  * YUI licensed under the BSD License:
2918  * http://developer.yahoo.net/yui/license.txt
2919  * <script type="text/javascript">
2920  *
2921  */
2922
2923 Roo.lib.Region = function(t, r, b, l) {
2924     this.top = t;
2925     this[1] = t;
2926     this.right = r;
2927     this.bottom = b;
2928     this.left = l;
2929     this[0] = l;
2930 };
2931
2932
2933 Roo.lib.Region.prototype = {
2934     contains : function(region) {
2935         return ( region.left >= this.left &&
2936                  region.right <= this.right &&
2937                  region.top >= this.top &&
2938                  region.bottom <= this.bottom    );
2939
2940     },
2941
2942     getArea : function() {
2943         return ( (this.bottom - this.top) * (this.right - this.left) );
2944     },
2945
2946     intersect : function(region) {
2947         var t = Math.max(this.top, region.top);
2948         var r = Math.min(this.right, region.right);
2949         var b = Math.min(this.bottom, region.bottom);
2950         var l = Math.max(this.left, region.left);
2951
2952         if (b >= t && r >= l) {
2953             return new Roo.lib.Region(t, r, b, l);
2954         } else {
2955             return null;
2956         }
2957     },
2958     union : function(region) {
2959         var t = Math.min(this.top, region.top);
2960         var r = Math.max(this.right, region.right);
2961         var b = Math.max(this.bottom, region.bottom);
2962         var l = Math.min(this.left, region.left);
2963
2964         return new Roo.lib.Region(t, r, b, l);
2965     },
2966
2967     adjust : function(t, l, b, r) {
2968         this.top += t;
2969         this.left += l;
2970         this.right += r;
2971         this.bottom += b;
2972         return this;
2973     }
2974 };
2975
2976 Roo.lib.Region.getRegion = function(el) {
2977     var p = Roo.lib.Dom.getXY(el);
2978
2979     var t = p[1];
2980     var r = p[0] + el.offsetWidth;
2981     var b = p[1] + el.offsetHeight;
2982     var l = p[0];
2983
2984     return new Roo.lib.Region(t, r, b, l);
2985 };
2986 /*
2987  * Portions of this file are based on pieces of Yahoo User Interface Library
2988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2989  * YUI licensed under the BSD License:
2990  * http://developer.yahoo.net/yui/license.txt
2991  * <script type="text/javascript">
2992  *
2993  */
2994 //@@dep Roo.lib.Region
2995
2996
2997 Roo.lib.Point = function(x, y) {
2998     if (x instanceof Array) {
2999         y = x[1];
3000         x = x[0];
3001     }
3002     this.x = this.right = this.left = this[0] = x;
3003     this.y = this.top = this.bottom = this[1] = y;
3004 };
3005
3006 Roo.lib.Point.prototype = new Roo.lib.Region();
3007 /*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015  
3016 (function() {   
3017
3018     Roo.lib.Anim = {
3019         scroll : function(el, args, duration, easing, cb, scope) {
3020             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3021         },
3022
3023         motion : function(el, args, duration, easing, cb, scope) {
3024             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3025         },
3026
3027         color : function(el, args, duration, easing, cb, scope) {
3028             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3029         },
3030
3031         run : function(el, args, duration, easing, cb, scope, type) {
3032             type = type || Roo.lib.AnimBase;
3033             if (typeof easing == "string") {
3034                 easing = Roo.lib.Easing[easing];
3035             }
3036             var anim = new type(el, args, duration, easing);
3037             anim.animateX(function() {
3038                 Roo.callback(cb, scope);
3039             });
3040             return anim;
3041         }
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 (function() {    
3053     var libFlyweight;
3054     
3055     function fly(el) {
3056         if (!libFlyweight) {
3057             libFlyweight = new Roo.Element.Flyweight();
3058         }
3059         libFlyweight.dom = el;
3060         return libFlyweight;
3061     }
3062
3063     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3064     
3065    
3066     
3067     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3068         if (el) {
3069             this.init(el, attributes, duration, method);
3070         }
3071     };
3072
3073     Roo.lib.AnimBase.fly = fly;
3074     
3075     
3076     
3077     Roo.lib.AnimBase.prototype = {
3078
3079         toString: function() {
3080             var el = this.getEl();
3081             var id = el.id || el.tagName;
3082             return ("Anim " + id);
3083         },
3084
3085         patterns: {
3086             noNegatives:        /width|height|opacity|padding/i,
3087             offsetAttribute:  /^((width|height)|(top|left))$/,
3088             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3089             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3090         },
3091
3092
3093         doMethod: function(attr, start, end) {
3094             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3095         },
3096
3097
3098         setAttribute: function(attr, val, unit) {
3099             if (this.patterns.noNegatives.test(attr)) {
3100                 val = (val > 0) ? val : 0;
3101             }
3102
3103             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3104         },
3105
3106
3107         getAttribute: function(attr) {
3108             var el = this.getEl();
3109             var val = fly(el).getStyle(attr);
3110
3111             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3112                 return parseFloat(val);
3113             }
3114
3115             var a = this.patterns.offsetAttribute.exec(attr) || [];
3116             var pos = !!( a[3] );
3117             var box = !!( a[2] );
3118
3119
3120             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3121                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3122             } else {
3123                 val = 0;
3124             }
3125
3126             return val;
3127         },
3128
3129
3130         getDefaultUnit: function(attr) {
3131             if (this.patterns.defaultUnit.test(attr)) {
3132                 return 'px';
3133             }
3134
3135             return '';
3136         },
3137
3138         animateX : function(callback, scope) {
3139             var f = function() {
3140                 this.onComplete.removeListener(f);
3141                 if (typeof callback == "function") {
3142                     callback.call(scope || this, this);
3143                 }
3144             };
3145             this.onComplete.addListener(f, this);
3146             this.animate();
3147         },
3148
3149
3150         setRuntimeAttribute: function(attr) {
3151             var start;
3152             var end;
3153             var attributes = this.attributes;
3154
3155             this.runtimeAttributes[attr] = {};
3156
3157             var isset = function(prop) {
3158                 return (typeof prop !== 'undefined');
3159             };
3160
3161             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3162                 return false;
3163             }
3164
3165             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3166
3167
3168             if (isset(attributes[attr]['to'])) {
3169                 end = attributes[attr]['to'];
3170             } else if (isset(attributes[attr]['by'])) {
3171                 if (start.constructor == Array) {
3172                     end = [];
3173                     for (var i = 0, len = start.length; i < len; ++i) {
3174                         end[i] = start[i] + attributes[attr]['by'][i];
3175                     }
3176                 } else {
3177                     end = start + attributes[attr]['by'];
3178                 }
3179             }
3180
3181             this.runtimeAttributes[attr].start = start;
3182             this.runtimeAttributes[attr].end = end;
3183
3184
3185             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3186         },
3187
3188
3189         init: function(el, attributes, duration, method) {
3190
3191             var isAnimated = false;
3192
3193
3194             var startTime = null;
3195
3196
3197             var actualFrames = 0;
3198
3199
3200             el = Roo.getDom(el);
3201
3202
3203             this.attributes = attributes || {};
3204
3205
3206             this.duration = duration || 1;
3207
3208
3209             this.method = method || Roo.lib.Easing.easeNone;
3210
3211
3212             this.useSeconds = true;
3213
3214
3215             this.currentFrame = 0;
3216
3217
3218             this.totalFrames = Roo.lib.AnimMgr.fps;
3219
3220
3221             this.getEl = function() {
3222                 return el;
3223             };
3224
3225
3226             this.isAnimated = function() {
3227                 return isAnimated;
3228             };
3229
3230
3231             this.getStartTime = function() {
3232                 return startTime;
3233             };
3234
3235             this.runtimeAttributes = {};
3236
3237
3238             this.animate = function() {
3239                 if (this.isAnimated()) {
3240                     return false;
3241                 }
3242
3243                 this.currentFrame = 0;
3244
3245                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3246
3247                 Roo.lib.AnimMgr.registerElement(this);
3248             };
3249
3250
3251             this.stop = function(finish) {
3252                 if (finish) {
3253                     this.currentFrame = this.totalFrames;
3254                     this._onTween.fire();
3255                 }
3256                 Roo.lib.AnimMgr.stop(this);
3257             };
3258
3259             var onStart = function() {
3260                 this.onStart.fire();
3261
3262                 this.runtimeAttributes = {};
3263                 for (var attr in this.attributes) {
3264                     this.setRuntimeAttribute(attr);
3265                 }
3266
3267                 isAnimated = true;
3268                 actualFrames = 0;
3269                 startTime = new Date();
3270             };
3271
3272
3273             var onTween = function() {
3274                 var data = {
3275                     duration: new Date() - this.getStartTime(),
3276                     currentFrame: this.currentFrame
3277                 };
3278
3279                 data.toString = function() {
3280                     return (
3281                             'duration: ' + data.duration +
3282                             ', currentFrame: ' + data.currentFrame
3283                             );
3284                 };
3285
3286                 this.onTween.fire(data);
3287
3288                 var runtimeAttributes = this.runtimeAttributes;
3289
3290                 for (var attr in runtimeAttributes) {
3291                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3292                 }
3293
3294                 actualFrames += 1;
3295             };
3296
3297             var onComplete = function() {
3298                 var actual_duration = (new Date() - startTime) / 1000 ;
3299
3300                 var data = {
3301                     duration: actual_duration,
3302                     frames: actualFrames,
3303                     fps: actualFrames / actual_duration
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', frames: ' + data.frames +
3310                             ', fps: ' + data.fps
3311                             );
3312                 };
3313
3314                 isAnimated = false;
3315                 actualFrames = 0;
3316                 this.onComplete.fire(data);
3317             };
3318
3319
3320             this._onStart = new Roo.util.Event(this);
3321             this.onStart = new Roo.util.Event(this);
3322             this.onTween = new Roo.util.Event(this);
3323             this._onTween = new Roo.util.Event(this);
3324             this.onComplete = new Roo.util.Event(this);
3325             this._onComplete = new Roo.util.Event(this);
3326             this._onStart.addListener(onStart);
3327             this._onTween.addListener(onTween);
3328             this._onComplete.addListener(onComplete);
3329         }
3330     };
3331 })();
3332 /*
3333  * Portions of this file are based on pieces of Yahoo User Interface Library
3334  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3335  * YUI licensed under the BSD License:
3336  * http://developer.yahoo.net/yui/license.txt
3337  * <script type="text/javascript">
3338  *
3339  */
3340
3341 Roo.lib.AnimMgr = new function() {
3342
3343         var thread = null;
3344
3345
3346         var queue = [];
3347
3348
3349         var tweenCount = 0;
3350
3351
3352         this.fps = 1000;
3353
3354
3355         this.delay = 1;
3356
3357
3358         this.registerElement = function(tween) {
3359             queue[queue.length] = tween;
3360             tweenCount += 1;
3361             tween._onStart.fire();
3362             this.start();
3363         };
3364
3365
3366         this.unRegister = function(tween, index) {
3367             tween._onComplete.fire();
3368             index = index || getIndex(tween);
3369             if (index != -1) {
3370                 queue.splice(index, 1);
3371             }
3372
3373             tweenCount -= 1;
3374             if (tweenCount <= 0) {
3375                 this.stop();
3376             }
3377         };
3378
3379
3380         this.start = function() {
3381             if (thread === null) {
3382                 thread = setInterval(this.run, this.delay);
3383             }
3384         };
3385
3386
3387         this.stop = function(tween) {
3388             if (!tween) {
3389                 clearInterval(thread);
3390
3391                 for (var i = 0, len = queue.length; i < len; ++i) {
3392                     if (queue[0].isAnimated()) {
3393                         this.unRegister(queue[0], 0);
3394                     }
3395                 }
3396
3397                 queue = [];
3398                 thread = null;
3399                 tweenCount = 0;
3400             }
3401             else {
3402                 this.unRegister(tween);
3403             }
3404         };
3405
3406
3407         this.run = function() {
3408             for (var i = 0, len = queue.length; i < len; ++i) {
3409                 var tween = queue[i];
3410                 if (!tween || !tween.isAnimated()) {
3411                     continue;
3412                 }
3413
3414                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3415                 {
3416                     tween.currentFrame += 1;
3417
3418                     if (tween.useSeconds) {
3419                         correctFrame(tween);
3420                     }
3421                     tween._onTween.fire();
3422                 }
3423                 else {
3424                     Roo.lib.AnimMgr.stop(tween, i);
3425                 }
3426             }
3427         };
3428
3429         var getIndex = function(anim) {
3430             for (var i = 0, len = queue.length; i < len; ++i) {
3431                 if (queue[i] == anim) {
3432                     return i;
3433                 }
3434             }
3435             return -1;
3436         };
3437
3438
3439         var correctFrame = function(tween) {
3440             var frames = tween.totalFrames;
3441             var frame = tween.currentFrame;
3442             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3443             var elapsed = (new Date() - tween.getStartTime());
3444             var tweak = 0;
3445
3446             if (elapsed < tween.duration * 1000) {
3447                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3448             } else {
3449                 tweak = frames - (frame + 1);
3450             }
3451             if (tweak > 0 && isFinite(tweak)) {
3452                 if (tween.currentFrame + tweak >= frames) {
3453                     tweak = frames - (frame + 1);
3454                 }
3455
3456                 tween.currentFrame += tweak;
3457             }
3458         };
3459     };/*
3460  * Portions of this file are based on pieces of Yahoo User Interface Library
3461  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3462  * YUI licensed under the BSD License:
3463  * http://developer.yahoo.net/yui/license.txt
3464  * <script type="text/javascript">
3465  *
3466  */
3467 Roo.lib.Bezier = new function() {
3468
3469         this.getPosition = function(points, t) {
3470             var n = points.length;
3471             var tmp = [];
3472
3473             for (var i = 0; i < n; ++i) {
3474                 tmp[i] = [points[i][0], points[i][1]];
3475             }
3476
3477             for (var j = 1; j < n; ++j) {
3478                 for (i = 0; i < n - j; ++i) {
3479                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3480                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3481                 }
3482             }
3483
3484             return [ tmp[0][0], tmp[0][1] ];
3485
3486         };
3487     };/*
3488  * Portions of this file are based on pieces of Yahoo User Interface Library
3489  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3490  * YUI licensed under the BSD License:
3491  * http://developer.yahoo.net/yui/license.txt
3492  * <script type="text/javascript">
3493  *
3494  */
3495 (function() {
3496
3497     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3498         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3499     };
3500
3501     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3502
3503     var fly = Roo.lib.AnimBase.fly;
3504     var Y = Roo.lib;
3505     var superclass = Y.ColorAnim.superclass;
3506     var proto = Y.ColorAnim.prototype;
3507
3508     proto.toString = function() {
3509         var el = this.getEl();
3510         var id = el.id || el.tagName;
3511         return ("ColorAnim " + id);
3512     };
3513
3514     proto.patterns.color = /color$/i;
3515     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3516     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3517     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3518     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3519
3520
3521     proto.parseColor = function(s) {
3522         if (s.length == 3) {
3523             return s;
3524         }
3525
3526         var c = this.patterns.hex.exec(s);
3527         if (c && c.length == 4) {
3528             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3529         }
3530
3531         c = this.patterns.rgb.exec(s);
3532         if (c && c.length == 4) {
3533             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3534         }
3535
3536         c = this.patterns.hex3.exec(s);
3537         if (c && c.length == 4) {
3538             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3539         }
3540
3541         return null;
3542     };
3543     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3544     proto.getAttribute = function(attr) {
3545         var el = this.getEl();
3546         if (this.patterns.color.test(attr)) {
3547             var val = fly(el).getStyle(attr);
3548
3549             if (this.patterns.transparent.test(val)) {
3550                 var parent = el.parentNode;
3551                 val = fly(parent).getStyle(attr);
3552
3553                 while (parent && this.patterns.transparent.test(val)) {
3554                     parent = parent.parentNode;
3555                     val = fly(parent).getStyle(attr);
3556                     if (parent.tagName.toUpperCase() == 'HTML') {
3557                         val = '#fff';
3558                     }
3559                 }
3560             }
3561         } else {
3562             val = superclass.getAttribute.call(this, attr);
3563         }
3564
3565         return val;
3566     };
3567     proto.getAttribute = function(attr) {
3568         var el = this.getEl();
3569         if (this.patterns.color.test(attr)) {
3570             var val = fly(el).getStyle(attr);
3571
3572             if (this.patterns.transparent.test(val)) {
3573                 var parent = el.parentNode;
3574                 val = fly(parent).getStyle(attr);
3575
3576                 while (parent && this.patterns.transparent.test(val)) {
3577                     parent = parent.parentNode;
3578                     val = fly(parent).getStyle(attr);
3579                     if (parent.tagName.toUpperCase() == 'HTML') {
3580                         val = '#fff';
3581                     }
3582                 }
3583             }
3584         } else {
3585             val = superclass.getAttribute.call(this, attr);
3586         }
3587
3588         return val;
3589     };
3590
3591     proto.doMethod = function(attr, start, end) {
3592         var val;
3593
3594         if (this.patterns.color.test(attr)) {
3595             val = [];
3596             for (var i = 0, len = start.length; i < len; ++i) {
3597                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3598             }
3599
3600             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3601         }
3602         else {
3603             val = superclass.doMethod.call(this, attr, start, end);
3604         }
3605
3606         return val;
3607     };
3608
3609     proto.setRuntimeAttribute = function(attr) {
3610         superclass.setRuntimeAttribute.call(this, attr);
3611
3612         if (this.patterns.color.test(attr)) {
3613             var attributes = this.attributes;
3614             var start = this.parseColor(this.runtimeAttributes[attr].start);
3615             var end = this.parseColor(this.runtimeAttributes[attr].end);
3616
3617             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3618                 end = this.parseColor(attributes[attr].by);
3619
3620                 for (var i = 0, len = start.length; i < len; ++i) {
3621                     end[i] = start[i] + end[i];
3622                 }
3623             }
3624
3625             this.runtimeAttributes[attr].start = start;
3626             this.runtimeAttributes[attr].end = end;
3627         }
3628     };
3629 })();
3630
3631 /*
3632  * Portions of this file are based on pieces of Yahoo User Interface Library
3633  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3634  * YUI licensed under the BSD License:
3635  * http://developer.yahoo.net/yui/license.txt
3636  * <script type="text/javascript">
3637  *
3638  */
3639 Roo.lib.Easing = {
3640
3641
3642     easeNone: function (t, b, c, d) {
3643         return c * t / d + b;
3644     },
3645
3646
3647     easeIn: function (t, b, c, d) {
3648         return c * (t /= d) * t + b;
3649     },
3650
3651
3652     easeOut: function (t, b, c, d) {
3653         return -c * (t /= d) * (t - 2) + b;
3654     },
3655
3656
3657     easeBoth: function (t, b, c, d) {
3658         if ((t /= d / 2) < 1) {
3659             return c / 2 * t * t + b;
3660         }
3661
3662         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3663     },
3664
3665
3666     easeInStrong: function (t, b, c, d) {
3667         return c * (t /= d) * t * t * t + b;
3668     },
3669
3670
3671     easeOutStrong: function (t, b, c, d) {
3672         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3673     },
3674
3675
3676     easeBothStrong: function (t, b, c, d) {
3677         if ((t /= d / 2) < 1) {
3678             return c / 2 * t * t * t * t + b;
3679         }
3680
3681         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3682     },
3683
3684
3685
3686     elasticIn: function (t, b, c, d, a, p) {
3687         if (t == 0) {
3688             return b;
3689         }
3690         if ((t /= d) == 1) {
3691             return b + c;
3692         }
3693         if (!p) {
3694             p = d * .3;
3695         }
3696
3697         if (!a || a < Math.abs(c)) {
3698             a = c;
3699             var s = p / 4;
3700         }
3701         else {
3702             var s = p / (2 * Math.PI) * Math.asin(c / a);
3703         }
3704
3705         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3706     },
3707
3708
3709     elasticOut: function (t, b, c, d, a, p) {
3710         if (t == 0) {
3711             return b;
3712         }
3713         if ((t /= d) == 1) {
3714             return b + c;
3715         }
3716         if (!p) {
3717             p = d * .3;
3718         }
3719
3720         if (!a || a < Math.abs(c)) {
3721             a = c;
3722             var s = p / 4;
3723         }
3724         else {
3725             var s = p / (2 * Math.PI) * Math.asin(c / a);
3726         }
3727
3728         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3729     },
3730
3731
3732     elasticBoth: function (t, b, c, d, a, p) {
3733         if (t == 0) {
3734             return b;
3735         }
3736
3737         if ((t /= d / 2) == 2) {
3738             return b + c;
3739         }
3740
3741         if (!p) {
3742             p = d * (.3 * 1.5);
3743         }
3744
3745         if (!a || a < Math.abs(c)) {
3746             a = c;
3747             var s = p / 4;
3748         }
3749         else {
3750             var s = p / (2 * Math.PI) * Math.asin(c / a);
3751         }
3752
3753         if (t < 1) {
3754             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3755                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3756         }
3757         return a * Math.pow(2, -10 * (t -= 1)) *
3758                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3759     },
3760
3761
3762
3763     backIn: function (t, b, c, d, s) {
3764         if (typeof s == 'undefined') {
3765             s = 1.70158;
3766         }
3767         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3768     },
3769
3770
3771     backOut: function (t, b, c, d, s) {
3772         if (typeof s == 'undefined') {
3773             s = 1.70158;
3774         }
3775         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3776     },
3777
3778
3779     backBoth: function (t, b, c, d, s) {
3780         if (typeof s == 'undefined') {
3781             s = 1.70158;
3782         }
3783
3784         if ((t /= d / 2 ) < 1) {
3785             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3786         }
3787         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3788     },
3789
3790
3791     bounceIn: function (t, b, c, d) {
3792         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3793     },
3794
3795
3796     bounceOut: function (t, b, c, d) {
3797         if ((t /= d) < (1 / 2.75)) {
3798             return c * (7.5625 * t * t) + b;
3799         } else if (t < (2 / 2.75)) {
3800             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3801         } else if (t < (2.5 / 2.75)) {
3802             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3803         }
3804         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3805     },
3806
3807
3808     bounceBoth: function (t, b, c, d) {
3809         if (t < d / 2) {
3810             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3811         }
3812         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3813     }
3814 };/*
3815  * Portions of this file are based on pieces of Yahoo User Interface Library
3816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3817  * YUI licensed under the BSD License:
3818  * http://developer.yahoo.net/yui/license.txt
3819  * <script type="text/javascript">
3820  *
3821  */
3822     (function() {
3823         Roo.lib.Motion = function(el, attributes, duration, method) {
3824             if (el) {
3825                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3826             }
3827         };
3828
3829         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3830
3831
3832         var Y = Roo.lib;
3833         var superclass = Y.Motion.superclass;
3834         var proto = Y.Motion.prototype;
3835
3836         proto.toString = function() {
3837             var el = this.getEl();
3838             var id = el.id || el.tagName;
3839             return ("Motion " + id);
3840         };
3841
3842         proto.patterns.points = /^points$/i;
3843
3844         proto.setAttribute = function(attr, val, unit) {
3845             if (this.patterns.points.test(attr)) {
3846                 unit = unit || 'px';
3847                 superclass.setAttribute.call(this, 'left', val[0], unit);
3848                 superclass.setAttribute.call(this, 'top', val[1], unit);
3849             } else {
3850                 superclass.setAttribute.call(this, attr, val, unit);
3851             }
3852         };
3853
3854         proto.getAttribute = function(attr) {
3855             if (this.patterns.points.test(attr)) {
3856                 var val = [
3857                         superclass.getAttribute.call(this, 'left'),
3858                         superclass.getAttribute.call(this, 'top')
3859                         ];
3860             } else {
3861                 val = superclass.getAttribute.call(this, attr);
3862             }
3863
3864             return val;
3865         };
3866
3867         proto.doMethod = function(attr, start, end) {
3868             var val = null;
3869
3870             if (this.patterns.points.test(attr)) {
3871                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3872                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3873             } else {
3874                 val = superclass.doMethod.call(this, attr, start, end);
3875             }
3876             return val;
3877         };
3878
3879         proto.setRuntimeAttribute = function(attr) {
3880             if (this.patterns.points.test(attr)) {
3881                 var el = this.getEl();
3882                 var attributes = this.attributes;
3883                 var start;
3884                 var control = attributes['points']['control'] || [];
3885                 var end;
3886                 var i, len;
3887
3888                 if (control.length > 0 && !(control[0] instanceof Array)) {
3889                     control = [control];
3890                 } else {
3891                     var tmp = [];
3892                     for (i = 0,len = control.length; i < len; ++i) {
3893                         tmp[i] = control[i];
3894                     }
3895                     control = tmp;
3896                 }
3897
3898                 Roo.fly(el).position();
3899
3900                 if (isset(attributes['points']['from'])) {
3901                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3902                 }
3903                 else {
3904                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3905                 }
3906
3907                 start = this.getAttribute('points');
3908
3909
3910                 if (isset(attributes['points']['to'])) {
3911                     end = translateValues.call(this, attributes['points']['to'], start);
3912
3913                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3914                     for (i = 0,len = control.length; i < len; ++i) {
3915                         control[i] = translateValues.call(this, control[i], start);
3916                     }
3917
3918
3919                 } else if (isset(attributes['points']['by'])) {
3920                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3921
3922                     for (i = 0,len = control.length; i < len; ++i) {
3923                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3924                     }
3925                 }
3926
3927                 this.runtimeAttributes[attr] = [start];
3928
3929                 if (control.length > 0) {
3930                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3931                 }
3932
3933                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3934             }
3935             else {
3936                 superclass.setRuntimeAttribute.call(this, attr);
3937             }
3938         };
3939
3940         var translateValues = function(val, start) {
3941             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3942             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3943
3944             return val;
3945         };
3946
3947         var isset = function(prop) {
3948             return (typeof prop !== 'undefined');
3949         };
3950     })();
3951 /*
3952  * Portions of this file are based on pieces of Yahoo User Interface Library
3953  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3954  * YUI licensed under the BSD License:
3955  * http://developer.yahoo.net/yui/license.txt
3956  * <script type="text/javascript">
3957  *
3958  */
3959     (function() {
3960         Roo.lib.Scroll = function(el, attributes, duration, method) {
3961             if (el) {
3962                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3963             }
3964         };
3965
3966         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3967
3968
3969         var Y = Roo.lib;
3970         var superclass = Y.Scroll.superclass;
3971         var proto = Y.Scroll.prototype;
3972
3973         proto.toString = function() {
3974             var el = this.getEl();
3975             var id = el.id || el.tagName;
3976             return ("Scroll " + id);
3977         };
3978
3979         proto.doMethod = function(attr, start, end) {
3980             var val = null;
3981
3982             if (attr == 'scroll') {
3983                 val = [
3984                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3985                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3986                         ];
3987
3988             } else {
3989                 val = superclass.doMethod.call(this, attr, start, end);
3990             }
3991             return val;
3992         };
3993
3994         proto.getAttribute = function(attr) {
3995             var val = null;
3996             var el = this.getEl();
3997
3998             if (attr == 'scroll') {
3999                 val = [ el.scrollLeft, el.scrollTop ];
4000             } else {
4001                 val = superclass.getAttribute.call(this, attr);
4002             }
4003
4004             return val;
4005         };
4006
4007         proto.setAttribute = function(attr, val, unit) {
4008             var el = this.getEl();
4009
4010             if (attr == 'scroll') {
4011                 el.scrollLeft = val[0];
4012                 el.scrollTop = val[1];
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017     })();
4018 /*
4019  * Based on:
4020  * Ext JS Library 1.1.1
4021  * Copyright(c) 2006-2007, Ext JS, LLC.
4022  *
4023  * Originally Released Under LGPL - original licence link has changed is not relivant.
4024  *
4025  * Fork - LGPL
4026  * <script type="text/javascript">
4027  */
4028
4029
4030 // nasty IE9 hack - what a pile of crap that is..
4031
4032 <<<<<<< HEAD
4033  if (typeof Range.prototype.createContextualFragment == "undefined") {
4034 =======
4035  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4036 >>>>>>> cba88e023db3bc6c12affc2e25a149cdac04fd17
4037     Range.prototype.createContextualFragment = function (html) {
4038         var doc = window.document;
4039         var container = doc.createElement("div");
4040         container.innerHTML = html;
4041         var frag = doc.createDocumentFragment(), n;
4042         while ((n = container.firstChild)) {
4043             frag.appendChild(n);
4044         }
4045         return frag;
4046     };
4047 }
4048
4049 /**
4050  * @class Roo.DomHelper
4051  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4052  * 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>.
4053  * @singleton
4054  */
4055 Roo.DomHelper = function(){
4056     var tempTableEl = null;
4057     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4058     var tableRe = /^table|tbody|tr|td$/i;
4059     var xmlns = {};
4060     // build as innerHTML where available
4061     /** @ignore */
4062     var createHtml = function(o){
4063         if(typeof o == 'string'){
4064             return o;
4065         }
4066         var b = "";
4067         if(!o.tag){
4068             o.tag = "div";
4069         }
4070         b += "<" + o.tag;
4071         for(var attr in o){
4072             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4073             if(attr == "style"){
4074                 var s = o["style"];
4075                 if(typeof s == "function"){
4076                     s = s.call();
4077                 }
4078                 if(typeof s == "string"){
4079                     b += ' style="' + s + '"';
4080                 }else if(typeof s == "object"){
4081                     b += ' style="';
4082                     for(var key in s){
4083                         if(typeof s[key] != "function"){
4084                             b += key + ":" + s[key] + ";";
4085                         }
4086                     }
4087                     b += '"';
4088                 }
4089             }else{
4090                 if(attr == "cls"){
4091                     b += ' class="' + o["cls"] + '"';
4092                 }else if(attr == "htmlFor"){
4093                     b += ' for="' + o["htmlFor"] + '"';
4094                 }else{
4095                     b += " " + attr + '="' + o[attr] + '"';
4096                 }
4097             }
4098         }
4099         if(emptyTags.test(o.tag)){
4100             b += "/>";
4101         }else{
4102             b += ">";
4103             var cn = o.children || o.cn;
4104             if(cn){
4105                 //http://bugs.kde.org/show_bug.cgi?id=71506
4106                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4107                     for(var i = 0, len = cn.length; i < len; i++) {
4108                         b += createHtml(cn[i], b);
4109                     }
4110                 }else{
4111                     b += createHtml(cn, b);
4112                 }
4113             }
4114             if(o.html){
4115                 b += o.html;
4116             }
4117             b += "</" + o.tag + ">";
4118         }
4119         return b;
4120     };
4121
4122     // build as dom
4123     /** @ignore */
4124     var createDom = function(o, parentNode){
4125          
4126         // defininition craeted..
4127         var ns = false;
4128         if (o.ns && o.ns != 'html') {
4129                
4130             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4131                 xmlns[o.ns] = o.xmlns;
4132                 ns = o.xmlns;
4133             }
4134             if (typeof(xmlns[o.ns]) == 'undefined') {
4135                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4136             }
4137             ns = xmlns[o.ns];
4138         }
4139         
4140         
4141         if (typeof(o) == 'string') {
4142             return parentNode.appendChild(document.createTextNode(o));
4143         }
4144         o.tag = o.tag || div;
4145         if (o.ns && Roo.isIE) {
4146             ns = false;
4147             o.tag = o.ns + ':' + o.tag;
4148             
4149         }
4150         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4151         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4152         for(var attr in o){
4153             
4154             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4155                     attr == "style" || typeof o[attr] == "function") continue;
4156                     
4157             if(attr=="cls" && Roo.isIE){
4158                 el.className = o["cls"];
4159             }else{
4160                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4161                 else el[attr] = o[attr];
4162             }
4163         }
4164         Roo.DomHelper.applyStyles(el, o.style);
4165         var cn = o.children || o.cn;
4166         if(cn){
4167             //http://bugs.kde.org/show_bug.cgi?id=71506
4168              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                 for(var i = 0, len = cn.length; i < len; i++) {
4170                     createDom(cn[i], el);
4171                 }
4172             }else{
4173                 createDom(cn, el);
4174             }
4175         }
4176         if(o.html){
4177             el.innerHTML = o.html;
4178         }
4179         if(parentNode){
4180            parentNode.appendChild(el);
4181         }
4182         return el;
4183     };
4184
4185     var ieTable = function(depth, s, h, e){
4186         tempTableEl.innerHTML = [s, h, e].join('');
4187         var i = -1, el = tempTableEl;
4188         while(++i < depth){
4189             el = el.firstChild;
4190         }
4191         return el;
4192     };
4193
4194     // kill repeat to save bytes
4195     var ts = '<table>',
4196         te = '</table>',
4197         tbs = ts+'<tbody>',
4198         tbe = '</tbody>'+te,
4199         trs = tbs + '<tr>',
4200         tre = '</tr>'+tbe;
4201
4202     /**
4203      * @ignore
4204      * Nasty code for IE's broken table implementation
4205      */
4206     var insertIntoTable = function(tag, where, el, html){
4207         if(!tempTableEl){
4208             tempTableEl = document.createElement('div');
4209         }
4210         var node;
4211         var before = null;
4212         if(tag == 'td'){
4213             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4214                 return;
4215             }
4216             if(where == 'beforebegin'){
4217                 before = el;
4218                 el = el.parentNode;
4219             } else{
4220                 before = el.nextSibling;
4221                 el = el.parentNode;
4222             }
4223             node = ieTable(4, trs, html, tre);
4224         }
4225         else if(tag == 'tr'){
4226             if(where == 'beforebegin'){
4227                 before = el;
4228                 el = el.parentNode;
4229                 node = ieTable(3, tbs, html, tbe);
4230             } else if(where == 'afterend'){
4231                 before = el.nextSibling;
4232                 el = el.parentNode;
4233                 node = ieTable(3, tbs, html, tbe);
4234             } else{ // INTO a TR
4235                 if(where == 'afterbegin'){
4236                     before = el.firstChild;
4237                 }
4238                 node = ieTable(4, trs, html, tre);
4239             }
4240         } else if(tag == 'tbody'){
4241             if(where == 'beforebegin'){
4242                 before = el;
4243                 el = el.parentNode;
4244                 node = ieTable(2, ts, html, te);
4245             } else if(where == 'afterend'){
4246                 before = el.nextSibling;
4247                 el = el.parentNode;
4248                 node = ieTable(2, ts, html, te);
4249             } else{
4250                 if(where == 'afterbegin'){
4251                     before = el.firstChild;
4252                 }
4253                 node = ieTable(3, tbs, html, tbe);
4254             }
4255         } else{ // TABLE
4256             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4257                 return;
4258             }
4259             if(where == 'afterbegin'){
4260                 before = el.firstChild;
4261             }
4262             node = ieTable(2, ts, html, te);
4263         }
4264         el.insertBefore(node, before);
4265         return node;
4266     };
4267
4268     return {
4269     /** True to force the use of DOM instead of html fragments @type Boolean */
4270     useDom : false,
4271
4272     /**
4273      * Returns the markup for the passed Element(s) config
4274      * @param {Object} o The Dom object spec (and children)
4275      * @return {String}
4276      */
4277     markup : function(o){
4278         return createHtml(o);
4279     },
4280
4281     /**
4282      * Applies a style specification to an element
4283      * @param {String/HTMLElement} el The element to apply styles to
4284      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4285      * a function which returns such a specification.
4286      */
4287     applyStyles : function(el, styles){
4288         if(styles){
4289            el = Roo.fly(el);
4290            if(typeof styles == "string"){
4291                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4292                var matches;
4293                while ((matches = re.exec(styles)) != null){
4294                    el.setStyle(matches[1], matches[2]);
4295                }
4296            }else if (typeof styles == "object"){
4297                for (var style in styles){
4298                   el.setStyle(style, styles[style]);
4299                }
4300            }else if (typeof styles == "function"){
4301                 Roo.DomHelper.applyStyles(el, styles.call());
4302            }
4303         }
4304     },
4305
4306     /**
4307      * Inserts an HTML fragment into the Dom
4308      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4309      * @param {HTMLElement} el The context element
4310      * @param {String} html The HTML fragmenet
4311      * @return {HTMLElement} The new node
4312      */
4313     insertHtml : function(where, el, html){
4314         where = where.toLowerCase();
4315         if(el.insertAdjacentHTML){
4316             if(tableRe.test(el.tagName)){
4317                 var rs;
4318                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4319                     return rs;
4320                 }
4321             }
4322             switch(where){
4323                 case "beforebegin":
4324                     el.insertAdjacentHTML('BeforeBegin', html);
4325                     return el.previousSibling;
4326                 case "afterbegin":
4327                     el.insertAdjacentHTML('AfterBegin', html);
4328                     return el.firstChild;
4329                 case "beforeend":
4330                     el.insertAdjacentHTML('BeforeEnd', html);
4331                     return el.lastChild;
4332                 case "afterend":
4333                     el.insertAdjacentHTML('AfterEnd', html);
4334                     return el.nextSibling;
4335             }
4336             throw 'Illegal insertion point -> "' + where + '"';
4337         }
4338         var range = el.ownerDocument.createRange();
4339         var frag;
4340         switch(where){
4341              case "beforebegin":
4342                 range.setStartBefore(el);
4343                 frag = range.createContextualFragment(html);
4344                 el.parentNode.insertBefore(frag, el);
4345                 return el.previousSibling;
4346              case "afterbegin":
4347                 if(el.firstChild){
4348                     range.setStartBefore(el.firstChild);
4349                     frag = range.createContextualFragment(html);
4350                     el.insertBefore(frag, el.firstChild);
4351                     return el.firstChild;
4352                 }else{
4353                     el.innerHTML = html;
4354                     return el.firstChild;
4355                 }
4356             case "beforeend":
4357                 if(el.lastChild){
4358                     range.setStartAfter(el.lastChild);
4359                     frag = range.createContextualFragment(html);
4360                     el.appendChild(frag);
4361                     return el.lastChild;
4362                 }else{
4363                     el.innerHTML = html;
4364                     return el.lastChild;
4365                 }
4366             case "afterend":
4367                 range.setStartAfter(el);
4368                 frag = range.createContextualFragment(html);
4369                 el.parentNode.insertBefore(frag, el.nextSibling);
4370                 return el.nextSibling;
4371             }
4372             throw 'Illegal insertion point -> "' + where + '"';
4373     },
4374
4375     /**
4376      * Creates new Dom element(s) and inserts them before el
4377      * @param {String/HTMLElement/Element} el The context element
4378      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4379      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4380      * @return {HTMLElement/Roo.Element} The new node
4381      */
4382     insertBefore : function(el, o, returnElement){
4383         return this.doInsert(el, o, returnElement, "beforeBegin");
4384     },
4385
4386     /**
4387      * Creates new Dom element(s) and inserts them after el
4388      * @param {String/HTMLElement/Element} el The context element
4389      * @param {Object} o The Dom object spec (and children)
4390      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4391      * @return {HTMLElement/Roo.Element} The new node
4392      */
4393     insertAfter : function(el, o, returnElement){
4394         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4395     },
4396
4397     /**
4398      * Creates new Dom element(s) and inserts them as the first child of el
4399      * @param {String/HTMLElement/Element} el The context element
4400      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4401      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4402      * @return {HTMLElement/Roo.Element} The new node
4403      */
4404     insertFirst : function(el, o, returnElement){
4405         return this.doInsert(el, o, returnElement, "afterBegin");
4406     },
4407
4408     // private
4409     doInsert : function(el, o, returnElement, pos, sibling){
4410         el = Roo.getDom(el);
4411         var newNode;
4412         if(this.useDom || o.ns){
4413             newNode = createDom(o, null);
4414             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4415         }else{
4416             var html = createHtml(o);
4417             newNode = this.insertHtml(pos, el, html);
4418         }
4419         return returnElement ? Roo.get(newNode, true) : newNode;
4420     },
4421
4422     /**
4423      * Creates new Dom element(s) and appends them to el
4424      * @param {String/HTMLElement/Element} el The context element
4425      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4426      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4427      * @return {HTMLElement/Roo.Element} The new node
4428      */
4429     append : function(el, o, returnElement){
4430         el = Roo.getDom(el);
4431         var newNode;
4432         if(this.useDom || o.ns){
4433             newNode = createDom(o, null);
4434             el.appendChild(newNode);
4435         }else{
4436             var html = createHtml(o);
4437             newNode = this.insertHtml("beforeEnd", el, html);
4438         }
4439         return returnElement ? Roo.get(newNode, true) : newNode;
4440     },
4441
4442     /**
4443      * Creates new Dom element(s) and overwrites the contents of el with them
4444      * @param {String/HTMLElement/Element} el The context element
4445      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4446      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4447      * @return {HTMLElement/Roo.Element} The new node
4448      */
4449     overwrite : function(el, o, returnElement){
4450         el = Roo.getDom(el);
4451         if (o.ns) {
4452           
4453             while (el.childNodes.length) {
4454                 el.removeChild(el.firstChild);
4455             }
4456             createDom(o, el);
4457         } else {
4458             el.innerHTML = createHtml(o);   
4459         }
4460         
4461         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4462     },
4463
4464     /**
4465      * Creates a new Roo.DomHelper.Template from the Dom object spec
4466      * @param {Object} o The Dom object spec (and children)
4467      * @return {Roo.DomHelper.Template} The new template
4468      */
4469     createTemplate : function(o){
4470         var html = createHtml(o);
4471         return new Roo.Template(html);
4472     }
4473     };
4474 }();
4475 /*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485  
4486 /**
4487 * @class Roo.Template
4488 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4489 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4490 * Usage:
4491 <pre><code>
4492 var t = new Roo.Template({
4493     html :  '&lt;div name="{id}"&gt;' + 
4494         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4495         '&lt;/div&gt;',
4496     myformat: function (value, allValues) {
4497         return 'XX' + value;
4498     }
4499 });
4500 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4501 </code></pre>
4502 * 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>. 
4503 * @constructor
4504 * @param {Object} cfg - Configuration object.
4505 */
4506 Roo.Template = function(cfg){
4507     // BC!
4508     if(cfg instanceof Array){
4509         cfg = cfg.join("");
4510     }else if(arguments.length > 1){
4511         cfg = Array.prototype.join.call(arguments, "");
4512     }
4513     
4514     
4515     if (typeof(cfg) == 'object') {
4516         Roo.apply(this,cfg)
4517     } else {
4518         // bc
4519         this.html = cfg;
4520     }
4521     
4522     
4523 };
4524 Roo.Template.prototype = {
4525     
4526     /**
4527      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4528      */
4529     html : '',
4530     /**
4531      * Returns an HTML fragment of this template with the specified values applied.
4532      * @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'})
4533      * @return {String} The HTML fragment
4534      */
4535     applyTemplate : function(values){
4536         try {
4537             
4538             if(this.compiled){
4539                 return this.compiled(values);
4540             }
4541             var useF = this.disableFormats !== true;
4542             var fm = Roo.util.Format, tpl = this;
4543             var fn = function(m, name, format, args){
4544                 if(format && useF){
4545                     if(format.substr(0, 5) == "this."){
4546                         return tpl.call(format.substr(5), values[name], values);
4547                     }else{
4548                         if(args){
4549                             // quoted values are required for strings in compiled templates, 
4550                             // but for non compiled we need to strip them
4551                             // quoted reversed for jsmin
4552                             var re = /^\s*['"](.*)["']\s*$/;
4553                             args = args.split(',');
4554                             for(var i = 0, len = args.length; i < len; i++){
4555                                 args[i] = args[i].replace(re, "$1");
4556                             }
4557                             args = [values[name]].concat(args);
4558                         }else{
4559                             args = [values[name]];
4560                         }
4561                         return fm[format].apply(fm, args);
4562                     }
4563                 }else{
4564                     return values[name] !== undefined ? values[name] : "";
4565                 }
4566             };
4567             return this.html.replace(this.re, fn);
4568         } catch (e) {
4569             Roo.log(e);
4570             throw e;
4571         }
4572          
4573     },
4574     
4575     /**
4576      * Sets the HTML used as the template and optionally compiles it.
4577      * @param {String} html
4578      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4579      * @return {Roo.Template} this
4580      */
4581     set : function(html, compile){
4582         this.html = html;
4583         this.compiled = null;
4584         if(compile){
4585             this.compile();
4586         }
4587         return this;
4588     },
4589     
4590     /**
4591      * True to disable format functions (defaults to false)
4592      * @type Boolean
4593      */
4594     disableFormats : false,
4595     
4596     /**
4597     * The regular expression used to match template variables 
4598     * @type RegExp
4599     * @property 
4600     */
4601     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4602     
4603     /**
4604      * Compiles the template into an internal function, eliminating the RegEx overhead.
4605      * @return {Roo.Template} this
4606      */
4607     compile : function(){
4608         var fm = Roo.util.Format;
4609         var useF = this.disableFormats !== true;
4610         var sep = Roo.isGecko ? "+" : ",";
4611         var fn = function(m, name, format, args){
4612             if(format && useF){
4613                 args = args ? ',' + args : "";
4614                 if(format.substr(0, 5) != "this."){
4615                     format = "fm." + format + '(';
4616                 }else{
4617                     format = 'this.call("'+ format.substr(5) + '", ';
4618                     args = ", values";
4619                 }
4620             }else{
4621                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4622             }
4623             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4624         };
4625         var body;
4626         // branched to use + in gecko and [].join() in others
4627         if(Roo.isGecko){
4628             body = "this.compiled = function(values){ return '" +
4629                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4630                     "';};";
4631         }else{
4632             body = ["this.compiled = function(values){ return ['"];
4633             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4634             body.push("'].join('');};");
4635             body = body.join('');
4636         }
4637         /**
4638          * eval:var:values
4639          * eval:var:fm
4640          */
4641         eval(body);
4642         return this;
4643     },
4644     
4645     // private function used to call members
4646     call : function(fnName, value, allValues){
4647         return this[fnName](value, allValues);
4648     },
4649     
4650     /**
4651      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4652      * @param {String/HTMLElement/Roo.Element} el The context element
4653      * @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'})
4654      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4655      * @return {HTMLElement/Roo.Element} The new node or Element
4656      */
4657     insertFirst: function(el, values, returnElement){
4658         return this.doInsert('afterBegin', el, values, returnElement);
4659     },
4660
4661     /**
4662      * Applies the supplied values to the template and inserts the new node(s) before el.
4663      * @param {String/HTMLElement/Roo.Element} el The context element
4664      * @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'})
4665      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4666      * @return {HTMLElement/Roo.Element} The new node or Element
4667      */
4668     insertBefore: function(el, values, returnElement){
4669         return this.doInsert('beforeBegin', el, values, returnElement);
4670     },
4671
4672     /**
4673      * Applies the supplied values to the template and inserts the new node(s) after el.
4674      * @param {String/HTMLElement/Roo.Element} el The context element
4675      * @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'})
4676      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4677      * @return {HTMLElement/Roo.Element} The new node or Element
4678      */
4679     insertAfter : function(el, values, returnElement){
4680         return this.doInsert('afterEnd', el, values, returnElement);
4681     },
4682     
4683     /**
4684      * Applies the supplied values to the template and appends the new node(s) to el.
4685      * @param {String/HTMLElement/Roo.Element} el The context element
4686      * @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'})
4687      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4688      * @return {HTMLElement/Roo.Element} The new node or Element
4689      */
4690     append : function(el, values, returnElement){
4691         return this.doInsert('beforeEnd', el, values, returnElement);
4692     },
4693
4694     doInsert : function(where, el, values, returnEl){
4695         el = Roo.getDom(el);
4696         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4697         return returnEl ? Roo.get(newNode, true) : newNode;
4698     },
4699
4700     /**
4701      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4702      * @param {String/HTMLElement/Roo.Element} el The context element
4703      * @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'})
4704      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4705      * @return {HTMLElement/Roo.Element} The new node or Element
4706      */
4707     overwrite : function(el, values, returnElement){
4708         el = Roo.getDom(el);
4709         el.innerHTML = this.applyTemplate(values);
4710         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4711     }
4712 };
4713 /**
4714  * Alias for {@link #applyTemplate}
4715  * @method
4716  */
4717 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4718
4719 // backwards compat
4720 Roo.DomHelper.Template = Roo.Template;
4721
4722 /**
4723  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4724  * @param {String/HTMLElement} el A DOM element or its id
4725  * @returns {Roo.Template} The created template
4726  * @static
4727  */
4728 Roo.Template.from = function(el){
4729     el = Roo.getDom(el);
4730     return new Roo.Template(el.value || el.innerHTML);
4731 };/*
4732  * Based on:
4733  * Ext JS Library 1.1.1
4734  * Copyright(c) 2006-2007, Ext JS, LLC.
4735  *
4736  * Originally Released Under LGPL - original licence link has changed is not relivant.
4737  *
4738  * Fork - LGPL
4739  * <script type="text/javascript">
4740  */
4741  
4742
4743 /*
4744  * This is code is also distributed under MIT license for use
4745  * with jQuery and prototype JavaScript libraries.
4746  */
4747 /**
4748  * @class Roo.DomQuery
4749 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).
4750 <p>
4751 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>
4752
4753 <p>
4754 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.
4755 </p>
4756 <h4>Element Selectors:</h4>
4757 <ul class="list">
4758     <li> <b>*</b> any element</li>
4759     <li> <b>E</b> an element with the tag E</li>
4760     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4761     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4762     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4763     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4764 </ul>
4765 <h4>Attribute Selectors:</h4>
4766 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4767 <ul class="list">
4768     <li> <b>E[foo]</b> has an attribute "foo"</li>
4769     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4770     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4771     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4772     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4773     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4774     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4775 </ul>
4776 <h4>Pseudo Classes:</h4>
4777 <ul class="list">
4778     <li> <b>E:first-child</b> E is the first child of its parent</li>
4779     <li> <b>E:last-child</b> E is the last child of its parent</li>
4780     <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>
4781     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4782     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4783     <li> <b>E:only-child</b> E is the only child of its parent</li>
4784     <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>
4785     <li> <b>E:first</b> the first E in the resultset</li>
4786     <li> <b>E:last</b> the last E in the resultset</li>
4787     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4788     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4789     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4790     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4791     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4792     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4793     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4794     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4795     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4796 </ul>
4797 <h4>CSS Value Selectors:</h4>
4798 <ul class="list">
4799     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4800     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4801     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4802     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4803     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4804     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4805 </ul>
4806  * @singleton
4807  */
4808 Roo.DomQuery = function(){
4809     var cache = {}, simpleCache = {}, valueCache = {};
4810     var nonSpace = /\S/;
4811     var trimRe = /^\s+|\s+$/g;
4812     var tplRe = /\{(\d+)\}/g;
4813     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4814     var tagTokenRe = /^(#)?([\w-\*]+)/;
4815     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4816
4817     function child(p, index){
4818         var i = 0;
4819         var n = p.firstChild;
4820         while(n){
4821             if(n.nodeType == 1){
4822                if(++i == index){
4823                    return n;
4824                }
4825             }
4826             n = n.nextSibling;
4827         }
4828         return null;
4829     };
4830
4831     function next(n){
4832         while((n = n.nextSibling) && n.nodeType != 1);
4833         return n;
4834     };
4835
4836     function prev(n){
4837         while((n = n.previousSibling) && n.nodeType != 1);
4838         return n;
4839     };
4840
4841     function children(d){
4842         var n = d.firstChild, ni = -1;
4843             while(n){
4844                 var nx = n.nextSibling;
4845                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4846                     d.removeChild(n);
4847                 }else{
4848                     n.nodeIndex = ++ni;
4849                 }
4850                 n = nx;
4851             }
4852             return this;
4853         };
4854
4855     function byClassName(c, a, v){
4856         if(!v){
4857             return c;
4858         }
4859         var r = [], ri = -1, cn;
4860         for(var i = 0, ci; ci = c[i]; i++){
4861             if((' '+ci.className+' ').indexOf(v) != -1){
4862                 r[++ri] = ci;
4863             }
4864         }
4865         return r;
4866     };
4867
4868     function attrValue(n, attr){
4869         if(!n.tagName && typeof n.length != "undefined"){
4870             n = n[0];
4871         }
4872         if(!n){
4873             return null;
4874         }
4875         if(attr == "for"){
4876             return n.htmlFor;
4877         }
4878         if(attr == "class" || attr == "className"){
4879             return n.className;
4880         }
4881         return n.getAttribute(attr) || n[attr];
4882
4883     };
4884
4885     function getNodes(ns, mode, tagName){
4886         var result = [], ri = -1, cs;
4887         if(!ns){
4888             return result;
4889         }
4890         tagName = tagName || "*";
4891         if(typeof ns.getElementsByTagName != "undefined"){
4892             ns = [ns];
4893         }
4894         if(!mode){
4895             for(var i = 0, ni; ni = ns[i]; i++){
4896                 cs = ni.getElementsByTagName(tagName);
4897                 for(var j = 0, ci; ci = cs[j]; j++){
4898                     result[++ri] = ci;
4899                 }
4900             }
4901         }else if(mode == "/" || mode == ">"){
4902             var utag = tagName.toUpperCase();
4903             for(var i = 0, ni, cn; ni = ns[i]; i++){
4904                 cn = ni.children || ni.childNodes;
4905                 for(var j = 0, cj; cj = cn[j]; j++){
4906                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4907                         result[++ri] = cj;
4908                     }
4909                 }
4910             }
4911         }else if(mode == "+"){
4912             var utag = tagName.toUpperCase();
4913             for(var i = 0, n; n = ns[i]; i++){
4914                 while((n = n.nextSibling) && n.nodeType != 1);
4915                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4916                     result[++ri] = n;
4917                 }
4918             }
4919         }else if(mode == "~"){
4920             for(var i = 0, n; n = ns[i]; i++){
4921                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4922                 if(n){
4923                     result[++ri] = n;
4924                 }
4925             }
4926         }
4927         return result;
4928     };
4929
4930     function concat(a, b){
4931         if(b.slice){
4932             return a.concat(b);
4933         }
4934         for(var i = 0, l = b.length; i < l; i++){
4935             a[a.length] = b[i];
4936         }
4937         return a;
4938     }
4939
4940     function byTag(cs, tagName){
4941         if(cs.tagName || cs == document){
4942             cs = [cs];
4943         }
4944         if(!tagName){
4945             return cs;
4946         }
4947         var r = [], ri = -1;
4948         tagName = tagName.toLowerCase();
4949         for(var i = 0, ci; ci = cs[i]; i++){
4950             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4951                 r[++ri] = ci;
4952             }
4953         }
4954         return r;
4955     };
4956
4957     function byId(cs, attr, id){
4958         if(cs.tagName || cs == document){
4959             cs = [cs];
4960         }
4961         if(!id){
4962             return cs;
4963         }
4964         var r = [], ri = -1;
4965         for(var i = 0,ci; ci = cs[i]; i++){
4966             if(ci && ci.id == id){
4967                 r[++ri] = ci;
4968                 return r;
4969             }
4970         }
4971         return r;
4972     };
4973
4974     function byAttribute(cs, attr, value, op, custom){
4975         var r = [], ri = -1, st = custom=="{";
4976         var f = Roo.DomQuery.operators[op];
4977         for(var i = 0, ci; ci = cs[i]; i++){
4978             var a;
4979             if(st){
4980                 a = Roo.DomQuery.getStyle(ci, attr);
4981             }
4982             else if(attr == "class" || attr == "className"){
4983                 a = ci.className;
4984             }else if(attr == "for"){
4985                 a = ci.htmlFor;
4986             }else if(attr == "href"){
4987                 a = ci.getAttribute("href", 2);
4988             }else{
4989                 a = ci.getAttribute(attr);
4990             }
4991             if((f && f(a, value)) || (!f && a)){
4992                 r[++ri] = ci;
4993             }
4994         }
4995         return r;
4996     };
4997
4998     function byPseudo(cs, name, value){
4999         return Roo.DomQuery.pseudos[name](cs, value);
5000     };
5001
5002     // This is for IE MSXML which does not support expandos.
5003     // IE runs the same speed using setAttribute, however FF slows way down
5004     // and Safari completely fails so they need to continue to use expandos.
5005     var isIE = window.ActiveXObject ? true : false;
5006
5007     // this eval is stop the compressor from
5008     // renaming the variable to something shorter
5009     
5010     /** eval:var:batch */
5011     var batch = 30803; 
5012
5013     var key = 30803;
5014
5015     function nodupIEXml(cs){
5016         var d = ++key;
5017         cs[0].setAttribute("_nodup", d);
5018         var r = [cs[0]];
5019         for(var i = 1, len = cs.length; i < len; i++){
5020             var c = cs[i];
5021             if(!c.getAttribute("_nodup") != d){
5022                 c.setAttribute("_nodup", d);
5023                 r[r.length] = c;
5024             }
5025         }
5026         for(var i = 0, len = cs.length; i < len; i++){
5027             cs[i].removeAttribute("_nodup");
5028         }
5029         return r;
5030     }
5031
5032     function nodup(cs){
5033         if(!cs){
5034             return [];
5035         }
5036         var len = cs.length, c, i, r = cs, cj, ri = -1;
5037         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5038             return cs;
5039         }
5040         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5041             return nodupIEXml(cs);
5042         }
5043         var d = ++key;
5044         cs[0]._nodup = d;
5045         for(i = 1; c = cs[i]; i++){
5046             if(c._nodup != d){
5047                 c._nodup = d;
5048             }else{
5049                 r = [];
5050                 for(var j = 0; j < i; j++){
5051                     r[++ri] = cs[j];
5052                 }
5053                 for(j = i+1; cj = cs[j]; j++){
5054                     if(cj._nodup != d){
5055                         cj._nodup = d;
5056                         r[++ri] = cj;
5057                     }
5058                 }
5059                 return r;
5060             }
5061         }
5062         return r;
5063     }
5064
5065     function quickDiffIEXml(c1, c2){
5066         var d = ++key;
5067         for(var i = 0, len = c1.length; i < len; i++){
5068             c1[i].setAttribute("_qdiff", d);
5069         }
5070         var r = [];
5071         for(var i = 0, len = c2.length; i < len; i++){
5072             if(c2[i].getAttribute("_qdiff") != d){
5073                 r[r.length] = c2[i];
5074             }
5075         }
5076         for(var i = 0, len = c1.length; i < len; i++){
5077            c1[i].removeAttribute("_qdiff");
5078         }
5079         return r;
5080     }
5081
5082     function quickDiff(c1, c2){
5083         var len1 = c1.length;
5084         if(!len1){
5085             return c2;
5086         }
5087         if(isIE && c1[0].selectSingleNode){
5088             return quickDiffIEXml(c1, c2);
5089         }
5090         var d = ++key;
5091         for(var i = 0; i < len1; i++){
5092             c1[i]._qdiff = d;
5093         }
5094         var r = [];
5095         for(var i = 0, len = c2.length; i < len; i++){
5096             if(c2[i]._qdiff != d){
5097                 r[r.length] = c2[i];
5098             }
5099         }
5100         return r;
5101     }
5102
5103     function quickId(ns, mode, root, id){
5104         if(ns == root){
5105            var d = root.ownerDocument || root;
5106            return d.getElementById(id);
5107         }
5108         ns = getNodes(ns, mode, "*");
5109         return byId(ns, null, id);
5110     }
5111
5112     return {
5113         getStyle : function(el, name){
5114             return Roo.fly(el).getStyle(name);
5115         },
5116         /**
5117          * Compiles a selector/xpath query into a reusable function. The returned function
5118          * takes one parameter "root" (optional), which is the context node from where the query should start.
5119          * @param {String} selector The selector/xpath query
5120          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5121          * @return {Function}
5122          */
5123         compile : function(path, type){
5124             type = type || "select";
5125             
5126             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5127             var q = path, mode, lq;
5128             var tk = Roo.DomQuery.matchers;
5129             var tklen = tk.length;
5130             var mm;
5131
5132             // accept leading mode switch
5133             var lmode = q.match(modeRe);
5134             if(lmode && lmode[1]){
5135                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5136                 q = q.replace(lmode[1], "");
5137             }
5138             // strip leading slashes
5139             while(path.substr(0, 1)=="/"){
5140                 path = path.substr(1);
5141             }
5142
5143             while(q && lq != q){
5144                 lq = q;
5145                 var tm = q.match(tagTokenRe);
5146                 if(type == "select"){
5147                     if(tm){
5148                         if(tm[1] == "#"){
5149                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5150                         }else{
5151                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5152                         }
5153                         q = q.replace(tm[0], "");
5154                     }else if(q.substr(0, 1) != '@'){
5155                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5156                     }
5157                 }else{
5158                     if(tm){
5159                         if(tm[1] == "#"){
5160                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5161                         }else{
5162                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5163                         }
5164                         q = q.replace(tm[0], "");
5165                     }
5166                 }
5167                 while(!(mm = q.match(modeRe))){
5168                     var matched = false;
5169                     for(var j = 0; j < tklen; j++){
5170                         var t = tk[j];
5171                         var m = q.match(t.re);
5172                         if(m){
5173                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5174                                                     return m[i];
5175                                                 });
5176                             q = q.replace(m[0], "");
5177                             matched = true;
5178                             break;
5179                         }
5180                     }
5181                     // prevent infinite loop on bad selector
5182                     if(!matched){
5183                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5184                     }
5185                 }
5186                 if(mm[1]){
5187                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5188                     q = q.replace(mm[1], "");
5189                 }
5190             }
5191             fn[fn.length] = "return nodup(n);\n}";
5192             
5193              /** 
5194               * list of variables that need from compression as they are used by eval.
5195              *  eval:var:batch 
5196              *  eval:var:nodup
5197              *  eval:var:byTag
5198              *  eval:var:ById
5199              *  eval:var:getNodes
5200              *  eval:var:quickId
5201              *  eval:var:mode
5202              *  eval:var:root
5203              *  eval:var:n
5204              *  eval:var:byClassName
5205              *  eval:var:byPseudo
5206              *  eval:var:byAttribute
5207              *  eval:var:attrValue
5208              * 
5209              **/ 
5210             eval(fn.join(""));
5211             return f;
5212         },
5213
5214         /**
5215          * Selects a group of elements.
5216          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5217          * @param {Node} root (optional) The start of the query (defaults to document).
5218          * @return {Array}
5219          */
5220         select : function(path, root, type){
5221             if(!root || root == document){
5222                 root = document;
5223             }
5224             if(typeof root == "string"){
5225                 root = document.getElementById(root);
5226             }
5227             var paths = path.split(",");
5228             var results = [];
5229             for(var i = 0, len = paths.length; i < len; i++){
5230                 var p = paths[i].replace(trimRe, "");
5231                 if(!cache[p]){
5232                     cache[p] = Roo.DomQuery.compile(p);
5233                     if(!cache[p]){
5234                         throw p + " is not a valid selector";
5235                     }
5236                 }
5237                 var result = cache[p](root);
5238                 if(result && result != document){
5239                     results = results.concat(result);
5240                 }
5241             }
5242             if(paths.length > 1){
5243                 return nodup(results);
5244             }
5245             return results;
5246         },
5247
5248         /**
5249          * Selects a single element.
5250          * @param {String} selector The selector/xpath query
5251          * @param {Node} root (optional) The start of the query (defaults to document).
5252          * @return {Element}
5253          */
5254         selectNode : function(path, root){
5255             return Roo.DomQuery.select(path, root)[0];
5256         },
5257
5258         /**
5259          * Selects the value of a node, optionally replacing null with the defaultValue.
5260          * @param {String} selector The selector/xpath query
5261          * @param {Node} root (optional) The start of the query (defaults to document).
5262          * @param {String} defaultValue
5263          */
5264         selectValue : function(path, root, defaultValue){
5265             path = path.replace(trimRe, "");
5266             if(!valueCache[path]){
5267                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5268             }
5269             var n = valueCache[path](root);
5270             n = n[0] ? n[0] : n;
5271             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5272             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5273         },
5274
5275         /**
5276          * Selects the value of a node, parsing integers and floats.
5277          * @param {String} selector The selector/xpath query
5278          * @param {Node} root (optional) The start of the query (defaults to document).
5279          * @param {Number} defaultValue
5280          * @return {Number}
5281          */
5282         selectNumber : function(path, root, defaultValue){
5283             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5284             return parseFloat(v);
5285         },
5286
5287         /**
5288          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5289          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5290          * @param {String} selector The simple selector to test
5291          * @return {Boolean}
5292          */
5293         is : function(el, ss){
5294             if(typeof el == "string"){
5295                 el = document.getElementById(el);
5296             }
5297             var isArray = (el instanceof Array);
5298             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5299             return isArray ? (result.length == el.length) : (result.length > 0);
5300         },
5301
5302         /**
5303          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5304          * @param {Array} el An array of elements to filter
5305          * @param {String} selector The simple selector to test
5306          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5307          * the selector instead of the ones that match
5308          * @return {Array}
5309          */
5310         filter : function(els, ss, nonMatches){
5311             ss = ss.replace(trimRe, "");
5312             if(!simpleCache[ss]){
5313                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5314             }
5315             var result = simpleCache[ss](els);
5316             return nonMatches ? quickDiff(result, els) : result;
5317         },
5318
5319         /**
5320          * Collection of matching regular expressions and code snippets.
5321          */
5322         matchers : [{
5323                 re: /^\.([\w-]+)/,
5324                 select: 'n = byClassName(n, null, " {1} ");'
5325             }, {
5326                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5327                 select: 'n = byPseudo(n, "{1}", "{2}");'
5328             },{
5329                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5330                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5331             }, {
5332                 re: /^#([\w-]+)/,
5333                 select: 'n = byId(n, null, "{1}");'
5334             },{
5335                 re: /^@([\w-]+)/,
5336                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5337             }
5338         ],
5339
5340         /**
5341          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5342          * 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;.
5343          */
5344         operators : {
5345             "=" : function(a, v){
5346                 return a == v;
5347             },
5348             "!=" : function(a, v){
5349                 return a != v;
5350             },
5351             "^=" : function(a, v){
5352                 return a && a.substr(0, v.length) == v;
5353             },
5354             "$=" : function(a, v){
5355                 return a && a.substr(a.length-v.length) == v;
5356             },
5357             "*=" : function(a, v){
5358                 return a && a.indexOf(v) !== -1;
5359             },
5360             "%=" : function(a, v){
5361                 return (a % v) == 0;
5362             },
5363             "|=" : function(a, v){
5364                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5365             },
5366             "~=" : function(a, v){
5367                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5368             }
5369         },
5370
5371         /**
5372          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5373          * and the argument (if any) supplied in the selector.
5374          */
5375         pseudos : {
5376             "first-child" : function(c){
5377                 var r = [], ri = -1, n;
5378                 for(var i = 0, ci; ci = n = c[i]; i++){
5379                     while((n = n.previousSibling) && n.nodeType != 1);
5380                     if(!n){
5381                         r[++ri] = ci;
5382                     }
5383                 }
5384                 return r;
5385             },
5386
5387             "last-child" : function(c){
5388                 var r = [], ri = -1, n;
5389                 for(var i = 0, ci; ci = n = c[i]; i++){
5390                     while((n = n.nextSibling) && n.nodeType != 1);
5391                     if(!n){
5392                         r[++ri] = ci;
5393                     }
5394                 }
5395                 return r;
5396             },
5397
5398             "nth-child" : function(c, a) {
5399                 var r = [], ri = -1;
5400                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5401                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5402                 for(var i = 0, n; n = c[i]; i++){
5403                     var pn = n.parentNode;
5404                     if (batch != pn._batch) {
5405                         var j = 0;
5406                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5407                             if(cn.nodeType == 1){
5408                                cn.nodeIndex = ++j;
5409                             }
5410                         }
5411                         pn._batch = batch;
5412                     }
5413                     if (f == 1) {
5414                         if (l == 0 || n.nodeIndex == l){
5415                             r[++ri] = n;
5416                         }
5417                     } else if ((n.nodeIndex + l) % f == 0){
5418                         r[++ri] = n;
5419                     }
5420                 }
5421
5422                 return r;
5423             },
5424
5425             "only-child" : function(c){
5426                 var r = [], ri = -1;;
5427                 for(var i = 0, ci; ci = c[i]; i++){
5428                     if(!prev(ci) && !next(ci)){
5429                         r[++ri] = ci;
5430                     }
5431                 }
5432                 return r;
5433             },
5434
5435             "empty" : function(c){
5436                 var r = [], ri = -1;
5437                 for(var i = 0, ci; ci = c[i]; i++){
5438                     var cns = ci.childNodes, j = 0, cn, empty = true;
5439                     while(cn = cns[j]){
5440                         ++j;
5441                         if(cn.nodeType == 1 || cn.nodeType == 3){
5442                             empty = false;
5443                             break;
5444                         }
5445                     }
5446                     if(empty){
5447                         r[++ri] = ci;
5448                     }
5449                 }
5450                 return r;
5451             },
5452
5453             "contains" : function(c, v){
5454                 var r = [], ri = -1;
5455                 for(var i = 0, ci; ci = c[i]; i++){
5456                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5457                         r[++ri] = ci;
5458                     }
5459                 }
5460                 return r;
5461             },
5462
5463             "nodeValue" : function(c, v){
5464                 var r = [], ri = -1;
5465                 for(var i = 0, ci; ci = c[i]; i++){
5466                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5467                         r[++ri] = ci;
5468                     }
5469                 }
5470                 return r;
5471             },
5472
5473             "checked" : function(c){
5474                 var r = [], ri = -1;
5475                 for(var i = 0, ci; ci = c[i]; i++){
5476                     if(ci.checked == true){
5477                         r[++ri] = ci;
5478                     }
5479                 }
5480                 return r;
5481             },
5482
5483             "not" : function(c, ss){
5484                 return Roo.DomQuery.filter(c, ss, true);
5485             },
5486
5487             "odd" : function(c){
5488                 return this["nth-child"](c, "odd");
5489             },
5490
5491             "even" : function(c){
5492                 return this["nth-child"](c, "even");
5493             },
5494
5495             "nth" : function(c, a){
5496                 return c[a-1] || [];
5497             },
5498
5499             "first" : function(c){
5500                 return c[0] || [];
5501             },
5502
5503             "last" : function(c){
5504                 return c[c.length-1] || [];
5505             },
5506
5507             "has" : function(c, ss){
5508                 var s = Roo.DomQuery.select;
5509                 var r = [], ri = -1;
5510                 for(var i = 0, ci; ci = c[i]; i++){
5511                     if(s(ss, ci).length > 0){
5512                         r[++ri] = ci;
5513                     }
5514                 }
5515                 return r;
5516             },
5517
5518             "next" : function(c, ss){
5519                 var is = Roo.DomQuery.is;
5520                 var r = [], ri = -1;
5521                 for(var i = 0, ci; ci = c[i]; i++){
5522                     var n = next(ci);
5523                     if(n && is(n, ss)){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "prev" : function(c, ss){
5531                 var is = Roo.DomQuery.is;
5532                 var r = [], ri = -1;
5533                 for(var i = 0, ci; ci = c[i]; i++){
5534                     var n = prev(ci);
5535                     if(n && is(n, ss)){
5536                         r[++ri] = ci;
5537                     }
5538                 }
5539                 return r;
5540             }
5541         }
5542     };
5543 }();
5544
5545 /**
5546  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5547  * @param {String} path The selector/xpath query
5548  * @param {Node} root (optional) The start of the query (defaults to document).
5549  * @return {Array}
5550  * @member Roo
5551  * @method query
5552  */
5553 Roo.query = Roo.DomQuery.select;
5554 /*
5555  * Based on:
5556  * Ext JS Library 1.1.1
5557  * Copyright(c) 2006-2007, Ext JS, LLC.
5558  *
5559  * Originally Released Under LGPL - original licence link has changed is not relivant.
5560  *
5561  * Fork - LGPL
5562  * <script type="text/javascript">
5563  */
5564
5565 /**
5566  * @class Roo.util.Observable
5567  * Base class that provides a common interface for publishing events. Subclasses are expected to
5568  * to have a property "events" with all the events defined.<br>
5569  * For example:
5570  * <pre><code>
5571  Employee = function(name){
5572     this.name = name;
5573     this.addEvents({
5574         "fired" : true,
5575         "quit" : true
5576     });
5577  }
5578  Roo.extend(Employee, Roo.util.Observable);
5579 </code></pre>
5580  * @param {Object} config properties to use (incuding events / listeners)
5581  */
5582
5583 Roo.util.Observable = function(cfg){
5584     
5585     cfg = cfg|| {};
5586     this.addEvents(cfg.events || {});
5587     if (cfg.events) {
5588         delete cfg.events; // make sure
5589     }
5590      
5591     Roo.apply(this, cfg);
5592     
5593     if(this.listeners){
5594         this.on(this.listeners);
5595         delete this.listeners;
5596     }
5597 };
5598 Roo.util.Observable.prototype = {
5599     /** 
5600  * @cfg {Object} listeners  list of events and functions to call for this object, 
5601  * For example :
5602  * <pre><code>
5603     listeners :  { 
5604        'click' : function(e) {
5605            ..... 
5606         } ,
5607         .... 
5608     } 
5609   </code></pre>
5610  */
5611     
5612     
5613     /**
5614      * Fires the specified event with the passed parameters (minus the event name).
5615      * @param {String} eventName
5616      * @param {Object...} args Variable number of parameters are passed to handlers
5617      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5618      */
5619     fireEvent : function(){
5620         var ce = this.events[arguments[0].toLowerCase()];
5621         if(typeof ce == "object"){
5622             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5623         }else{
5624             return true;
5625         }
5626     },
5627
5628     // private
5629     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5630
5631     /**
5632      * Appends an event handler to this component
5633      * @param {String}   eventName The type of event to listen for
5634      * @param {Function} handler The method the event invokes
5635      * @param {Object}   scope (optional) The scope in which to execute the handler
5636      * function. The handler function's "this" context.
5637      * @param {Object}   options (optional) An object containing handler configuration
5638      * properties. This may contain any of the following properties:<ul>
5639      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5640      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5641      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5642      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5643      * by the specified number of milliseconds. If the event fires again within that time, the original
5644      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5645      * </ul><br>
5646      * <p>
5647      * <b>Combining Options</b><br>
5648      * Using the options argument, it is possible to combine different types of listeners:<br>
5649      * <br>
5650      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5651                 <pre><code>
5652                 el.on('click', this.onClick, this, {
5653                         single: true,
5654                 delay: 100,
5655                 forumId: 4
5656                 });
5657                 </code></pre>
5658      * <p>
5659      * <b>Attaching multiple handlers in 1 call</b><br>
5660      * The method also allows for a single argument to be passed which is a config object containing properties
5661      * which specify multiple handlers.
5662      * <pre><code>
5663                 el.on({
5664                         'click': {
5665                         fn: this.onClick,
5666                         scope: this,
5667                         delay: 100
5668                 }, 
5669                 'mouseover': {
5670                         fn: this.onMouseOver,
5671                         scope: this
5672                 },
5673                 'mouseout': {
5674                         fn: this.onMouseOut,
5675                         scope: this
5676                 }
5677                 });
5678                 </code></pre>
5679      * <p>
5680      * Or a shorthand syntax which passes the same scope object to all handlers:
5681         <pre><code>
5682                 el.on({
5683                         'click': this.onClick,
5684                 'mouseover': this.onMouseOver,
5685                 'mouseout': this.onMouseOut,
5686                 scope: this
5687                 });
5688                 </code></pre>
5689      */
5690     addListener : function(eventName, fn, scope, o){
5691         if(typeof eventName == "object"){
5692             o = eventName;
5693             for(var e in o){
5694                 if(this.filterOptRe.test(e)){
5695                     continue;
5696                 }
5697                 if(typeof o[e] == "function"){
5698                     // shared options
5699                     this.addListener(e, o[e], o.scope,  o);
5700                 }else{
5701                     // individual options
5702                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5703                 }
5704             }
5705             return;
5706         }
5707         o = (!o || typeof o == "boolean") ? {} : o;
5708         eventName = eventName.toLowerCase();
5709         var ce = this.events[eventName] || true;
5710         if(typeof ce == "boolean"){
5711             ce = new Roo.util.Event(this, eventName);
5712             this.events[eventName] = ce;
5713         }
5714         ce.addListener(fn, scope, o);
5715     },
5716
5717     /**
5718      * Removes a listener
5719      * @param {String}   eventName     The type of event to listen for
5720      * @param {Function} handler        The handler to remove
5721      * @param {Object}   scope  (optional) The scope (this object) for the handler
5722      */
5723     removeListener : function(eventName, fn, scope){
5724         var ce = this.events[eventName.toLowerCase()];
5725         if(typeof ce == "object"){
5726             ce.removeListener(fn, scope);
5727         }
5728     },
5729
5730     /**
5731      * Removes all listeners for this object
5732      */
5733     purgeListeners : function(){
5734         for(var evt in this.events){
5735             if(typeof this.events[evt] == "object"){
5736                  this.events[evt].clearListeners();
5737             }
5738         }
5739     },
5740
5741     relayEvents : function(o, events){
5742         var createHandler = function(ename){
5743             return function(){
5744                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5745             };
5746         };
5747         for(var i = 0, len = events.length; i < len; i++){
5748             var ename = events[i];
5749             if(!this.events[ename]){ this.events[ename] = true; };
5750             o.on(ename, createHandler(ename), this);
5751         }
5752     },
5753
5754     /**
5755      * Used to define events on this Observable
5756      * @param {Object} object The object with the events defined
5757      */
5758     addEvents : function(o){
5759         if(!this.events){
5760             this.events = {};
5761         }
5762         Roo.applyIf(this.events, o);
5763     },
5764
5765     /**
5766      * Checks to see if this object has any listeners for a specified event
5767      * @param {String} eventName The name of the event to check for
5768      * @return {Boolean} True if the event is being listened for, else false
5769      */
5770     hasListener : function(eventName){
5771         var e = this.events[eventName];
5772         return typeof e == "object" && e.listeners.length > 0;
5773     }
5774 };
5775 /**
5776  * Appends an event handler to this element (shorthand for addListener)
5777  * @param {String}   eventName     The type of event to listen for
5778  * @param {Function} handler        The method the event invokes
5779  * @param {Object}   scope (optional) The scope in which to execute the handler
5780  * function. The handler function's "this" context.
5781  * @param {Object}   options  (optional)
5782  * @method
5783  */
5784 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5785 /**
5786  * Removes a listener (shorthand for removeListener)
5787  * @param {String}   eventName     The type of event to listen for
5788  * @param {Function} handler        The handler to remove
5789  * @param {Object}   scope  (optional) The scope (this object) for the handler
5790  * @method
5791  */
5792 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5793
5794 /**
5795  * Starts capture on the specified Observable. All events will be passed
5796  * to the supplied function with the event name + standard signature of the event
5797  * <b>before</b> the event is fired. If the supplied function returns false,
5798  * the event will not fire.
5799  * @param {Observable} o The Observable to capture
5800  * @param {Function} fn The function to call
5801  * @param {Object} scope (optional) The scope (this object) for the fn
5802  * @static
5803  */
5804 Roo.util.Observable.capture = function(o, fn, scope){
5805     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5806 };
5807
5808 /**
5809  * Removes <b>all</b> added captures from the Observable.
5810  * @param {Observable} o The Observable to release
5811  * @static
5812  */
5813 Roo.util.Observable.releaseCapture = function(o){
5814     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5815 };
5816
5817 (function(){
5818
5819     var createBuffered = function(h, o, scope){
5820         var task = new Roo.util.DelayedTask();
5821         return function(){
5822             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5823         };
5824     };
5825
5826     var createSingle = function(h, e, fn, scope){
5827         return function(){
5828             e.removeListener(fn, scope);
5829             return h.apply(scope, arguments);
5830         };
5831     };
5832
5833     var createDelayed = function(h, o, scope){
5834         return function(){
5835             var args = Array.prototype.slice.call(arguments, 0);
5836             setTimeout(function(){
5837                 h.apply(scope, args);
5838             }, o.delay || 10);
5839         };
5840     };
5841
5842     Roo.util.Event = function(obj, name){
5843         this.name = name;
5844         this.obj = obj;
5845         this.listeners = [];
5846     };
5847
5848     Roo.util.Event.prototype = {
5849         addListener : function(fn, scope, options){
5850             var o = options || {};
5851             scope = scope || this.obj;
5852             if(!this.isListening(fn, scope)){
5853                 var l = {fn: fn, scope: scope, options: o};
5854                 var h = fn;
5855                 if(o.delay){
5856                     h = createDelayed(h, o, scope);
5857                 }
5858                 if(o.single){
5859                     h = createSingle(h, this, fn, scope);
5860                 }
5861                 if(o.buffer){
5862                     h = createBuffered(h, o, scope);
5863                 }
5864                 l.fireFn = h;
5865                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5866                     this.listeners.push(l);
5867                 }else{
5868                     this.listeners = this.listeners.slice(0);
5869                     this.listeners.push(l);
5870                 }
5871             }
5872         },
5873
5874         findListener : function(fn, scope){
5875             scope = scope || this.obj;
5876             var ls = this.listeners;
5877             for(var i = 0, len = ls.length; i < len; i++){
5878                 var l = ls[i];
5879                 if(l.fn == fn && l.scope == scope){
5880                     return i;
5881                 }
5882             }
5883             return -1;
5884         },
5885
5886         isListening : function(fn, scope){
5887             return this.findListener(fn, scope) != -1;
5888         },
5889
5890         removeListener : function(fn, scope){
5891             var index;
5892             if((index = this.findListener(fn, scope)) != -1){
5893                 if(!this.firing){
5894                     this.listeners.splice(index, 1);
5895                 }else{
5896                     this.listeners = this.listeners.slice(0);
5897                     this.listeners.splice(index, 1);
5898                 }
5899                 return true;
5900             }
5901             return false;
5902         },
5903
5904         clearListeners : function(){
5905             this.listeners = [];
5906         },
5907
5908         fire : function(){
5909             var ls = this.listeners, scope, len = ls.length;
5910             if(len > 0){
5911                 this.firing = true;
5912                 var args = Array.prototype.slice.call(arguments, 0);
5913                 for(var i = 0; i < len; i++){
5914                     var l = ls[i];
5915                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5916                         this.firing = false;
5917                         return false;
5918                     }
5919                 }
5920                 this.firing = false;
5921             }
5922             return true;
5923         }
5924     };
5925 })();/*
5926  * Based on:
5927  * Ext JS Library 1.1.1
5928  * Copyright(c) 2006-2007, Ext JS, LLC.
5929  *
5930  * Originally Released Under LGPL - original licence link has changed is not relivant.
5931  *
5932  * Fork - LGPL
5933  * <script type="text/javascript">
5934  */
5935
5936 /**
5937  * @class Roo.EventManager
5938  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5939  * several useful events directly.
5940  * See {@link Roo.EventObject} for more details on normalized event objects.
5941  * @singleton
5942  */
5943 Roo.EventManager = function(){
5944     var docReadyEvent, docReadyProcId, docReadyState = false;
5945     var resizeEvent, resizeTask, textEvent, textSize;
5946     var E = Roo.lib.Event;
5947     var D = Roo.lib.Dom;
5948
5949
5950     var fireDocReady = function(){
5951         if(!docReadyState){
5952             docReadyState = true;
5953             Roo.isReady = true;
5954             if(docReadyProcId){
5955                 clearInterval(docReadyProcId);
5956             }
5957             if(Roo.isGecko || Roo.isOpera) {
5958                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5959             }
5960             if(Roo.isIE){
5961                 var defer = document.getElementById("ie-deferred-loader");
5962                 if(defer){
5963                     defer.onreadystatechange = null;
5964                     defer.parentNode.removeChild(defer);
5965                 }
5966             }
5967             if(docReadyEvent){
5968                 docReadyEvent.fire();
5969                 docReadyEvent.clearListeners();
5970             }
5971         }
5972     };
5973     
5974     var initDocReady = function(){
5975         docReadyEvent = new Roo.util.Event();
5976         if(Roo.isGecko || Roo.isOpera) {
5977             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5978         }else if(Roo.isIE){
5979             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5980             var defer = document.getElementById("ie-deferred-loader");
5981             defer.onreadystatechange = function(){
5982                 if(this.readyState == "complete"){
5983                     fireDocReady();
5984                 }
5985             };
5986         }else if(Roo.isSafari){ 
5987             docReadyProcId = setInterval(function(){
5988                 var rs = document.readyState;
5989                 if(rs == "complete") {
5990                     fireDocReady();     
5991                  }
5992             }, 10);
5993         }
5994         // no matter what, make sure it fires on load
5995         E.on(window, "load", fireDocReady);
5996     };
5997
5998     var createBuffered = function(h, o){
5999         var task = new Roo.util.DelayedTask(h);
6000         return function(e){
6001             // create new event object impl so new events don't wipe out properties
6002             e = new Roo.EventObjectImpl(e);
6003             task.delay(o.buffer, h, null, [e]);
6004         };
6005     };
6006
6007     var createSingle = function(h, el, ename, fn){
6008         return function(e){
6009             Roo.EventManager.removeListener(el, ename, fn);
6010             h(e);
6011         };
6012     };
6013
6014     var createDelayed = function(h, o){
6015         return function(e){
6016             // create new event object impl so new events don't wipe out properties
6017             e = new Roo.EventObjectImpl(e);
6018             setTimeout(function(){
6019                 h(e);
6020             }, o.delay || 10);
6021         };
6022     };
6023
6024     var listen = function(element, ename, opt, fn, scope){
6025         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6026         fn = fn || o.fn; scope = scope || o.scope;
6027         var el = Roo.getDom(element);
6028         if(!el){
6029             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6030         }
6031         var h = function(e){
6032             e = Roo.EventObject.setEvent(e);
6033             var t;
6034             if(o.delegate){
6035                 t = e.getTarget(o.delegate, el);
6036                 if(!t){
6037                     return;
6038                 }
6039             }else{
6040                 t = e.target;
6041             }
6042             if(o.stopEvent === true){
6043                 e.stopEvent();
6044             }
6045             if(o.preventDefault === true){
6046                e.preventDefault();
6047             }
6048             if(o.stopPropagation === true){
6049                 e.stopPropagation();
6050             }
6051
6052             if(o.normalized === false){
6053                 e = e.browserEvent;
6054             }
6055
6056             fn.call(scope || el, e, t, o);
6057         };
6058         if(o.delay){
6059             h = createDelayed(h, o);
6060         }
6061         if(o.single){
6062             h = createSingle(h, el, ename, fn);
6063         }
6064         if(o.buffer){
6065             h = createBuffered(h, o);
6066         }
6067         fn._handlers = fn._handlers || [];
6068         fn._handlers.push([Roo.id(el), ename, h]);
6069
6070         E.on(el, ename, h);
6071         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6072             el.addEventListener("DOMMouseScroll", h, false);
6073             E.on(window, 'unload', function(){
6074                 el.removeEventListener("DOMMouseScroll", h, false);
6075             });
6076         }
6077         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6078             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6079         }
6080         return h;
6081     };
6082
6083     var stopListening = function(el, ename, fn){
6084         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6085         if(hds){
6086             for(var i = 0, len = hds.length; i < len; i++){
6087                 var h = hds[i];
6088                 if(h[0] == id && h[1] == ename){
6089                     hd = h[2];
6090                     hds.splice(i, 1);
6091                     break;
6092                 }
6093             }
6094         }
6095         E.un(el, ename, hd);
6096         el = Roo.getDom(el);
6097         if(ename == "mousewheel" && el.addEventListener){
6098             el.removeEventListener("DOMMouseScroll", hd, false);
6099         }
6100         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6101             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6102         }
6103     };
6104
6105     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6106     
6107     var pub = {
6108         
6109         
6110         /** 
6111          * Fix for doc tools
6112          * @scope Roo.EventManager
6113          */
6114         
6115         
6116         /** 
6117          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6118          * object with a Roo.EventObject
6119          * @param {Function} fn        The method the event invokes
6120          * @param {Object}   scope    An object that becomes the scope of the handler
6121          * @param {boolean}  override If true, the obj passed in becomes
6122          *                             the execution scope of the listener
6123          * @return {Function} The wrapped function
6124          * @deprecated
6125          */
6126         wrap : function(fn, scope, override){
6127             return function(e){
6128                 Roo.EventObject.setEvent(e);
6129                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6130             };
6131         },
6132         
6133         /**
6134      * Appends an event handler to an element (shorthand for addListener)
6135      * @param {String/HTMLElement}   element        The html element or id to assign the
6136      * @param {String}   eventName The type of event to listen for
6137      * @param {Function} handler The method the event invokes
6138      * @param {Object}   scope (optional) The scope in which to execute the handler
6139      * function. The handler function's "this" context.
6140      * @param {Object}   options (optional) An object containing handler configuration
6141      * properties. This may contain any of the following properties:<ul>
6142      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6143      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6144      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6145      * <li>preventDefault {Boolean} True to prevent the default action</li>
6146      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6147      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6148      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6149      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6150      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6151      * by the specified number of milliseconds. If the event fires again within that time, the original
6152      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6153      * </ul><br>
6154      * <p>
6155      * <b>Combining Options</b><br>
6156      * Using the options argument, it is possible to combine different types of listeners:<br>
6157      * <br>
6158      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6159      * Code:<pre><code>
6160 el.on('click', this.onClick, this, {
6161     single: true,
6162     delay: 100,
6163     stopEvent : true,
6164     forumId: 4
6165 });</code></pre>
6166      * <p>
6167      * <b>Attaching multiple handlers in 1 call</b><br>
6168       * The method also allows for a single argument to be passed which is a config object containing properties
6169      * which specify multiple handlers.
6170      * <p>
6171      * Code:<pre><code>
6172 el.on({
6173     'click' : {
6174         fn: this.onClick
6175         scope: this,
6176         delay: 100
6177     },
6178     'mouseover' : {
6179         fn: this.onMouseOver
6180         scope: this
6181     },
6182     'mouseout' : {
6183         fn: this.onMouseOut
6184         scope: this
6185     }
6186 });</code></pre>
6187      * <p>
6188      * Or a shorthand syntax:<br>
6189      * Code:<pre><code>
6190 el.on({
6191     'click' : this.onClick,
6192     'mouseover' : this.onMouseOver,
6193     'mouseout' : this.onMouseOut
6194     scope: this
6195 });</code></pre>
6196      */
6197         addListener : function(element, eventName, fn, scope, options){
6198             if(typeof eventName == "object"){
6199                 var o = eventName;
6200                 for(var e in o){
6201                     if(propRe.test(e)){
6202                         continue;
6203                     }
6204                     if(typeof o[e] == "function"){
6205                         // shared options
6206                         listen(element, e, o, o[e], o.scope);
6207                     }else{
6208                         // individual options
6209                         listen(element, e, o[e]);
6210                     }
6211                 }
6212                 return;
6213             }
6214             return listen(element, eventName, options, fn, scope);
6215         },
6216         
6217         /**
6218          * Removes an event handler
6219          *
6220          * @param {String/HTMLElement}   element        The id or html element to remove the 
6221          *                             event from
6222          * @param {String}   eventName     The type of event
6223          * @param {Function} fn
6224          * @return {Boolean} True if a listener was actually removed
6225          */
6226         removeListener : function(element, eventName, fn){
6227             return stopListening(element, eventName, fn);
6228         },
6229         
6230         /**
6231          * Fires when the document is ready (before onload and before images are loaded). Can be 
6232          * accessed shorthanded Roo.onReady().
6233          * @param {Function} fn        The method the event invokes
6234          * @param {Object}   scope    An  object that becomes the scope of the handler
6235          * @param {boolean}  options
6236          */
6237         onDocumentReady : function(fn, scope, options){
6238             if(docReadyState){ // if it already fired
6239                 docReadyEvent.addListener(fn, scope, options);
6240                 docReadyEvent.fire();
6241                 docReadyEvent.clearListeners();
6242                 return;
6243             }
6244             if(!docReadyEvent){
6245                 initDocReady();
6246             }
6247             docReadyEvent.addListener(fn, scope, options);
6248         },
6249         
6250         /**
6251          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6252          * @param {Function} fn        The method the event invokes
6253          * @param {Object}   scope    An object that becomes the scope of the handler
6254          * @param {boolean}  options
6255          */
6256         onWindowResize : function(fn, scope, options){
6257             if(!resizeEvent){
6258                 resizeEvent = new Roo.util.Event();
6259                 resizeTask = new Roo.util.DelayedTask(function(){
6260                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6261                 });
6262                 E.on(window, "resize", function(){
6263                     if(Roo.isIE){
6264                         resizeTask.delay(50);
6265                     }else{
6266                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6267                     }
6268                 });
6269             }
6270             resizeEvent.addListener(fn, scope, options);
6271         },
6272
6273         /**
6274          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6275          * @param {Function} fn        The method the event invokes
6276          * @param {Object}   scope    An object that becomes the scope of the handler
6277          * @param {boolean}  options
6278          */
6279         onTextResize : function(fn, scope, options){
6280             if(!textEvent){
6281                 textEvent = new Roo.util.Event();
6282                 var textEl = new Roo.Element(document.createElement('div'));
6283                 textEl.dom.className = 'x-text-resize';
6284                 textEl.dom.innerHTML = 'X';
6285                 textEl.appendTo(document.body);
6286                 textSize = textEl.dom.offsetHeight;
6287                 setInterval(function(){
6288                     if(textEl.dom.offsetHeight != textSize){
6289                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6290                     }
6291                 }, this.textResizeInterval);
6292             }
6293             textEvent.addListener(fn, scope, options);
6294         },
6295
6296         /**
6297          * Removes the passed window resize listener.
6298          * @param {Function} fn        The method the event invokes
6299          * @param {Object}   scope    The scope of handler
6300          */
6301         removeResizeListener : function(fn, scope){
6302             if(resizeEvent){
6303                 resizeEvent.removeListener(fn, scope);
6304             }
6305         },
6306
6307         // private
6308         fireResize : function(){
6309             if(resizeEvent){
6310                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6311             }   
6312         },
6313         /**
6314          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6315          */
6316         ieDeferSrc : false,
6317         /**
6318          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6319          */
6320         textResizeInterval : 50
6321     };
6322     
6323     /**
6324      * Fix for doc tools
6325      * @scopeAlias pub=Roo.EventManager
6326      */
6327     
6328      /**
6329      * Appends an event handler to an element (shorthand for addListener)
6330      * @param {String/HTMLElement}   element        The html element or id to assign the
6331      * @param {String}   eventName The type of event to listen for
6332      * @param {Function} handler The method the event invokes
6333      * @param {Object}   scope (optional) The scope in which to execute the handler
6334      * function. The handler function's "this" context.
6335      * @param {Object}   options (optional) An object containing handler configuration
6336      * properties. This may contain any of the following properties:<ul>
6337      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6338      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6339      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6340      * <li>preventDefault {Boolean} True to prevent the default action</li>
6341      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6342      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6343      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6344      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6345      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6346      * by the specified number of milliseconds. If the event fires again within that time, the original
6347      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6348      * </ul><br>
6349      * <p>
6350      * <b>Combining Options</b><br>
6351      * Using the options argument, it is possible to combine different types of listeners:<br>
6352      * <br>
6353      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6354      * Code:<pre><code>
6355 el.on('click', this.onClick, this, {
6356     single: true,
6357     delay: 100,
6358     stopEvent : true,
6359     forumId: 4
6360 });</code></pre>
6361      * <p>
6362      * <b>Attaching multiple handlers in 1 call</b><br>
6363       * The method also allows for a single argument to be passed which is a config object containing properties
6364      * which specify multiple handlers.
6365      * <p>
6366      * Code:<pre><code>
6367 el.on({
6368     'click' : {
6369         fn: this.onClick
6370         scope: this,
6371         delay: 100
6372     },
6373     'mouseover' : {
6374         fn: this.onMouseOver
6375         scope: this
6376     },
6377     'mouseout' : {
6378         fn: this.onMouseOut
6379         scope: this
6380     }
6381 });</code></pre>
6382      * <p>
6383      * Or a shorthand syntax:<br>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : this.onClick,
6387     'mouseover' : this.onMouseOver,
6388     'mouseout' : this.onMouseOut
6389     scope: this
6390 });</code></pre>
6391      */
6392     pub.on = pub.addListener;
6393     pub.un = pub.removeListener;
6394
6395     pub.stoppedMouseDownEvent = new Roo.util.Event();
6396     return pub;
6397 }();
6398 /**
6399   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6400   * @param {Function} fn        The method the event invokes
6401   * @param {Object}   scope    An  object that becomes the scope of the handler
6402   * @param {boolean}  override If true, the obj passed in becomes
6403   *                             the execution scope of the listener
6404   * @member Roo
6405   * @method onReady
6406  */
6407 Roo.onReady = Roo.EventManager.onDocumentReady;
6408
6409 Roo.onReady(function(){
6410     var bd = Roo.get(document.body);
6411     if(!bd){ return; }
6412
6413     var cls = [
6414             Roo.isIE ? "roo-ie"
6415             : Roo.isGecko ? "roo-gecko"
6416             : Roo.isOpera ? "roo-opera"
6417             : Roo.isSafari ? "roo-safari" : ""];
6418
6419     if(Roo.isMac){
6420         cls.push("roo-mac");
6421     }
6422     if(Roo.isLinux){
6423         cls.push("roo-linux");
6424     }
6425     if(Roo.isBorderBox){
6426         cls.push('roo-border-box');
6427     }
6428     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6429         var p = bd.dom.parentNode;
6430         if(p){
6431             p.className += ' roo-strict';
6432         }
6433     }
6434     bd.addClass(cls.join(' '));
6435 });
6436
6437 /**
6438  * @class Roo.EventObject
6439  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6440  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6441  * Example:
6442  * <pre><code>
6443  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6444     e.preventDefault();
6445     var target = e.getTarget();
6446     ...
6447  }
6448  var myDiv = Roo.get("myDiv");
6449  myDiv.on("click", handleClick);
6450  //or
6451  Roo.EventManager.on("myDiv", 'click', handleClick);
6452  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6453  </code></pre>
6454  * @singleton
6455  */
6456 Roo.EventObject = function(){
6457     
6458     var E = Roo.lib.Event;
6459     
6460     // safari keypress events for special keys return bad keycodes
6461     var safariKeys = {
6462         63234 : 37, // left
6463         63235 : 39, // right
6464         63232 : 38, // up
6465         63233 : 40, // down
6466         63276 : 33, // page up
6467         63277 : 34, // page down
6468         63272 : 46, // delete
6469         63273 : 36, // home
6470         63275 : 35  // end
6471     };
6472
6473     // normalize button clicks
6474     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6475                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6476
6477     Roo.EventObjectImpl = function(e){
6478         if(e){
6479             this.setEvent(e.browserEvent || e);
6480         }
6481     };
6482     Roo.EventObjectImpl.prototype = {
6483         /**
6484          * Used to fix doc tools.
6485          * @scope Roo.EventObject.prototype
6486          */
6487             
6488
6489         
6490         
6491         /** The normal browser event */
6492         browserEvent : null,
6493         /** The button pressed in a mouse event */
6494         button : -1,
6495         /** True if the shift key was down during the event */
6496         shiftKey : false,
6497         /** True if the control key was down during the event */
6498         ctrlKey : false,
6499         /** True if the alt key was down during the event */
6500         altKey : false,
6501
6502         /** Key constant 
6503         * @type Number */
6504         BACKSPACE : 8,
6505         /** Key constant 
6506         * @type Number */
6507         TAB : 9,
6508         /** Key constant 
6509         * @type Number */
6510         RETURN : 13,
6511         /** Key constant 
6512         * @type Number */
6513         ENTER : 13,
6514         /** Key constant 
6515         * @type Number */
6516         SHIFT : 16,
6517         /** Key constant 
6518         * @type Number */
6519         CONTROL : 17,
6520         /** Key constant 
6521         * @type Number */
6522         ESC : 27,
6523         /** Key constant 
6524         * @type Number */
6525         SPACE : 32,
6526         /** Key constant 
6527         * @type Number */
6528         PAGEUP : 33,
6529         /** Key constant 
6530         * @type Number */
6531         PAGEDOWN : 34,
6532         /** Key constant 
6533         * @type Number */
6534         END : 35,
6535         /** Key constant 
6536         * @type Number */
6537         HOME : 36,
6538         /** Key constant 
6539         * @type Number */
6540         LEFT : 37,
6541         /** Key constant 
6542         * @type Number */
6543         UP : 38,
6544         /** Key constant 
6545         * @type Number */
6546         RIGHT : 39,
6547         /** Key constant 
6548         * @type Number */
6549         DOWN : 40,
6550         /** Key constant 
6551         * @type Number */
6552         DELETE : 46,
6553         /** Key constant 
6554         * @type Number */
6555         F5 : 116,
6556
6557            /** @private */
6558         setEvent : function(e){
6559             if(e == this || (e && e.browserEvent)){ // already wrapped
6560                 return e;
6561             }
6562             this.browserEvent = e;
6563             if(e){
6564                 // normalize buttons
6565                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6566                 if(e.type == 'click' && this.button == -1){
6567                     this.button = 0;
6568                 }
6569                 this.type = e.type;
6570                 this.shiftKey = e.shiftKey;
6571                 // mac metaKey behaves like ctrlKey
6572                 this.ctrlKey = e.ctrlKey || e.metaKey;
6573                 this.altKey = e.altKey;
6574                 // in getKey these will be normalized for the mac
6575                 this.keyCode = e.keyCode;
6576                 // keyup warnings on firefox.
6577                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6578                 // cache the target for the delayed and or buffered events
6579                 this.target = E.getTarget(e);
6580                 // same for XY
6581                 this.xy = E.getXY(e);
6582             }else{
6583                 this.button = -1;
6584                 this.shiftKey = false;
6585                 this.ctrlKey = false;
6586                 this.altKey = false;
6587                 this.keyCode = 0;
6588                 this.charCode =0;
6589                 this.target = null;
6590                 this.xy = [0, 0];
6591             }
6592             return this;
6593         },
6594
6595         /**
6596          * Stop the event (preventDefault and stopPropagation)
6597          */
6598         stopEvent : function(){
6599             if(this.browserEvent){
6600                 if(this.browserEvent.type == 'mousedown'){
6601                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6602                 }
6603                 E.stopEvent(this.browserEvent);
6604             }
6605         },
6606
6607         /**
6608          * Prevents the browsers default handling of the event.
6609          */
6610         preventDefault : function(){
6611             if(this.browserEvent){
6612                 E.preventDefault(this.browserEvent);
6613             }
6614         },
6615
6616         /** @private */
6617         isNavKeyPress : function(){
6618             var k = this.keyCode;
6619             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6620             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6621         },
6622
6623         isSpecialKey : function(){
6624             var k = this.keyCode;
6625             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6626             (k == 16) || (k == 17) ||
6627             (k >= 18 && k <= 20) ||
6628             (k >= 33 && k <= 35) ||
6629             (k >= 36 && k <= 39) ||
6630             (k >= 44 && k <= 45);
6631         },
6632         /**
6633          * Cancels bubbling of the event.
6634          */
6635         stopPropagation : function(){
6636             if(this.browserEvent){
6637                 if(this.type == 'mousedown'){
6638                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6639                 }
6640                 E.stopPropagation(this.browserEvent);
6641             }
6642         },
6643
6644         /**
6645          * Gets the key code for the event.
6646          * @return {Number}
6647          */
6648         getCharCode : function(){
6649             return this.charCode || this.keyCode;
6650         },
6651
6652         /**
6653          * Returns a normalized keyCode for the event.
6654          * @return {Number} The key code
6655          */
6656         getKey : function(){
6657             var k = this.keyCode || this.charCode;
6658             return Roo.isSafari ? (safariKeys[k] || k) : k;
6659         },
6660
6661         /**
6662          * Gets the x coordinate of the event.
6663          * @return {Number}
6664          */
6665         getPageX : function(){
6666             return this.xy[0];
6667         },
6668
6669         /**
6670          * Gets the y coordinate of the event.
6671          * @return {Number}
6672          */
6673         getPageY : function(){
6674             return this.xy[1];
6675         },
6676
6677         /**
6678          * Gets the time of the event.
6679          * @return {Number}
6680          */
6681         getTime : function(){
6682             if(this.browserEvent){
6683                 return E.getTime(this.browserEvent);
6684             }
6685             return null;
6686         },
6687
6688         /**
6689          * Gets the page coordinates of the event.
6690          * @return {Array} The xy values like [x, y]
6691          */
6692         getXY : function(){
6693             return this.xy;
6694         },
6695
6696         /**
6697          * Gets the target for the event.
6698          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6699          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6700                 search as a number or element (defaults to 10 || document.body)
6701          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6702          * @return {HTMLelement}
6703          */
6704         getTarget : function(selector, maxDepth, returnEl){
6705             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6706         },
6707         /**
6708          * Gets the related target.
6709          * @return {HTMLElement}
6710          */
6711         getRelatedTarget : function(){
6712             if(this.browserEvent){
6713                 return E.getRelatedTarget(this.browserEvent);
6714             }
6715             return null;
6716         },
6717
6718         /**
6719          * Normalizes mouse wheel delta across browsers
6720          * @return {Number} The delta
6721          */
6722         getWheelDelta : function(){
6723             var e = this.browserEvent;
6724             var delta = 0;
6725             if(e.wheelDelta){ /* IE/Opera. */
6726                 delta = e.wheelDelta/120;
6727             }else if(e.detail){ /* Mozilla case. */
6728                 delta = -e.detail/3;
6729             }
6730             return delta;
6731         },
6732
6733         /**
6734          * Returns true if the control, meta, shift or alt key was pressed during this event.
6735          * @return {Boolean}
6736          */
6737         hasModifier : function(){
6738             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6739         },
6740
6741         /**
6742          * Returns true if the target of this event equals el or is a child of el
6743          * @param {String/HTMLElement/Element} el
6744          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6745          * @return {Boolean}
6746          */
6747         within : function(el, related){
6748             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6749             return t && Roo.fly(el).contains(t);
6750         },
6751
6752         getPoint : function(){
6753             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6754         }
6755     };
6756
6757     return new Roo.EventObjectImpl();
6758 }();
6759             
6760     /*
6761  * Based on:
6762  * Ext JS Library 1.1.1
6763  * Copyright(c) 2006-2007, Ext JS, LLC.
6764  *
6765  * Originally Released Under LGPL - original licence link has changed is not relivant.
6766  *
6767  * Fork - LGPL
6768  * <script type="text/javascript">
6769  */
6770
6771  
6772 // was in Composite Element!??!?!
6773  
6774 (function(){
6775     var D = Roo.lib.Dom;
6776     var E = Roo.lib.Event;
6777     var A = Roo.lib.Anim;
6778
6779     // local style camelizing for speed
6780     var propCache = {};
6781     var camelRe = /(-[a-z])/gi;
6782     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6783     var view = document.defaultView;
6784
6785 /**
6786  * @class Roo.Element
6787  * Represents an Element in the DOM.<br><br>
6788  * Usage:<br>
6789 <pre><code>
6790 var el = Roo.get("my-div");
6791
6792 // or with getEl
6793 var el = getEl("my-div");
6794
6795 // or with a DOM element
6796 var el = Roo.get(myDivElement);
6797 </code></pre>
6798  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6799  * each call instead of constructing a new one.<br><br>
6800  * <b>Animations</b><br />
6801  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6802  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6803 <pre>
6804 Option    Default   Description
6805 --------- --------  ---------------------------------------------
6806 duration  .35       The duration of the animation in seconds
6807 easing    easeOut   The YUI easing method
6808 callback  none      A function to execute when the anim completes
6809 scope     this      The scope (this) of the callback function
6810 </pre>
6811 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6812 * manipulate the animation. Here's an example:
6813 <pre><code>
6814 var el = Roo.get("my-div");
6815
6816 // no animation
6817 el.setWidth(100);
6818
6819 // default animation
6820 el.setWidth(100, true);
6821
6822 // animation with some options set
6823 el.setWidth(100, {
6824     duration: 1,
6825     callback: this.foo,
6826     scope: this
6827 });
6828
6829 // using the "anim" property to get the Anim object
6830 var opt = {
6831     duration: 1,
6832     callback: this.foo,
6833     scope: this
6834 };
6835 el.setWidth(100, opt);
6836 ...
6837 if(opt.anim.isAnimated()){
6838     opt.anim.stop();
6839 }
6840 </code></pre>
6841 * <b> Composite (Collections of) Elements</b><br />
6842  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6843  * @constructor Create a new Element directly.
6844  * @param {String/HTMLElement} element
6845  * @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).
6846  */
6847     Roo.Element = function(element, forceNew){
6848         var dom = typeof element == "string" ?
6849                 document.getElementById(element) : element;
6850         if(!dom){ // invalid id/element
6851             return null;
6852         }
6853         var id = dom.id;
6854         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6855             return Roo.Element.cache[id];
6856         }
6857
6858         /**
6859          * The DOM element
6860          * @type HTMLElement
6861          */
6862         this.dom = dom;
6863
6864         /**
6865          * The DOM element ID
6866          * @type String
6867          */
6868         this.id = id || Roo.id(dom);
6869     };
6870
6871     var El = Roo.Element;
6872
6873     El.prototype = {
6874         /**
6875          * The element's default display mode  (defaults to "")
6876          * @type String
6877          */
6878         originalDisplay : "",
6879
6880         visibilityMode : 1,
6881         /**
6882          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6883          * @type String
6884          */
6885         defaultUnit : "px",
6886         /**
6887          * Sets the element's visibility mode. When setVisible() is called it
6888          * will use this to determine whether to set the visibility or the display property.
6889          * @param visMode Element.VISIBILITY or Element.DISPLAY
6890          * @return {Roo.Element} this
6891          */
6892         setVisibilityMode : function(visMode){
6893             this.visibilityMode = visMode;
6894             return this;
6895         },
6896         /**
6897          * Convenience method for setVisibilityMode(Element.DISPLAY)
6898          * @param {String} display (optional) What to set display to when visible
6899          * @return {Roo.Element} this
6900          */
6901         enableDisplayMode : function(display){
6902             this.setVisibilityMode(El.DISPLAY);
6903             if(typeof display != "undefined") this.originalDisplay = display;
6904             return this;
6905         },
6906
6907         /**
6908          * 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)
6909          * @param {String} selector The simple selector to test
6910          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6911                 search as a number or element (defaults to 10 || document.body)
6912          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6913          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6914          */
6915         findParent : function(simpleSelector, maxDepth, returnEl){
6916             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6917             maxDepth = maxDepth || 50;
6918             if(typeof maxDepth != "number"){
6919                 stopEl = Roo.getDom(maxDepth);
6920                 maxDepth = 10;
6921             }
6922             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6923                 if(dq.is(p, simpleSelector)){
6924                     return returnEl ? Roo.get(p) : p;
6925                 }
6926                 depth++;
6927                 p = p.parentNode;
6928             }
6929             return null;
6930         },
6931
6932
6933         /**
6934          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6935          * @param {String} selector The simple selector to test
6936          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6937                 search as a number or element (defaults to 10 || document.body)
6938          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6939          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6940          */
6941         findParentNode : function(simpleSelector, maxDepth, returnEl){
6942             var p = Roo.fly(this.dom.parentNode, '_internal');
6943             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6944         },
6945
6946         /**
6947          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6948          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6949          * @param {String} selector The simple selector to test
6950          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6951                 search as a number or element (defaults to 10 || document.body)
6952          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6953          */
6954         up : function(simpleSelector, maxDepth){
6955             return this.findParentNode(simpleSelector, maxDepth, true);
6956         },
6957
6958
6959
6960         /**
6961          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6962          * @param {String} selector The simple selector to test
6963          * @return {Boolean} True if this element matches the selector, else false
6964          */
6965         is : function(simpleSelector){
6966             return Roo.DomQuery.is(this.dom, simpleSelector);
6967         },
6968
6969         /**
6970          * Perform animation on this element.
6971          * @param {Object} args The YUI animation control args
6972          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6973          * @param {Function} onComplete (optional) Function to call when animation completes
6974          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6975          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6976          * @return {Roo.Element} this
6977          */
6978         animate : function(args, duration, onComplete, easing, animType){
6979             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6980             return this;
6981         },
6982
6983         /*
6984          * @private Internal animation call
6985          */
6986         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6987             animType = animType || 'run';
6988             opt = opt || {};
6989             var anim = Roo.lib.Anim[animType](
6990                 this.dom, args,
6991                 (opt.duration || defaultDur) || .35,
6992                 (opt.easing || defaultEase) || 'easeOut',
6993                 function(){
6994                     Roo.callback(cb, this);
6995                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6996                 },
6997                 this
6998             );
6999             opt.anim = anim;
7000             return anim;
7001         },
7002
7003         // private legacy anim prep
7004         preanim : function(a, i){
7005             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7006         },
7007
7008         /**
7009          * Removes worthless text nodes
7010          * @param {Boolean} forceReclean (optional) By default the element
7011          * keeps track if it has been cleaned already so
7012          * you can call this over and over. However, if you update the element and
7013          * need to force a reclean, you can pass true.
7014          */
7015         clean : function(forceReclean){
7016             if(this.isCleaned && forceReclean !== true){
7017                 return this;
7018             }
7019             var ns = /\S/;
7020             var d = this.dom, n = d.firstChild, ni = -1;
7021             while(n){
7022                 var nx = n.nextSibling;
7023                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7024                     d.removeChild(n);
7025                 }else{
7026                     n.nodeIndex = ++ni;
7027                 }
7028                 n = nx;
7029             }
7030             this.isCleaned = true;
7031             return this;
7032         },
7033
7034         // private
7035         calcOffsetsTo : function(el){
7036             el = Roo.get(el);
7037             var d = el.dom;
7038             var restorePos = false;
7039             if(el.getStyle('position') == 'static'){
7040                 el.position('relative');
7041                 restorePos = true;
7042             }
7043             var x = 0, y =0;
7044             var op = this.dom;
7045             while(op && op != d && op.tagName != 'HTML'){
7046                 x+= op.offsetLeft;
7047                 y+= op.offsetTop;
7048                 op = op.offsetParent;
7049             }
7050             if(restorePos){
7051                 el.position('static');
7052             }
7053             return [x, y];
7054         },
7055
7056         /**
7057          * Scrolls this element into view within the passed container.
7058          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7059          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7060          * @return {Roo.Element} this
7061          */
7062         scrollIntoView : function(container, hscroll){
7063             var c = Roo.getDom(container) || document.body;
7064             var el = this.dom;
7065
7066             var o = this.calcOffsetsTo(c),
7067                 l = o[0],
7068                 t = o[1],
7069                 b = t+el.offsetHeight,
7070                 r = l+el.offsetWidth;
7071
7072             var ch = c.clientHeight;
7073             var ct = parseInt(c.scrollTop, 10);
7074             var cl = parseInt(c.scrollLeft, 10);
7075             var cb = ct + ch;
7076             var cr = cl + c.clientWidth;
7077
7078             if(t < ct){
7079                 c.scrollTop = t;
7080             }else if(b > cb){
7081                 c.scrollTop = b-ch;
7082             }
7083
7084             if(hscroll !== false){
7085                 if(l < cl){
7086                     c.scrollLeft = l;
7087                 }else if(r > cr){
7088                     c.scrollLeft = r-c.clientWidth;
7089                 }
7090             }
7091             return this;
7092         },
7093
7094         // private
7095         scrollChildIntoView : function(child, hscroll){
7096             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7097         },
7098
7099         /**
7100          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7101          * the new height may not be available immediately.
7102          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7103          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7104          * @param {Function} onComplete (optional) Function to call when animation completes
7105          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7106          * @return {Roo.Element} this
7107          */
7108         autoHeight : function(animate, duration, onComplete, easing){
7109             var oldHeight = this.getHeight();
7110             this.clip();
7111             this.setHeight(1); // force clipping
7112             setTimeout(function(){
7113                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7114                 if(!animate){
7115                     this.setHeight(height);
7116                     this.unclip();
7117                     if(typeof onComplete == "function"){
7118                         onComplete();
7119                     }
7120                 }else{
7121                     this.setHeight(oldHeight); // restore original height
7122                     this.setHeight(height, animate, duration, function(){
7123                         this.unclip();
7124                         if(typeof onComplete == "function") onComplete();
7125                     }.createDelegate(this), easing);
7126                 }
7127             }.createDelegate(this), 0);
7128             return this;
7129         },
7130
7131         /**
7132          * Returns true if this element is an ancestor of the passed element
7133          * @param {HTMLElement/String} el The element to check
7134          * @return {Boolean} True if this element is an ancestor of el, else false
7135          */
7136         contains : function(el){
7137             if(!el){return false;}
7138             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7139         },
7140
7141         /**
7142          * Checks whether the element is currently visible using both visibility and display properties.
7143          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7144          * @return {Boolean} True if the element is currently visible, else false
7145          */
7146         isVisible : function(deep) {
7147             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7148             if(deep !== true || !vis){
7149                 return vis;
7150             }
7151             var p = this.dom.parentNode;
7152             while(p && p.tagName.toLowerCase() != "body"){
7153                 if(!Roo.fly(p, '_isVisible').isVisible()){
7154                     return false;
7155                 }
7156                 p = p.parentNode;
7157             }
7158             return true;
7159         },
7160
7161         /**
7162          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7163          * @param {String} selector The CSS selector
7164          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7165          * @return {CompositeElement/CompositeElementLite} The composite element
7166          */
7167         select : function(selector, unique){
7168             return El.select(selector, unique, this.dom);
7169         },
7170
7171         /**
7172          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7173          * @param {String} selector The CSS selector
7174          * @return {Array} An array of the matched nodes
7175          */
7176         query : function(selector, unique){
7177             return Roo.DomQuery.select(selector, this.dom);
7178         },
7179
7180         /**
7181          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7182          * @param {String} selector The CSS selector
7183          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7184          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7185          */
7186         child : function(selector, returnDom){
7187             var n = Roo.DomQuery.selectNode(selector, this.dom);
7188             return returnDom ? n : Roo.get(n);
7189         },
7190
7191         /**
7192          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7193          * @param {String} selector The CSS selector
7194          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7195          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7196          */
7197         down : function(selector, returnDom){
7198             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7199             return returnDom ? n : Roo.get(n);
7200         },
7201
7202         /**
7203          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7204          * @param {String} group The group the DD object is member of
7205          * @param {Object} config The DD config object
7206          * @param {Object} overrides An object containing methods to override/implement on the DD object
7207          * @return {Roo.dd.DD} The DD object
7208          */
7209         initDD : function(group, config, overrides){
7210             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7211             return Roo.apply(dd, overrides);
7212         },
7213
7214         /**
7215          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7216          * @param {String} group The group the DDProxy object is member of
7217          * @param {Object} config The DDProxy config object
7218          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7219          * @return {Roo.dd.DDProxy} The DDProxy object
7220          */
7221         initDDProxy : function(group, config, overrides){
7222             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7223             return Roo.apply(dd, overrides);
7224         },
7225
7226         /**
7227          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7228          * @param {String} group The group the DDTarget object is member of
7229          * @param {Object} config The DDTarget config object
7230          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7231          * @return {Roo.dd.DDTarget} The DDTarget object
7232          */
7233         initDDTarget : function(group, config, overrides){
7234             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7235             return Roo.apply(dd, overrides);
7236         },
7237
7238         /**
7239          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7240          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7241          * @param {Boolean} visible Whether the element is visible
7242          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7243          * @return {Roo.Element} this
7244          */
7245          setVisible : function(visible, animate){
7246             if(!animate || !A){
7247                 if(this.visibilityMode == El.DISPLAY){
7248                     this.setDisplayed(visible);
7249                 }else{
7250                     this.fixDisplay();
7251                     this.dom.style.visibility = visible ? "visible" : "hidden";
7252                 }
7253             }else{
7254                 // closure for composites
7255                 var dom = this.dom;
7256                 var visMode = this.visibilityMode;
7257                 if(visible){
7258                     this.setOpacity(.01);
7259                     this.setVisible(true);
7260                 }
7261                 this.anim({opacity: { to: (visible?1:0) }},
7262                       this.preanim(arguments, 1),
7263                       null, .35, 'easeIn', function(){
7264                          if(!visible){
7265                              if(visMode == El.DISPLAY){
7266                                  dom.style.display = "none";
7267                              }else{
7268                                  dom.style.visibility = "hidden";
7269                              }
7270                              Roo.get(dom).setOpacity(1);
7271                          }
7272                      });
7273             }
7274             return this;
7275         },
7276
7277         /**
7278          * Returns true if display is not "none"
7279          * @return {Boolean}
7280          */
7281         isDisplayed : function() {
7282             return this.getStyle("display") != "none";
7283         },
7284
7285         /**
7286          * Toggles the element's visibility or display, depending on visibility mode.
7287          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7288          * @return {Roo.Element} this
7289          */
7290         toggle : function(animate){
7291             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7292             return this;
7293         },
7294
7295         /**
7296          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7297          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7298          * @return {Roo.Element} this
7299          */
7300         setDisplayed : function(value) {
7301             if(typeof value == "boolean"){
7302                value = value ? this.originalDisplay : "none";
7303             }
7304             this.setStyle("display", value);
7305             return this;
7306         },
7307
7308         /**
7309          * Tries to focus the element. Any exceptions are caught and ignored.
7310          * @return {Roo.Element} this
7311          */
7312         focus : function() {
7313             try{
7314                 this.dom.focus();
7315             }catch(e){}
7316             return this;
7317         },
7318
7319         /**
7320          * Tries to blur the element. Any exceptions are caught and ignored.
7321          * @return {Roo.Element} this
7322          */
7323         blur : function() {
7324             try{
7325                 this.dom.blur();
7326             }catch(e){}
7327             return this;
7328         },
7329
7330         /**
7331          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7332          * @param {String/Array} className The CSS class to add, or an array of classes
7333          * @return {Roo.Element} this
7334          */
7335         addClass : function(className){
7336             if(className instanceof Array){
7337                 for(var i = 0, len = className.length; i < len; i++) {
7338                     this.addClass(className[i]);
7339                 }
7340             }else{
7341                 if(className && !this.hasClass(className)){
7342                     this.dom.className = this.dom.className + " " + className;
7343                 }
7344             }
7345             return this;
7346         },
7347
7348         /**
7349          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7350          * @param {String/Array} className The CSS class to add, or an array of classes
7351          * @return {Roo.Element} this
7352          */
7353         radioClass : function(className){
7354             var siblings = this.dom.parentNode.childNodes;
7355             for(var i = 0; i < siblings.length; i++) {
7356                 var s = siblings[i];
7357                 if(s.nodeType == 1){
7358                     Roo.get(s).removeClass(className);
7359                 }
7360             }
7361             this.addClass(className);
7362             return this;
7363         },
7364
7365         /**
7366          * Removes one or more CSS classes from the element.
7367          * @param {String/Array} className The CSS class to remove, or an array of classes
7368          * @return {Roo.Element} this
7369          */
7370         removeClass : function(className){
7371             if(!className || !this.dom.className){
7372                 return this;
7373             }
7374             if(className instanceof Array){
7375                 for(var i = 0, len = className.length; i < len; i++) {
7376                     this.removeClass(className[i]);
7377                 }
7378             }else{
7379                 if(this.hasClass(className)){
7380                     var re = this.classReCache[className];
7381                     if (!re) {
7382                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7383                        this.classReCache[className] = re;
7384                     }
7385                     this.dom.className =
7386                         this.dom.className.replace(re, " ");
7387                 }
7388             }
7389             return this;
7390         },
7391
7392         // private
7393         classReCache: {},
7394
7395         /**
7396          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7397          * @param {String} className The CSS class to toggle
7398          * @return {Roo.Element} this
7399          */
7400         toggleClass : function(className){
7401             if(this.hasClass(className)){
7402                 this.removeClass(className);
7403             }else{
7404                 this.addClass(className);
7405             }
7406             return this;
7407         },
7408
7409         /**
7410          * Checks if the specified CSS class exists on this element's DOM node.
7411          * @param {String} className The CSS class to check for
7412          * @return {Boolean} True if the class exists, else false
7413          */
7414         hasClass : function(className){
7415             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7416         },
7417
7418         /**
7419          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7420          * @param {String} oldClassName The CSS class to replace
7421          * @param {String} newClassName The replacement CSS class
7422          * @return {Roo.Element} this
7423          */
7424         replaceClass : function(oldClassName, newClassName){
7425             this.removeClass(oldClassName);
7426             this.addClass(newClassName);
7427             return this;
7428         },
7429
7430         /**
7431          * Returns an object with properties matching the styles requested.
7432          * For example, el.getStyles('color', 'font-size', 'width') might return
7433          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7434          * @param {String} style1 A style name
7435          * @param {String} style2 A style name
7436          * @param {String} etc.
7437          * @return {Object} The style object
7438          */
7439         getStyles : function(){
7440             var a = arguments, len = a.length, r = {};
7441             for(var i = 0; i < len; i++){
7442                 r[a[i]] = this.getStyle(a[i]);
7443             }
7444             return r;
7445         },
7446
7447         /**
7448          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7449          * @param {String} property The style property whose value is returned.
7450          * @return {String} The current value of the style property for this element.
7451          */
7452         getStyle : function(){
7453             return view && view.getComputedStyle ?
7454                 function(prop){
7455                     var el = this.dom, v, cs, camel;
7456                     if(prop == 'float'){
7457                         prop = "cssFloat";
7458                     }
7459                     if(el.style && (v = el.style[prop])){
7460                         return v;
7461                     }
7462                     if(cs = view.getComputedStyle(el, "")){
7463                         if(!(camel = propCache[prop])){
7464                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7465                         }
7466                         return cs[camel];
7467                     }
7468                     return null;
7469                 } :
7470                 function(prop){
7471                     var el = this.dom, v, cs, camel;
7472                     if(prop == 'opacity'){
7473                         if(typeof el.style.filter == 'string'){
7474                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7475                             if(m){
7476                                 var fv = parseFloat(m[1]);
7477                                 if(!isNaN(fv)){
7478                                     return fv ? fv / 100 : 0;
7479                                 }
7480                             }
7481                         }
7482                         return 1;
7483                     }else if(prop == 'float'){
7484                         prop = "styleFloat";
7485                     }
7486                     if(!(camel = propCache[prop])){
7487                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7488                     }
7489                     if(v = el.style[camel]){
7490                         return v;
7491                     }
7492                     if(cs = el.currentStyle){
7493                         return cs[camel];
7494                     }
7495                     return null;
7496                 };
7497         }(),
7498
7499         /**
7500          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7501          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7502          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7503          * @return {Roo.Element} this
7504          */
7505         setStyle : function(prop, value){
7506             if(typeof prop == "string"){
7507                 
7508                 if (prop == 'float') {
7509                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7510                     return this;
7511                 }
7512                 
7513                 var camel;
7514                 if(!(camel = propCache[prop])){
7515                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7516                 }
7517                 
7518                 if(camel == 'opacity') {
7519                     this.setOpacity(value);
7520                 }else{
7521                     this.dom.style[camel] = value;
7522                 }
7523             }else{
7524                 for(var style in prop){
7525                     if(typeof prop[style] != "function"){
7526                        this.setStyle(style, prop[style]);
7527                     }
7528                 }
7529             }
7530             return this;
7531         },
7532
7533         /**
7534          * More flexible version of {@link #setStyle} for setting style properties.
7535          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7536          * a function which returns such a specification.
7537          * @return {Roo.Element} this
7538          */
7539         applyStyles : function(style){
7540             Roo.DomHelper.applyStyles(this.dom, style);
7541             return this;
7542         },
7543
7544         /**
7545           * 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).
7546           * @return {Number} The X position of the element
7547           */
7548         getX : function(){
7549             return D.getX(this.dom);
7550         },
7551
7552         /**
7553           * 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).
7554           * @return {Number} The Y position of the element
7555           */
7556         getY : function(){
7557             return D.getY(this.dom);
7558         },
7559
7560         /**
7561           * 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).
7562           * @return {Array} The XY position of the element
7563           */
7564         getXY : function(){
7565             return D.getXY(this.dom);
7566         },
7567
7568         /**
7569          * 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).
7570          * @param {Number} The X position of the element
7571          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7572          * @return {Roo.Element} this
7573          */
7574         setX : function(x, animate){
7575             if(!animate || !A){
7576                 D.setX(this.dom, x);
7577             }else{
7578                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7579             }
7580             return this;
7581         },
7582
7583         /**
7584          * 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).
7585          * @param {Number} The Y position of the element
7586          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7587          * @return {Roo.Element} this
7588          */
7589         setY : function(y, animate){
7590             if(!animate || !A){
7591                 D.setY(this.dom, y);
7592             }else{
7593                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7594             }
7595             return this;
7596         },
7597
7598         /**
7599          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7600          * @param {String} left The left CSS property value
7601          * @return {Roo.Element} this
7602          */
7603         setLeft : function(left){
7604             this.setStyle("left", this.addUnits(left));
7605             return this;
7606         },
7607
7608         /**
7609          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7610          * @param {String} top The top CSS property value
7611          * @return {Roo.Element} this
7612          */
7613         setTop : function(top){
7614             this.setStyle("top", this.addUnits(top));
7615             return this;
7616         },
7617
7618         /**
7619          * Sets the element's CSS right style.
7620          * @param {String} right The right CSS property value
7621          * @return {Roo.Element} this
7622          */
7623         setRight : function(right){
7624             this.setStyle("right", this.addUnits(right));
7625             return this;
7626         },
7627
7628         /**
7629          * Sets the element's CSS bottom style.
7630          * @param {String} bottom The bottom CSS property value
7631          * @return {Roo.Element} this
7632          */
7633         setBottom : function(bottom){
7634             this.setStyle("bottom", this.addUnits(bottom));
7635             return this;
7636         },
7637
7638         /**
7639          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7640          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7641          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7642          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7643          * @return {Roo.Element} this
7644          */
7645         setXY : function(pos, animate){
7646             if(!animate || !A){
7647                 D.setXY(this.dom, pos);
7648             }else{
7649                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7650             }
7651             return this;
7652         },
7653
7654         /**
7655          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7656          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7657          * @param {Number} x X value for new position (coordinates are page-based)
7658          * @param {Number} y Y value for new position (coordinates are page-based)
7659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662         setLocation : function(x, y, animate){
7663             this.setXY([x, y], this.preanim(arguments, 2));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Number} x X value for new position (coordinates are page-based)
7671          * @param {Number} y Y value for new position (coordinates are page-based)
7672          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7673          * @return {Roo.Element} this
7674          */
7675         moveTo : function(x, y, animate){
7676             this.setXY([x, y], this.preanim(arguments, 2));
7677             return this;
7678         },
7679
7680         /**
7681          * Returns the region of the given element.
7682          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7683          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7684          */
7685         getRegion : function(){
7686             return D.getRegion(this.dom);
7687         },
7688
7689         /**
7690          * Returns the offset height of the element
7691          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7692          * @return {Number} The element's height
7693          */
7694         getHeight : function(contentHeight){
7695             var h = this.dom.offsetHeight || 0;
7696             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7697         },
7698
7699         /**
7700          * Returns the offset width of the element
7701          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7702          * @return {Number} The element's width
7703          */
7704         getWidth : function(contentWidth){
7705             var w = this.dom.offsetWidth || 0;
7706             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7707         },
7708
7709         /**
7710          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7711          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7712          * if a height has not been set using CSS.
7713          * @return {Number}
7714          */
7715         getComputedHeight : function(){
7716             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7717             if(!h){
7718                 h = parseInt(this.getStyle('height'), 10) || 0;
7719                 if(!this.isBorderBox()){
7720                     h += this.getFrameWidth('tb');
7721                 }
7722             }
7723             return h;
7724         },
7725
7726         /**
7727          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7728          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7729          * if a width has not been set using CSS.
7730          * @return {Number}
7731          */
7732         getComputedWidth : function(){
7733             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7734             if(!w){
7735                 w = parseInt(this.getStyle('width'), 10) || 0;
7736                 if(!this.isBorderBox()){
7737                     w += this.getFrameWidth('lr');
7738                 }
7739             }
7740             return w;
7741         },
7742
7743         /**
7744          * Returns the size of the element.
7745          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7746          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7747          */
7748         getSize : function(contentSize){
7749             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7750         },
7751
7752         /**
7753          * Returns the width and height of the viewport.
7754          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7755          */
7756         getViewSize : function(){
7757             var d = this.dom, doc = document, aw = 0, ah = 0;
7758             if(d == doc || d == doc.body){
7759                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7760             }else{
7761                 return {
7762                     width : d.clientWidth,
7763                     height: d.clientHeight
7764                 };
7765             }
7766         },
7767
7768         /**
7769          * Returns the value of the "value" attribute
7770          * @param {Boolean} asNumber true to parse the value as a number
7771          * @return {String/Number}
7772          */
7773         getValue : function(asNumber){
7774             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7775         },
7776
7777         // private
7778         adjustWidth : function(width){
7779             if(typeof width == "number"){
7780                 if(this.autoBoxAdjust && !this.isBorderBox()){
7781                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7782                 }
7783                 if(width < 0){
7784                     width = 0;
7785                 }
7786             }
7787             return width;
7788         },
7789
7790         // private
7791         adjustHeight : function(height){
7792             if(typeof height == "number"){
7793                if(this.autoBoxAdjust && !this.isBorderBox()){
7794                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7795                }
7796                if(height < 0){
7797                    height = 0;
7798                }
7799             }
7800             return height;
7801         },
7802
7803         /**
7804          * Set the width of the element
7805          * @param {Number} width The new width
7806          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7807          * @return {Roo.Element} this
7808          */
7809         setWidth : function(width, animate){
7810             width = this.adjustWidth(width);
7811             if(!animate || !A){
7812                 this.dom.style.width = this.addUnits(width);
7813             }else{
7814                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7815             }
7816             return this;
7817         },
7818
7819         /**
7820          * Set the height of the element
7821          * @param {Number} height The new height
7822          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7823          * @return {Roo.Element} this
7824          */
7825          setHeight : function(height, animate){
7826             height = this.adjustHeight(height);
7827             if(!animate || !A){
7828                 this.dom.style.height = this.addUnits(height);
7829             }else{
7830                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7831             }
7832             return this;
7833         },
7834
7835         /**
7836          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7837          * @param {Number} width The new width
7838          * @param {Number} height The new height
7839          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7840          * @return {Roo.Element} this
7841          */
7842          setSize : function(width, height, animate){
7843             if(typeof width == "object"){ // in case of object from getSize()
7844                 height = width.height; width = width.width;
7845             }
7846             width = this.adjustWidth(width); height = this.adjustHeight(height);
7847             if(!animate || !A){
7848                 this.dom.style.width = this.addUnits(width);
7849                 this.dom.style.height = this.addUnits(height);
7850             }else{
7851                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7852             }
7853             return this;
7854         },
7855
7856         /**
7857          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7858          * @param {Number} x X value for new position (coordinates are page-based)
7859          * @param {Number} y Y value for new position (coordinates are page-based)
7860          * @param {Number} width The new width
7861          * @param {Number} height The new height
7862          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7863          * @return {Roo.Element} this
7864          */
7865         setBounds : function(x, y, width, height, animate){
7866             if(!animate || !A){
7867                 this.setSize(width, height);
7868                 this.setLocation(x, y);
7869             }else{
7870                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7871                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7872                               this.preanim(arguments, 4), 'motion');
7873             }
7874             return this;
7875         },
7876
7877         /**
7878          * 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.
7879          * @param {Roo.lib.Region} region The region to fill
7880          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7881          * @return {Roo.Element} this
7882          */
7883         setRegion : function(region, animate){
7884             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7885             return this;
7886         },
7887
7888         /**
7889          * Appends an event handler
7890          *
7891          * @param {String}   eventName     The type of event to append
7892          * @param {Function} fn        The method the event invokes
7893          * @param {Object} scope       (optional) The scope (this object) of the fn
7894          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7895          */
7896         addListener : function(eventName, fn, scope, options){
7897             if (this.dom) {
7898                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7899             }
7900         },
7901
7902         /**
7903          * Removes an event handler from this element
7904          * @param {String} eventName the type of event to remove
7905          * @param {Function} fn the method the event invokes
7906          * @return {Roo.Element} this
7907          */
7908         removeListener : function(eventName, fn){
7909             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7910             return this;
7911         },
7912
7913         /**
7914          * Removes all previous added listeners from this element
7915          * @return {Roo.Element} this
7916          */
7917         removeAllListeners : function(){
7918             E.purgeElement(this.dom);
7919             return this;
7920         },
7921
7922         relayEvent : function(eventName, observable){
7923             this.on(eventName, function(e){
7924                 observable.fireEvent(eventName, e);
7925             });
7926         },
7927
7928         /**
7929          * Set the opacity of the element
7930          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7931          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934          setOpacity : function(opacity, animate){
7935             if(!animate || !A){
7936                 var s = this.dom.style;
7937                 if(Roo.isIE){
7938                     s.zoom = 1;
7939                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7940                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7941                 }else{
7942                     s.opacity = opacity;
7943                 }
7944             }else{
7945                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7946             }
7947             return this;
7948         },
7949
7950         /**
7951          * Gets the left X coordinate
7952          * @param {Boolean} local True to get the local css position instead of page coordinate
7953          * @return {Number}
7954          */
7955         getLeft : function(local){
7956             if(!local){
7957                 return this.getX();
7958             }else{
7959                 return parseInt(this.getStyle("left"), 10) || 0;
7960             }
7961         },
7962
7963         /**
7964          * Gets the right X coordinate of the element (element X position + element width)
7965          * @param {Boolean} local True to get the local css position instead of page coordinate
7966          * @return {Number}
7967          */
7968         getRight : function(local){
7969             if(!local){
7970                 return this.getX() + this.getWidth();
7971             }else{
7972                 return (this.getLeft(true) + this.getWidth()) || 0;
7973             }
7974         },
7975
7976         /**
7977          * Gets the top Y coordinate
7978          * @param {Boolean} local True to get the local css position instead of page coordinate
7979          * @return {Number}
7980          */
7981         getTop : function(local) {
7982             if(!local){
7983                 return this.getY();
7984             }else{
7985                 return parseInt(this.getStyle("top"), 10) || 0;
7986             }
7987         },
7988
7989         /**
7990          * Gets the bottom Y coordinate of the element (element Y position + element height)
7991          * @param {Boolean} local True to get the local css position instead of page coordinate
7992          * @return {Number}
7993          */
7994         getBottom : function(local){
7995             if(!local){
7996                 return this.getY() + this.getHeight();
7997             }else{
7998                 return (this.getTop(true) + this.getHeight()) || 0;
7999             }
8000         },
8001
8002         /**
8003         * Initializes positioning on this element. If a desired position is not passed, it will make the
8004         * the element positioned relative IF it is not already positioned.
8005         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8006         * @param {Number} zIndex (optional) The zIndex to apply
8007         * @param {Number} x (optional) Set the page X position
8008         * @param {Number} y (optional) Set the page Y position
8009         */
8010         position : function(pos, zIndex, x, y){
8011             if(!pos){
8012                if(this.getStyle('position') == 'static'){
8013                    this.setStyle('position', 'relative');
8014                }
8015             }else{
8016                 this.setStyle("position", pos);
8017             }
8018             if(zIndex){
8019                 this.setStyle("z-index", zIndex);
8020             }
8021             if(x !== undefined && y !== undefined){
8022                 this.setXY([x, y]);
8023             }else if(x !== undefined){
8024                 this.setX(x);
8025             }else if(y !== undefined){
8026                 this.setY(y);
8027             }
8028         },
8029
8030         /**
8031         * Clear positioning back to the default when the document was loaded
8032         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8033         * @return {Roo.Element} this
8034          */
8035         clearPositioning : function(value){
8036             value = value ||'';
8037             this.setStyle({
8038                 "left": value,
8039                 "right": value,
8040                 "top": value,
8041                 "bottom": value,
8042                 "z-index": "",
8043                 "position" : "static"
8044             });
8045             return this;
8046         },
8047
8048         /**
8049         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8050         * snapshot before performing an update and then restoring the element.
8051         * @return {Object}
8052         */
8053         getPositioning : function(){
8054             var l = this.getStyle("left");
8055             var t = this.getStyle("top");
8056             return {
8057                 "position" : this.getStyle("position"),
8058                 "left" : l,
8059                 "right" : l ? "" : this.getStyle("right"),
8060                 "top" : t,
8061                 "bottom" : t ? "" : this.getStyle("bottom"),
8062                 "z-index" : this.getStyle("z-index")
8063             };
8064         },
8065
8066         /**
8067          * Gets the width of the border(s) for the specified side(s)
8068          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8069          * passing lr would get the border (l)eft width + the border (r)ight width.
8070          * @return {Number} The width of the sides passed added together
8071          */
8072         getBorderWidth : function(side){
8073             return this.addStyles(side, El.borders);
8074         },
8075
8076         /**
8077          * Gets the width of the padding(s) for the specified side(s)
8078          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8079          * passing lr would get the padding (l)eft + the padding (r)ight.
8080          * @return {Number} The padding of the sides passed added together
8081          */
8082         getPadding : function(side){
8083             return this.addStyles(side, El.paddings);
8084         },
8085
8086         /**
8087         * Set positioning with an object returned by getPositioning().
8088         * @param {Object} posCfg
8089         * @return {Roo.Element} this
8090          */
8091         setPositioning : function(pc){
8092             this.applyStyles(pc);
8093             if(pc.right == "auto"){
8094                 this.dom.style.right = "";
8095             }
8096             if(pc.bottom == "auto"){
8097                 this.dom.style.bottom = "";
8098             }
8099             return this;
8100         },
8101
8102         // private
8103         fixDisplay : function(){
8104             if(this.getStyle("display") == "none"){
8105                 this.setStyle("visibility", "hidden");
8106                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8107                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8108                     this.setStyle("display", "block");
8109                 }
8110             }
8111         },
8112
8113         /**
8114          * Quick set left and top adding default units
8115          * @param {String} left The left CSS property value
8116          * @param {String} top The top CSS property value
8117          * @return {Roo.Element} this
8118          */
8119          setLeftTop : function(left, top){
8120             this.dom.style.left = this.addUnits(left);
8121             this.dom.style.top = this.addUnits(top);
8122             return this;
8123         },
8124
8125         /**
8126          * Move this element relative to its current position.
8127          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8128          * @param {Number} distance How far to move the element in pixels
8129          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8130          * @return {Roo.Element} this
8131          */
8132          move : function(direction, distance, animate){
8133             var xy = this.getXY();
8134             direction = direction.toLowerCase();
8135             switch(direction){
8136                 case "l":
8137                 case "left":
8138                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8139                     break;
8140                case "r":
8141                case "right":
8142                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8143                     break;
8144                case "t":
8145                case "top":
8146                case "up":
8147                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8148                     break;
8149                case "b":
8150                case "bottom":
8151                case "down":
8152                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8153                     break;
8154             }
8155             return this;
8156         },
8157
8158         /**
8159          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8160          * @return {Roo.Element} this
8161          */
8162         clip : function(){
8163             if(!this.isClipped){
8164                this.isClipped = true;
8165                this.originalClip = {
8166                    "o": this.getStyle("overflow"),
8167                    "x": this.getStyle("overflow-x"),
8168                    "y": this.getStyle("overflow-y")
8169                };
8170                this.setStyle("overflow", "hidden");
8171                this.setStyle("overflow-x", "hidden");
8172                this.setStyle("overflow-y", "hidden");
8173             }
8174             return this;
8175         },
8176
8177         /**
8178          *  Return clipping (overflow) to original clipping before clip() was called
8179          * @return {Roo.Element} this
8180          */
8181         unclip : function(){
8182             if(this.isClipped){
8183                 this.isClipped = false;
8184                 var o = this.originalClip;
8185                 if(o.o){this.setStyle("overflow", o.o);}
8186                 if(o.x){this.setStyle("overflow-x", o.x);}
8187                 if(o.y){this.setStyle("overflow-y", o.y);}
8188             }
8189             return this;
8190         },
8191
8192
8193         /**
8194          * Gets the x,y coordinates specified by the anchor position on the element.
8195          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8196          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8197          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8198          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8199          * @return {Array} [x, y] An array containing the element's x and y coordinates
8200          */
8201         getAnchorXY : function(anchor, local, s){
8202             //Passing a different size is useful for pre-calculating anchors,
8203             //especially for anchored animations that change the el size.
8204
8205             var w, h, vp = false;
8206             if(!s){
8207                 var d = this.dom;
8208                 if(d == document.body || d == document){
8209                     vp = true;
8210                     w = D.getViewWidth(); h = D.getViewHeight();
8211                 }else{
8212                     w = this.getWidth(); h = this.getHeight();
8213                 }
8214             }else{
8215                 w = s.width;  h = s.height;
8216             }
8217             var x = 0, y = 0, r = Math.round;
8218             switch((anchor || "tl").toLowerCase()){
8219                 case "c":
8220                     x = r(w*.5);
8221                     y = r(h*.5);
8222                 break;
8223                 case "t":
8224                     x = r(w*.5);
8225                     y = 0;
8226                 break;
8227                 case "l":
8228                     x = 0;
8229                     y = r(h*.5);
8230                 break;
8231                 case "r":
8232                     x = w;
8233                     y = r(h*.5);
8234                 break;
8235                 case "b":
8236                     x = r(w*.5);
8237                     y = h;
8238                 break;
8239                 case "tl":
8240                     x = 0;
8241                     y = 0;
8242                 break;
8243                 case "bl":
8244                     x = 0;
8245                     y = h;
8246                 break;
8247                 case "br":
8248                     x = w;
8249                     y = h;
8250                 break;
8251                 case "tr":
8252                     x = w;
8253                     y = 0;
8254                 break;
8255             }
8256             if(local === true){
8257                 return [x, y];
8258             }
8259             if(vp){
8260                 var sc = this.getScroll();
8261                 return [x + sc.left, y + sc.top];
8262             }
8263             //Add the element's offset xy
8264             var o = this.getXY();
8265             return [x+o[0], y+o[1]];
8266         },
8267
8268         /**
8269          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8270          * supported position values.
8271          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8272          * @param {String} position The position to align to.
8273          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8274          * @return {Array} [x, y]
8275          */
8276         getAlignToXY : function(el, p, o){
8277             el = Roo.get(el);
8278             var d = this.dom;
8279             if(!el.dom){
8280                 throw "Element.alignTo with an element that doesn't exist";
8281             }
8282             var c = false; //constrain to viewport
8283             var p1 = "", p2 = "";
8284             o = o || [0,0];
8285
8286             if(!p){
8287                 p = "tl-bl";
8288             }else if(p == "?"){
8289                 p = "tl-bl?";
8290             }else if(p.indexOf("-") == -1){
8291                 p = "tl-" + p;
8292             }
8293             p = p.toLowerCase();
8294             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8295             if(!m){
8296                throw "Element.alignTo with an invalid alignment " + p;
8297             }
8298             p1 = m[1]; p2 = m[2]; c = !!m[3];
8299
8300             //Subtract the aligned el's internal xy from the target's offset xy
8301             //plus custom offset to get the aligned el's new offset xy
8302             var a1 = this.getAnchorXY(p1, true);
8303             var a2 = el.getAnchorXY(p2, false);
8304             var x = a2[0] - a1[0] + o[0];
8305             var y = a2[1] - a1[1] + o[1];
8306             if(c){
8307                 //constrain the aligned el to viewport if necessary
8308                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8309                 // 5px of margin for ie
8310                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8311
8312                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8313                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8314                 //otherwise swap the aligned el to the opposite border of the target.
8315                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8316                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8317                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8318                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8319
8320                var doc = document;
8321                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8322                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8323
8324                if((x+w) > dw + scrollX){
8325                     x = swapX ? r.left-w : dw+scrollX-w;
8326                 }
8327                if(x < scrollX){
8328                    x = swapX ? r.right : scrollX;
8329                }
8330                if((y+h) > dh + scrollY){
8331                     y = swapY ? r.top-h : dh+scrollY-h;
8332                 }
8333                if (y < scrollY){
8334                    y = swapY ? r.bottom : scrollY;
8335                }
8336             }
8337             return [x,y];
8338         },
8339
8340         // private
8341         getConstrainToXY : function(){
8342             var os = {top:0, left:0, bottom:0, right: 0};
8343
8344             return function(el, local, offsets, proposedXY){
8345                 el = Roo.get(el);
8346                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8347
8348                 var vw, vh, vx = 0, vy = 0;
8349                 if(el.dom == document.body || el.dom == document){
8350                     vw = Roo.lib.Dom.getViewWidth();
8351                     vh = Roo.lib.Dom.getViewHeight();
8352                 }else{
8353                     vw = el.dom.clientWidth;
8354                     vh = el.dom.clientHeight;
8355                     if(!local){
8356                         var vxy = el.getXY();
8357                         vx = vxy[0];
8358                         vy = vxy[1];
8359                     }
8360                 }
8361
8362                 var s = el.getScroll();
8363
8364                 vx += offsets.left + s.left;
8365                 vy += offsets.top + s.top;
8366
8367                 vw -= offsets.right;
8368                 vh -= offsets.bottom;
8369
8370                 var vr = vx+vw;
8371                 var vb = vy+vh;
8372
8373                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8374                 var x = xy[0], y = xy[1];
8375                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8376
8377                 // only move it if it needs it
8378                 var moved = false;
8379
8380                 // first validate right/bottom
8381                 if((x + w) > vr){
8382                     x = vr - w;
8383                     moved = true;
8384                 }
8385                 if((y + h) > vb){
8386                     y = vb - h;
8387                     moved = true;
8388                 }
8389                 // then make sure top/left isn't negative
8390                 if(x < vx){
8391                     x = vx;
8392                     moved = true;
8393                 }
8394                 if(y < vy){
8395                     y = vy;
8396                     moved = true;
8397                 }
8398                 return moved ? [x, y] : false;
8399             };
8400         }(),
8401
8402         // private
8403         adjustForConstraints : function(xy, parent, offsets){
8404             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8405         },
8406
8407         /**
8408          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8409          * document it aligns it to the viewport.
8410          * The position parameter is optional, and can be specified in any one of the following formats:
8411          * <ul>
8412          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8413          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8414          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8415          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8416          *   <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
8417          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8418          * </ul>
8419          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8420          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8421          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8422          * that specified in order to enforce the viewport constraints.
8423          * Following are all of the supported anchor positions:
8424     <pre>
8425     Value  Description
8426     -----  -----------------------------
8427     tl     The top left corner (default)
8428     t      The center of the top edge
8429     tr     The top right corner
8430     l      The center of the left edge
8431     c      In the center of the element
8432     r      The center of the right edge
8433     bl     The bottom left corner
8434     b      The center of the bottom edge
8435     br     The bottom right corner
8436     </pre>
8437     Example Usage:
8438     <pre><code>
8439     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8440     el.alignTo("other-el");
8441
8442     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8443     el.alignTo("other-el", "tr?");
8444
8445     // align the bottom right corner of el with the center left edge of other-el
8446     el.alignTo("other-el", "br-l?");
8447
8448     // align the center of el with the bottom left corner of other-el and
8449     // adjust the x position by -6 pixels (and the y position by 0)
8450     el.alignTo("other-el", "c-bl", [-6, 0]);
8451     </code></pre>
8452          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8453          * @param {String} position The position to align to.
8454          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8455          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8456          * @return {Roo.Element} this
8457          */
8458         alignTo : function(element, position, offsets, animate){
8459             var xy = this.getAlignToXY(element, position, offsets);
8460             this.setXY(xy, this.preanim(arguments, 3));
8461             return this;
8462         },
8463
8464         /**
8465          * Anchors an element to another element and realigns it when the window is resized.
8466          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8467          * @param {String} position The position to align to.
8468          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8469          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8470          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8471          * is a number, it is used as the buffer delay (defaults to 50ms).
8472          * @param {Function} callback The function to call after the animation finishes
8473          * @return {Roo.Element} this
8474          */
8475         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8476             var action = function(){
8477                 this.alignTo(el, alignment, offsets, animate);
8478                 Roo.callback(callback, this);
8479             };
8480             Roo.EventManager.onWindowResize(action, this);
8481             var tm = typeof monitorScroll;
8482             if(tm != 'undefined'){
8483                 Roo.EventManager.on(window, 'scroll', action, this,
8484                     {buffer: tm == 'number' ? monitorScroll : 50});
8485             }
8486             action.call(this); // align immediately
8487             return this;
8488         },
8489         /**
8490          * Clears any opacity settings from this element. Required in some cases for IE.
8491          * @return {Roo.Element} this
8492          */
8493         clearOpacity : function(){
8494             if (window.ActiveXObject) {
8495                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8496                     this.dom.style.filter = "";
8497                 }
8498             } else {
8499                 this.dom.style.opacity = "";
8500                 this.dom.style["-moz-opacity"] = "";
8501                 this.dom.style["-khtml-opacity"] = "";
8502             }
8503             return this;
8504         },
8505
8506         /**
8507          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8508          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8509          * @return {Roo.Element} this
8510          */
8511         hide : function(animate){
8512             this.setVisible(false, this.preanim(arguments, 0));
8513             return this;
8514         },
8515
8516         /**
8517         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8518         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8519          * @return {Roo.Element} this
8520          */
8521         show : function(animate){
8522             this.setVisible(true, this.preanim(arguments, 0));
8523             return this;
8524         },
8525
8526         /**
8527          * @private Test if size has a unit, otherwise appends the default
8528          */
8529         addUnits : function(size){
8530             return Roo.Element.addUnits(size, this.defaultUnit);
8531         },
8532
8533         /**
8534          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8535          * @return {Roo.Element} this
8536          */
8537         beginMeasure : function(){
8538             var el = this.dom;
8539             if(el.offsetWidth || el.offsetHeight){
8540                 return this; // offsets work already
8541             }
8542             var changed = [];
8543             var p = this.dom, b = document.body; // start with this element
8544             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8545                 var pe = Roo.get(p);
8546                 if(pe.getStyle('display') == 'none'){
8547                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8548                     p.style.visibility = "hidden";
8549                     p.style.display = "block";
8550                 }
8551                 p = p.parentNode;
8552             }
8553             this._measureChanged = changed;
8554             return this;
8555
8556         },
8557
8558         /**
8559          * Restores displays to before beginMeasure was called
8560          * @return {Roo.Element} this
8561          */
8562         endMeasure : function(){
8563             var changed = this._measureChanged;
8564             if(changed){
8565                 for(var i = 0, len = changed.length; i < len; i++) {
8566                     var r = changed[i];
8567                     r.el.style.visibility = r.visibility;
8568                     r.el.style.display = "none";
8569                 }
8570                 this._measureChanged = null;
8571             }
8572             return this;
8573         },
8574
8575         /**
8576         * Update the innerHTML of this element, optionally searching for and processing scripts
8577         * @param {String} html The new HTML
8578         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8579         * @param {Function} callback For async script loading you can be noticed when the update completes
8580         * @return {Roo.Element} this
8581          */
8582         update : function(html, loadScripts, callback){
8583             if(typeof html == "undefined"){
8584                 html = "";
8585             }
8586             if(loadScripts !== true){
8587                 this.dom.innerHTML = html;
8588                 if(typeof callback == "function"){
8589                     callback();
8590                 }
8591                 return this;
8592             }
8593             var id = Roo.id();
8594             var dom = this.dom;
8595
8596             html += '<span id="' + id + '"></span>';
8597
8598             E.onAvailable(id, function(){
8599                 var hd = document.getElementsByTagName("head")[0];
8600                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8601                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8602                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8603
8604                 var match;
8605                 while(match = re.exec(html)){
8606                     var attrs = match[1];
8607                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8608                     if(srcMatch && srcMatch[2]){
8609                        var s = document.createElement("script");
8610                        s.src = srcMatch[2];
8611                        var typeMatch = attrs.match(typeRe);
8612                        if(typeMatch && typeMatch[2]){
8613                            s.type = typeMatch[2];
8614                        }
8615                        hd.appendChild(s);
8616                     }else if(match[2] && match[2].length > 0){
8617                         if(window.execScript) {
8618                            window.execScript(match[2]);
8619                         } else {
8620                             /**
8621                              * eval:var:id
8622                              * eval:var:dom
8623                              * eval:var:html
8624                              * 
8625                              */
8626                            window.eval(match[2]);
8627                         }
8628                     }
8629                 }
8630                 var el = document.getElementById(id);
8631                 if(el){el.parentNode.removeChild(el);}
8632                 if(typeof callback == "function"){
8633                     callback();
8634                 }
8635             });
8636             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8637             return this;
8638         },
8639
8640         /**
8641          * Direct access to the UpdateManager update() method (takes the same parameters).
8642          * @param {String/Function} url The url for this request or a function to call to get the url
8643          * @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}
8644          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8645          * @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.
8646          * @return {Roo.Element} this
8647          */
8648         load : function(){
8649             var um = this.getUpdateManager();
8650             um.update.apply(um, arguments);
8651             return this;
8652         },
8653
8654         /**
8655         * Gets this element's UpdateManager
8656         * @return {Roo.UpdateManager} The UpdateManager
8657         */
8658         getUpdateManager : function(){
8659             if(!this.updateManager){
8660                 this.updateManager = new Roo.UpdateManager(this);
8661             }
8662             return this.updateManager;
8663         },
8664
8665         /**
8666          * Disables text selection for this element (normalized across browsers)
8667          * @return {Roo.Element} this
8668          */
8669         unselectable : function(){
8670             this.dom.unselectable = "on";
8671             this.swallowEvent("selectstart", true);
8672             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8673             this.addClass("x-unselectable");
8674             return this;
8675         },
8676
8677         /**
8678         * Calculates the x, y to center this element on the screen
8679         * @return {Array} The x, y values [x, y]
8680         */
8681         getCenterXY : function(){
8682             return this.getAlignToXY(document, 'c-c');
8683         },
8684
8685         /**
8686         * Centers the Element in either the viewport, or another Element.
8687         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8688         */
8689         center : function(centerIn){
8690             this.alignTo(centerIn || document, 'c-c');
8691             return this;
8692         },
8693
8694         /**
8695          * Tests various css rules/browsers to determine if this element uses a border box
8696          * @return {Boolean}
8697          */
8698         isBorderBox : function(){
8699             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8700         },
8701
8702         /**
8703          * Return a box {x, y, width, height} that can be used to set another elements
8704          * size/location to match this element.
8705          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8706          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8707          * @return {Object} box An object in the format {x, y, width, height}
8708          */
8709         getBox : function(contentBox, local){
8710             var xy;
8711             if(!local){
8712                 xy = this.getXY();
8713             }else{
8714                 var left = parseInt(this.getStyle("left"), 10) || 0;
8715                 var top = parseInt(this.getStyle("top"), 10) || 0;
8716                 xy = [left, top];
8717             }
8718             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8719             if(!contentBox){
8720                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8721             }else{
8722                 var l = this.getBorderWidth("l")+this.getPadding("l");
8723                 var r = this.getBorderWidth("r")+this.getPadding("r");
8724                 var t = this.getBorderWidth("t")+this.getPadding("t");
8725                 var b = this.getBorderWidth("b")+this.getPadding("b");
8726                 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)};
8727             }
8728             bx.right = bx.x + bx.width;
8729             bx.bottom = bx.y + bx.height;
8730             return bx;
8731         },
8732
8733         /**
8734          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8735          for more information about the sides.
8736          * @param {String} sides
8737          * @return {Number}
8738          */
8739         getFrameWidth : function(sides, onlyContentBox){
8740             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8741         },
8742
8743         /**
8744          * 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.
8745          * @param {Object} box The box to fill {x, y, width, height}
8746          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8747          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8748          * @return {Roo.Element} this
8749          */
8750         setBox : function(box, adjust, animate){
8751             var w = box.width, h = box.height;
8752             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8753                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8754                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8755             }
8756             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8757             return this;
8758         },
8759
8760         /**
8761          * Forces the browser to repaint this element
8762          * @return {Roo.Element} this
8763          */
8764          repaint : function(){
8765             var dom = this.dom;
8766             this.addClass("x-repaint");
8767             setTimeout(function(){
8768                 Roo.get(dom).removeClass("x-repaint");
8769             }, 1);
8770             return this;
8771         },
8772
8773         /**
8774          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8775          * then it returns the calculated width of the sides (see getPadding)
8776          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8777          * @return {Object/Number}
8778          */
8779         getMargins : function(side){
8780             if(!side){
8781                 return {
8782                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8783                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8784                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8785                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8786                 };
8787             }else{
8788                 return this.addStyles(side, El.margins);
8789              }
8790         },
8791
8792         // private
8793         addStyles : function(sides, styles){
8794             var val = 0, v, w;
8795             for(var i = 0, len = sides.length; i < len; i++){
8796                 v = this.getStyle(styles[sides.charAt(i)]);
8797                 if(v){
8798                      w = parseInt(v, 10);
8799                      if(w){ val += w; }
8800                 }
8801             }
8802             return val;
8803         },
8804
8805         /**
8806          * Creates a proxy element of this element
8807          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8808          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8809          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8810          * @return {Roo.Element} The new proxy element
8811          */
8812         createProxy : function(config, renderTo, matchBox){
8813             if(renderTo){
8814                 renderTo = Roo.getDom(renderTo);
8815             }else{
8816                 renderTo = document.body;
8817             }
8818             config = typeof config == "object" ?
8819                 config : {tag : "div", cls: config};
8820             var proxy = Roo.DomHelper.append(renderTo, config, true);
8821             if(matchBox){
8822                proxy.setBox(this.getBox());
8823             }
8824             return proxy;
8825         },
8826
8827         /**
8828          * Puts a mask over this element to disable user interaction. Requires core.css.
8829          * This method can only be applied to elements which accept child nodes.
8830          * @param {String} msg (optional) A message to display in the mask
8831          * @param {String} msgCls (optional) A css class to apply to the msg element
8832          * @return {Element} The mask  element
8833          */
8834         mask : function(msg, msgCls){
8835             if(this.getStyle("position") == "static"){
8836                 this.setStyle("position", "relative");
8837             }
8838             if(!this._mask){
8839                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8840             }
8841             this.addClass("x-masked");
8842             this._mask.setDisplayed(true);
8843             if(typeof msg == 'string'){
8844                 if(!this._maskMsg){
8845                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8846                 }
8847                 var mm = this._maskMsg;
8848                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8849                 mm.dom.firstChild.innerHTML = msg;
8850                 mm.setDisplayed(true);
8851                 mm.center(this);
8852             }
8853             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8854                 this._mask.setHeight(this.getHeight());
8855             }
8856             return this._mask;
8857         },
8858
8859         /**
8860          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8861          * it is cached for reuse.
8862          */
8863         unmask : function(removeEl){
8864             if(this._mask){
8865                 if(removeEl === true){
8866                     this._mask.remove();
8867                     delete this._mask;
8868                     if(this._maskMsg){
8869                         this._maskMsg.remove();
8870                         delete this._maskMsg;
8871                     }
8872                 }else{
8873                     this._mask.setDisplayed(false);
8874                     if(this._maskMsg){
8875                         this._maskMsg.setDisplayed(false);
8876                     }
8877                 }
8878             }
8879             this.removeClass("x-masked");
8880         },
8881
8882         /**
8883          * Returns true if this element is masked
8884          * @return {Boolean}
8885          */
8886         isMasked : function(){
8887             return this._mask && this._mask.isVisible();
8888         },
8889
8890         /**
8891          * Creates an iframe shim for this element to keep selects and other windowed objects from
8892          * showing through.
8893          * @return {Roo.Element} The new shim element
8894          */
8895         createShim : function(){
8896             var el = document.createElement('iframe');
8897             el.frameBorder = 'no';
8898             el.className = 'roo-shim';
8899             if(Roo.isIE && Roo.isSecure){
8900                 el.src = Roo.SSL_SECURE_URL;
8901             }
8902             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8903             shim.autoBoxAdjust = false;
8904             return shim;
8905         },
8906
8907         /**
8908          * Removes this element from the DOM and deletes it from the cache
8909          */
8910         remove : function(){
8911             if(this.dom.parentNode){
8912                 this.dom.parentNode.removeChild(this.dom);
8913             }
8914             delete El.cache[this.dom.id];
8915         },
8916
8917         /**
8918          * Sets up event handlers to add and remove a css class when the mouse is over this element
8919          * @param {String} className
8920          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8921          * mouseout events for children elements
8922          * @return {Roo.Element} this
8923          */
8924         addClassOnOver : function(className, preventFlicker){
8925             this.on("mouseover", function(){
8926                 Roo.fly(this, '_internal').addClass(className);
8927             }, this.dom);
8928             var removeFn = function(e){
8929                 if(preventFlicker !== true || !e.within(this, true)){
8930                     Roo.fly(this, '_internal').removeClass(className);
8931                 }
8932             };
8933             this.on("mouseout", removeFn, this.dom);
8934             return this;
8935         },
8936
8937         /**
8938          * Sets up event handlers to add and remove a css class when this element has the focus
8939          * @param {String} className
8940          * @return {Roo.Element} this
8941          */
8942         addClassOnFocus : function(className){
8943             this.on("focus", function(){
8944                 Roo.fly(this, '_internal').addClass(className);
8945             }, this.dom);
8946             this.on("blur", function(){
8947                 Roo.fly(this, '_internal').removeClass(className);
8948             }, this.dom);
8949             return this;
8950         },
8951         /**
8952          * 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)
8953          * @param {String} className
8954          * @return {Roo.Element} this
8955          */
8956         addClassOnClick : function(className){
8957             var dom = this.dom;
8958             this.on("mousedown", function(){
8959                 Roo.fly(dom, '_internal').addClass(className);
8960                 var d = Roo.get(document);
8961                 var fn = function(){
8962                     Roo.fly(dom, '_internal').removeClass(className);
8963                     d.removeListener("mouseup", fn);
8964                 };
8965                 d.on("mouseup", fn);
8966             });
8967             return this;
8968         },
8969
8970         /**
8971          * Stops the specified event from bubbling and optionally prevents the default action
8972          * @param {String} eventName
8973          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8974          * @return {Roo.Element} this
8975          */
8976         swallowEvent : function(eventName, preventDefault){
8977             var fn = function(e){
8978                 e.stopPropagation();
8979                 if(preventDefault){
8980                     e.preventDefault();
8981                 }
8982             };
8983             if(eventName instanceof Array){
8984                 for(var i = 0, len = eventName.length; i < len; i++){
8985                      this.on(eventName[i], fn);
8986                 }
8987                 return this;
8988             }
8989             this.on(eventName, fn);
8990             return this;
8991         },
8992
8993         /**
8994          * @private
8995          */
8996       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8997
8998         /**
8999          * Sizes this element to its parent element's dimensions performing
9000          * neccessary box adjustments.
9001          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9002          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9003          * @return {Roo.Element} this
9004          */
9005         fitToParent : function(monitorResize, targetParent) {
9006           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9007           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9008           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9009             return;
9010           }
9011           var p = Roo.get(targetParent || this.dom.parentNode);
9012           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9013           if (monitorResize === true) {
9014             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9015             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9016           }
9017           return this;
9018         },
9019
9020         /**
9021          * Gets the next sibling, skipping text nodes
9022          * @return {HTMLElement} The next sibling or null
9023          */
9024         getNextSibling : function(){
9025             var n = this.dom.nextSibling;
9026             while(n && n.nodeType != 1){
9027                 n = n.nextSibling;
9028             }
9029             return n;
9030         },
9031
9032         /**
9033          * Gets the previous sibling, skipping text nodes
9034          * @return {HTMLElement} The previous sibling or null
9035          */
9036         getPrevSibling : function(){
9037             var n = this.dom.previousSibling;
9038             while(n && n.nodeType != 1){
9039                 n = n.previousSibling;
9040             }
9041             return n;
9042         },
9043
9044
9045         /**
9046          * Appends the passed element(s) to this element
9047          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9048          * @return {Roo.Element} this
9049          */
9050         appendChild: function(el){
9051             el = Roo.get(el);
9052             el.appendTo(this);
9053             return this;
9054         },
9055
9056         /**
9057          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9058          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9059          * automatically generated with the specified attributes.
9060          * @param {HTMLElement} insertBefore (optional) a child element of this element
9061          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9062          * @return {Roo.Element} The new child element
9063          */
9064         createChild: function(config, insertBefore, returnDom){
9065             config = config || {tag:'div'};
9066             if(insertBefore){
9067                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9068             }
9069             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9070         },
9071
9072         /**
9073          * Appends this element to the passed element
9074          * @param {String/HTMLElement/Element} el The new parent element
9075          * @return {Roo.Element} this
9076          */
9077         appendTo: function(el){
9078             el = Roo.getDom(el);
9079             el.appendChild(this.dom);
9080             return this;
9081         },
9082
9083         /**
9084          * Inserts this element before the passed element in the DOM
9085          * @param {String/HTMLElement/Element} el The element to insert before
9086          * @return {Roo.Element} this
9087          */
9088         insertBefore: function(el){
9089             el = Roo.getDom(el);
9090             el.parentNode.insertBefore(this.dom, el);
9091             return this;
9092         },
9093
9094         /**
9095          * Inserts this element after the passed element in the DOM
9096          * @param {String/HTMLElement/Element} el The element to insert after
9097          * @return {Roo.Element} this
9098          */
9099         insertAfter: function(el){
9100             el = Roo.getDom(el);
9101             el.parentNode.insertBefore(this.dom, el.nextSibling);
9102             return this;
9103         },
9104
9105         /**
9106          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9107          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9108          * @return {Roo.Element} The new child
9109          */
9110         insertFirst: function(el, returnDom){
9111             el = el || {};
9112             if(typeof el == 'object' && !el.nodeType){ // dh config
9113                 return this.createChild(el, this.dom.firstChild, returnDom);
9114             }else{
9115                 el = Roo.getDom(el);
9116                 this.dom.insertBefore(el, this.dom.firstChild);
9117                 return !returnDom ? Roo.get(el) : el;
9118             }
9119         },
9120
9121         /**
9122          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9123          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9124          * @param {String} where (optional) 'before' or 'after' defaults to before
9125          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9126          * @return {Roo.Element} the inserted Element
9127          */
9128         insertSibling: function(el, where, returnDom){
9129             where = where ? where.toLowerCase() : 'before';
9130             el = el || {};
9131             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9132
9133             if(typeof el == 'object' && !el.nodeType){ // dh config
9134                 if(where == 'after' && !this.dom.nextSibling){
9135                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9136                 }else{
9137                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9138                 }
9139
9140             }else{
9141                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9142                             where == 'before' ? this.dom : this.dom.nextSibling);
9143                 if(!returnDom){
9144                     rt = Roo.get(rt);
9145                 }
9146             }
9147             return rt;
9148         },
9149
9150         /**
9151          * Creates and wraps this element with another element
9152          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9153          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9154          * @return {HTMLElement/Element} The newly created wrapper element
9155          */
9156         wrap: function(config, returnDom){
9157             if(!config){
9158                 config = {tag: "div"};
9159             }
9160             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9161             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9162             return newEl;
9163         },
9164
9165         /**
9166          * Replaces the passed element with this element
9167          * @param {String/HTMLElement/Element} el The element to replace
9168          * @return {Roo.Element} this
9169          */
9170         replace: function(el){
9171             el = Roo.get(el);
9172             this.insertBefore(el);
9173             el.remove();
9174             return this;
9175         },
9176
9177         /**
9178          * Inserts an html fragment into this element
9179          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9180          * @param {String} html The HTML fragment
9181          * @param {Boolean} returnEl True to return an Roo.Element
9182          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9183          */
9184         insertHtml : function(where, html, returnEl){
9185             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9186             return returnEl ? Roo.get(el) : el;
9187         },
9188
9189         /**
9190          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9191          * @param {Object} o The object with the attributes
9192          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9193          * @return {Roo.Element} this
9194          */
9195         set : function(o, useSet){
9196             var el = this.dom;
9197             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9198             for(var attr in o){
9199                 if(attr == "style" || typeof o[attr] == "function") continue;
9200                 if(attr=="cls"){
9201                     el.className = o["cls"];
9202                 }else{
9203                     if(useSet) el.setAttribute(attr, o[attr]);
9204                     else el[attr] = o[attr];
9205                 }
9206             }
9207             if(o.style){
9208                 Roo.DomHelper.applyStyles(el, o.style);
9209             }
9210             return this;
9211         },
9212
9213         /**
9214          * Convenience method for constructing a KeyMap
9215          * @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:
9216          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9217          * @param {Function} fn The function to call
9218          * @param {Object} scope (optional) The scope of the function
9219          * @return {Roo.KeyMap} The KeyMap created
9220          */
9221         addKeyListener : function(key, fn, scope){
9222             var config;
9223             if(typeof key != "object" || key instanceof Array){
9224                 config = {
9225                     key: key,
9226                     fn: fn,
9227                     scope: scope
9228                 };
9229             }else{
9230                 config = {
9231                     key : key.key,
9232                     shift : key.shift,
9233                     ctrl : key.ctrl,
9234                     alt : key.alt,
9235                     fn: fn,
9236                     scope: scope
9237                 };
9238             }
9239             return new Roo.KeyMap(this, config);
9240         },
9241
9242         /**
9243          * Creates a KeyMap for this element
9244          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9245          * @return {Roo.KeyMap} The KeyMap created
9246          */
9247         addKeyMap : function(config){
9248             return new Roo.KeyMap(this, config);
9249         },
9250
9251         /**
9252          * Returns true if this element is scrollable.
9253          * @return {Boolean}
9254          */
9255          isScrollable : function(){
9256             var dom = this.dom;
9257             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9258         },
9259
9260         /**
9261          * 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().
9262          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9263          * @param {Number} value The new scroll value
9264          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9265          * @return {Element} this
9266          */
9267
9268         scrollTo : function(side, value, animate){
9269             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9270             if(!animate || !A){
9271                 this.dom[prop] = value;
9272             }else{
9273                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9274                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9275             }
9276             return this;
9277         },
9278
9279         /**
9280          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9281          * within this element's scrollable range.
9282          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9283          * @param {Number} distance How far to scroll the element in pixels
9284          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9285          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9286          * was scrolled as far as it could go.
9287          */
9288          scroll : function(direction, distance, animate){
9289              if(!this.isScrollable()){
9290                  return;
9291              }
9292              var el = this.dom;
9293              var l = el.scrollLeft, t = el.scrollTop;
9294              var w = el.scrollWidth, h = el.scrollHeight;
9295              var cw = el.clientWidth, ch = el.clientHeight;
9296              direction = direction.toLowerCase();
9297              var scrolled = false;
9298              var a = this.preanim(arguments, 2);
9299              switch(direction){
9300                  case "l":
9301                  case "left":
9302                      if(w - l > cw){
9303                          var v = Math.min(l + distance, w-cw);
9304                          this.scrollTo("left", v, a);
9305                          scrolled = true;
9306                      }
9307                      break;
9308                 case "r":
9309                 case "right":
9310                      if(l > 0){
9311                          var v = Math.max(l - distance, 0);
9312                          this.scrollTo("left", v, a);
9313                          scrolled = true;
9314                      }
9315                      break;
9316                 case "t":
9317                 case "top":
9318                 case "up":
9319                      if(t > 0){
9320                          var v = Math.max(t - distance, 0);
9321                          this.scrollTo("top", v, a);
9322                          scrolled = true;
9323                      }
9324                      break;
9325                 case "b":
9326                 case "bottom":
9327                 case "down":
9328                      if(h - t > ch){
9329                          var v = Math.min(t + distance, h-ch);
9330                          this.scrollTo("top", v, a);
9331                          scrolled = true;
9332                      }
9333                      break;
9334              }
9335              return scrolled;
9336         },
9337
9338         /**
9339          * Translates the passed page coordinates into left/top css values for this element
9340          * @param {Number/Array} x The page x or an array containing [x, y]
9341          * @param {Number} y The page y
9342          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9343          */
9344         translatePoints : function(x, y){
9345             if(typeof x == 'object' || x instanceof Array){
9346                 y = x[1]; x = x[0];
9347             }
9348             var p = this.getStyle('position');
9349             var o = this.getXY();
9350
9351             var l = parseInt(this.getStyle('left'), 10);
9352             var t = parseInt(this.getStyle('top'), 10);
9353
9354             if(isNaN(l)){
9355                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9356             }
9357             if(isNaN(t)){
9358                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9359             }
9360
9361             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9362         },
9363
9364         /**
9365          * Returns the current scroll position of the element.
9366          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9367          */
9368         getScroll : function(){
9369             var d = this.dom, doc = document;
9370             if(d == doc || d == doc.body){
9371                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9372                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9373                 return {left: l, top: t};
9374             }else{
9375                 return {left: d.scrollLeft, top: d.scrollTop};
9376             }
9377         },
9378
9379         /**
9380          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9381          * are convert to standard 6 digit hex color.
9382          * @param {String} attr The css attribute
9383          * @param {String} defaultValue The default value to use when a valid color isn't found
9384          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9385          * YUI color anims.
9386          */
9387         getColor : function(attr, defaultValue, prefix){
9388             var v = this.getStyle(attr);
9389             if(!v || v == "transparent" || v == "inherit") {
9390                 return defaultValue;
9391             }
9392             var color = typeof prefix == "undefined" ? "#" : prefix;
9393             if(v.substr(0, 4) == "rgb("){
9394                 var rvs = v.slice(4, v.length -1).split(",");
9395                 for(var i = 0; i < 3; i++){
9396                     var h = parseInt(rvs[i]).toString(16);
9397                     if(h < 16){
9398                         h = "0" + h;
9399                     }
9400                     color += h;
9401                 }
9402             } else {
9403                 if(v.substr(0, 1) == "#"){
9404                     if(v.length == 4) {
9405                         for(var i = 1; i < 4; i++){
9406                             var c = v.charAt(i);
9407                             color +=  c + c;
9408                         }
9409                     }else if(v.length == 7){
9410                         color += v.substr(1);
9411                     }
9412                 }
9413             }
9414             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9415         },
9416
9417         /**
9418          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9419          * gradient background, rounded corners and a 4-way shadow.
9420          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9421          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9422          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9423          * @return {Roo.Element} this
9424          */
9425         boxWrap : function(cls){
9426             cls = cls || 'x-box';
9427             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9428             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9429             return el;
9430         },
9431
9432         /**
9433          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9434          * @param {String} namespace The namespace in which to look for the attribute
9435          * @param {String} name The attribute name
9436          * @return {String} The attribute value
9437          */
9438         getAttributeNS : Roo.isIE ? function(ns, name){
9439             var d = this.dom;
9440             var type = typeof d[ns+":"+name];
9441             if(type != 'undefined' && type != 'unknown'){
9442                 return d[ns+":"+name];
9443             }
9444             return d[name];
9445         } : function(ns, name){
9446             var d = this.dom;
9447             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9448         }
9449     };
9450
9451     var ep = El.prototype;
9452
9453     /**
9454      * Appends an event handler (Shorthand for addListener)
9455      * @param {String}   eventName     The type of event to append
9456      * @param {Function} fn        The method the event invokes
9457      * @param {Object} scope       (optional) The scope (this object) of the fn
9458      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9459      * @method
9460      */
9461     ep.on = ep.addListener;
9462         // backwards compat
9463     ep.mon = ep.addListener;
9464
9465     /**
9466      * Removes an event handler from this element (shorthand for removeListener)
9467      * @param {String} eventName the type of event to remove
9468      * @param {Function} fn the method the event invokes
9469      * @return {Roo.Element} this
9470      * @method
9471      */
9472     ep.un = ep.removeListener;
9473
9474     /**
9475      * true to automatically adjust width and height settings for box-model issues (default to true)
9476      */
9477     ep.autoBoxAdjust = true;
9478
9479     // private
9480     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9481
9482     // private
9483     El.addUnits = function(v, defaultUnit){
9484         if(v === "" || v == "auto"){
9485             return v;
9486         }
9487         if(v === undefined){
9488             return '';
9489         }
9490         if(typeof v == "number" || !El.unitPattern.test(v)){
9491             return v + (defaultUnit || 'px');
9492         }
9493         return v;
9494     };
9495
9496     // special markup used throughout Roo when box wrapping elements
9497     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>';
9498     /**
9499      * Visibility mode constant - Use visibility to hide element
9500      * @static
9501      * @type Number
9502      */
9503     El.VISIBILITY = 1;
9504     /**
9505      * Visibility mode constant - Use display to hide element
9506      * @static
9507      * @type Number
9508      */
9509     El.DISPLAY = 2;
9510
9511     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9512     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9513     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9514
9515
9516
9517     /**
9518      * @private
9519      */
9520     El.cache = {};
9521
9522     var docEl;
9523
9524     /**
9525      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9526      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9527      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9528      * @return {Element} The Element object
9529      * @static
9530      */
9531     El.get = function(el){
9532         var ex, elm, id;
9533         if(!el){ return null; }
9534         if(typeof el == "string"){ // element id
9535             if(!(elm = document.getElementById(el))){
9536                 return null;
9537             }
9538             if(ex = El.cache[el]){
9539                 ex.dom = elm;
9540             }else{
9541                 ex = El.cache[el] = new El(elm);
9542             }
9543             return ex;
9544         }else if(el.tagName){ // dom element
9545             if(!(id = el.id)){
9546                 id = Roo.id(el);
9547             }
9548             if(ex = El.cache[id]){
9549                 ex.dom = el;
9550             }else{
9551                 ex = El.cache[id] = new El(el);
9552             }
9553             return ex;
9554         }else if(el instanceof El){
9555             if(el != docEl){
9556                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9557                                                               // catch case where it hasn't been appended
9558                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9559             }
9560             return el;
9561         }else if(el.isComposite){
9562             return el;
9563         }else if(el instanceof Array){
9564             return El.select(el);
9565         }else if(el == document){
9566             // create a bogus element object representing the document object
9567             if(!docEl){
9568                 var f = function(){};
9569                 f.prototype = El.prototype;
9570                 docEl = new f();
9571                 docEl.dom = document;
9572             }
9573             return docEl;
9574         }
9575         return null;
9576     };
9577
9578     // private
9579     El.uncache = function(el){
9580         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9581             if(a[i]){
9582                 delete El.cache[a[i].id || a[i]];
9583             }
9584         }
9585     };
9586
9587     // private
9588     // Garbage collection - uncache elements/purge listeners on orphaned elements
9589     // so we don't hold a reference and cause the browser to retain them
9590     El.garbageCollect = function(){
9591         if(!Roo.enableGarbageCollector){
9592             clearInterval(El.collectorThread);
9593             return;
9594         }
9595         for(var eid in El.cache){
9596             var el = El.cache[eid], d = el.dom;
9597             // -------------------------------------------------------
9598             // Determining what is garbage:
9599             // -------------------------------------------------------
9600             // !d
9601             // dom node is null, definitely garbage
9602             // -------------------------------------------------------
9603             // !d.parentNode
9604             // no parentNode == direct orphan, definitely garbage
9605             // -------------------------------------------------------
9606             // !d.offsetParent && !document.getElementById(eid)
9607             // display none elements have no offsetParent so we will
9608             // also try to look it up by it's id. However, check
9609             // offsetParent first so we don't do unneeded lookups.
9610             // This enables collection of elements that are not orphans
9611             // directly, but somewhere up the line they have an orphan
9612             // parent.
9613             // -------------------------------------------------------
9614             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9615                 delete El.cache[eid];
9616                 if(d && Roo.enableListenerCollection){
9617                     E.purgeElement(d);
9618                 }
9619             }
9620         }
9621     }
9622     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9623
9624
9625     // dom is optional
9626     El.Flyweight = function(dom){
9627         this.dom = dom;
9628     };
9629     El.Flyweight.prototype = El.prototype;
9630
9631     El._flyweights = {};
9632     /**
9633      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9634      * the dom node can be overwritten by other code.
9635      * @param {String/HTMLElement} el The dom node or id
9636      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9637      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9638      * @static
9639      * @return {Element} The shared Element object
9640      */
9641     El.fly = function(el, named){
9642         named = named || '_global';
9643         el = Roo.getDom(el);
9644         if(!el){
9645             return null;
9646         }
9647         if(!El._flyweights[named]){
9648             El._flyweights[named] = new El.Flyweight();
9649         }
9650         El._flyweights[named].dom = el;
9651         return El._flyweights[named];
9652     };
9653
9654     /**
9655      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9656      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9657      * Shorthand of {@link Roo.Element#get}
9658      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9659      * @return {Element} The Element object
9660      * @member Roo
9661      * @method get
9662      */
9663     Roo.get = El.get;
9664     /**
9665      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9666      * the dom node can be overwritten by other code.
9667      * Shorthand of {@link Roo.Element#fly}
9668      * @param {String/HTMLElement} el The dom node or id
9669      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9670      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9671      * @static
9672      * @return {Element} The shared Element object
9673      * @member Roo
9674      * @method fly
9675      */
9676     Roo.fly = El.fly;
9677
9678     // speedy lookup for elements never to box adjust
9679     var noBoxAdjust = Roo.isStrict ? {
9680         select:1
9681     } : {
9682         input:1, select:1, textarea:1
9683     };
9684     if(Roo.isIE || Roo.isGecko){
9685         noBoxAdjust['button'] = 1;
9686     }
9687
9688
9689     Roo.EventManager.on(window, 'unload', function(){
9690         delete El.cache;
9691         delete El._flyweights;
9692     });
9693 })();
9694
9695
9696
9697
9698 if(Roo.DomQuery){
9699     Roo.Element.selectorFunction = Roo.DomQuery.select;
9700 }
9701
9702 Roo.Element.select = function(selector, unique, root){
9703     var els;
9704     if(typeof selector == "string"){
9705         els = Roo.Element.selectorFunction(selector, root);
9706     }else if(selector.length !== undefined){
9707         els = selector;
9708     }else{
9709         throw "Invalid selector";
9710     }
9711     if(unique === true){
9712         return new Roo.CompositeElement(els);
9713     }else{
9714         return new Roo.CompositeElementLite(els);
9715     }
9716 };
9717 /**
9718  * Selects elements based on the passed CSS selector to enable working on them as 1.
9719  * @param {String/Array} selector The CSS selector or an array of elements
9720  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9721  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9722  * @return {CompositeElementLite/CompositeElement}
9723  * @member Roo
9724  * @method select
9725  */
9726 Roo.select = Roo.Element.select;
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741 /*
9742  * Based on:
9743  * Ext JS Library 1.1.1
9744  * Copyright(c) 2006-2007, Ext JS, LLC.
9745  *
9746  * Originally Released Under LGPL - original licence link has changed is not relivant.
9747  *
9748  * Fork - LGPL
9749  * <script type="text/javascript">
9750  */
9751
9752
9753
9754 //Notifies Element that fx methods are available
9755 Roo.enableFx = true;
9756
9757 /**
9758  * @class Roo.Fx
9759  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9760  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9761  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9762  * Element effects to work.</p><br/>
9763  *
9764  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9765  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9766  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9767  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9768  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9769  * expected results and should be done with care.</p><br/>
9770  *
9771  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9772  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9773 <pre>
9774 Value  Description
9775 -----  -----------------------------
9776 tl     The top left corner
9777 t      The center of the top edge
9778 tr     The top right corner
9779 l      The center of the left edge
9780 r      The center of the right edge
9781 bl     The bottom left corner
9782 b      The center of the bottom edge
9783 br     The bottom right corner
9784 </pre>
9785  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9786  * below are common options that can be passed to any Fx method.</b>
9787  * @cfg {Function} callback A function called when the effect is finished
9788  * @cfg {Object} scope The scope of the effect function
9789  * @cfg {String} easing A valid Easing value for the effect
9790  * @cfg {String} afterCls A css class to apply after the effect
9791  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9792  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9793  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9794  * effects that end with the element being visually hidden, ignored otherwise)
9795  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9796  * a function which returns such a specification that will be applied to the Element after the effect finishes
9797  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9798  * @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
9799  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9800  */
9801 Roo.Fx = {
9802         /**
9803          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9804          * origin for the slide effect.  This function automatically handles wrapping the element with
9805          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9806          * Usage:
9807          *<pre><code>
9808 // default: slide the element in from the top
9809 el.slideIn();
9810
9811 // custom: slide the element in from the right with a 2-second duration
9812 el.slideIn('r', { duration: 2 });
9813
9814 // common config options shown with default values
9815 el.slideIn('t', {
9816     easing: 'easeOut',
9817     duration: .5
9818 });
9819 </code></pre>
9820          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9821          * @param {Object} options (optional) Object literal with any of the Fx config options
9822          * @return {Roo.Element} The Element
9823          */
9824     slideIn : function(anchor, o){
9825         var el = this.getFxEl();
9826         o = o || {};
9827
9828         el.queueFx(o, function(){
9829
9830             anchor = anchor || "t";
9831
9832             // fix display to visibility
9833             this.fixDisplay();
9834
9835             // restore values after effect
9836             var r = this.getFxRestore();
9837             var b = this.getBox();
9838             // fixed size for slide
9839             this.setSize(b);
9840
9841             // wrap if needed
9842             var wrap = this.fxWrap(r.pos, o, "hidden");
9843
9844             var st = this.dom.style;
9845             st.visibility = "visible";
9846             st.position = "absolute";
9847
9848             // clear out temp styles after slide and unwrap
9849             var after = function(){
9850                 el.fxUnwrap(wrap, r.pos, o);
9851                 st.width = r.width;
9852                 st.height = r.height;
9853                 el.afterFx(o);
9854             };
9855             // time to calc the positions
9856             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9857
9858             switch(anchor.toLowerCase()){
9859                 case "t":
9860                     wrap.setSize(b.width, 0);
9861                     st.left = st.bottom = "0";
9862                     a = {height: bh};
9863                 break;
9864                 case "l":
9865                     wrap.setSize(0, b.height);
9866                     st.right = st.top = "0";
9867                     a = {width: bw};
9868                 break;
9869                 case "r":
9870                     wrap.setSize(0, b.height);
9871                     wrap.setX(b.right);
9872                     st.left = st.top = "0";
9873                     a = {width: bw, points: pt};
9874                 break;
9875                 case "b":
9876                     wrap.setSize(b.width, 0);
9877                     wrap.setY(b.bottom);
9878                     st.left = st.top = "0";
9879                     a = {height: bh, points: pt};
9880                 break;
9881                 case "tl":
9882                     wrap.setSize(0, 0);
9883                     st.right = st.bottom = "0";
9884                     a = {width: bw, height: bh};
9885                 break;
9886                 case "bl":
9887                     wrap.setSize(0, 0);
9888                     wrap.setY(b.y+b.height);
9889                     st.right = st.top = "0";
9890                     a = {width: bw, height: bh, points: pt};
9891                 break;
9892                 case "br":
9893                     wrap.setSize(0, 0);
9894                     wrap.setXY([b.right, b.bottom]);
9895                     st.left = st.top = "0";
9896                     a = {width: bw, height: bh, points: pt};
9897                 break;
9898                 case "tr":
9899                     wrap.setSize(0, 0);
9900                     wrap.setX(b.x+b.width);
9901                     st.left = st.bottom = "0";
9902                     a = {width: bw, height: bh, points: pt};
9903                 break;
9904             }
9905             this.dom.style.visibility = "visible";
9906             wrap.show();
9907
9908             arguments.callee.anim = wrap.fxanim(a,
9909                 o,
9910                 'motion',
9911                 .5,
9912                 'easeOut', after);
9913         });
9914         return this;
9915     },
9916     
9917         /**
9918          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9919          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9920          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9921          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9922          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9923          * Usage:
9924          *<pre><code>
9925 // default: slide the element out to the top
9926 el.slideOut();
9927
9928 // custom: slide the element out to the right with a 2-second duration
9929 el.slideOut('r', { duration: 2 });
9930
9931 // common config options shown with default values
9932 el.slideOut('t', {
9933     easing: 'easeOut',
9934     duration: .5,
9935     remove: false,
9936     useDisplay: false
9937 });
9938 </code></pre>
9939          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9940          * @param {Object} options (optional) Object literal with any of the Fx config options
9941          * @return {Roo.Element} The Element
9942          */
9943     slideOut : function(anchor, o){
9944         var el = this.getFxEl();
9945         o = o || {};
9946
9947         el.queueFx(o, function(){
9948
9949             anchor = anchor || "t";
9950
9951             // restore values after effect
9952             var r = this.getFxRestore();
9953             
9954             var b = this.getBox();
9955             // fixed size for slide
9956             this.setSize(b);
9957
9958             // wrap if needed
9959             var wrap = this.fxWrap(r.pos, o, "visible");
9960
9961             var st = this.dom.style;
9962             st.visibility = "visible";
9963             st.position = "absolute";
9964
9965             wrap.setSize(b);
9966
9967             var after = function(){
9968                 if(o.useDisplay){
9969                     el.setDisplayed(false);
9970                 }else{
9971                     el.hide();
9972                 }
9973
9974                 el.fxUnwrap(wrap, r.pos, o);
9975
9976                 st.width = r.width;
9977                 st.height = r.height;
9978
9979                 el.afterFx(o);
9980             };
9981
9982             var a, zero = {to: 0};
9983             switch(anchor.toLowerCase()){
9984                 case "t":
9985                     st.left = st.bottom = "0";
9986                     a = {height: zero};
9987                 break;
9988                 case "l":
9989                     st.right = st.top = "0";
9990                     a = {width: zero};
9991                 break;
9992                 case "r":
9993                     st.left = st.top = "0";
9994                     a = {width: zero, points: {to:[b.right, b.y]}};
9995                 break;
9996                 case "b":
9997                     st.left = st.top = "0";
9998                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9999                 break;
10000                 case "tl":
10001                     st.right = st.bottom = "0";
10002                     a = {width: zero, height: zero};
10003                 break;
10004                 case "bl":
10005                     st.right = st.top = "0";
10006                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10007                 break;
10008                 case "br":
10009                     st.left = st.top = "0";
10010                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10011                 break;
10012                 case "tr":
10013                     st.left = st.bottom = "0";
10014                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10015                 break;
10016             }
10017
10018             arguments.callee.anim = wrap.fxanim(a,
10019                 o,
10020                 'motion',
10021                 .5,
10022                 "easeOut", after);
10023         });
10024         return this;
10025     },
10026
10027         /**
10028          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10029          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10030          * The element must be removed from the DOM using the 'remove' config option if desired.
10031          * Usage:
10032          *<pre><code>
10033 // default
10034 el.puff();
10035
10036 // common config options shown with default values
10037 el.puff({
10038     easing: 'easeOut',
10039     duration: .5,
10040     remove: false,
10041     useDisplay: false
10042 });
10043 </code></pre>
10044          * @param {Object} options (optional) Object literal with any of the Fx config options
10045          * @return {Roo.Element} The Element
10046          */
10047     puff : function(o){
10048         var el = this.getFxEl();
10049         o = o || {};
10050
10051         el.queueFx(o, function(){
10052             this.clearOpacity();
10053             this.show();
10054
10055             // restore values after effect
10056             var r = this.getFxRestore();
10057             var st = this.dom.style;
10058
10059             var after = function(){
10060                 if(o.useDisplay){
10061                     el.setDisplayed(false);
10062                 }else{
10063                     el.hide();
10064                 }
10065
10066                 el.clearOpacity();
10067
10068                 el.setPositioning(r.pos);
10069                 st.width = r.width;
10070                 st.height = r.height;
10071                 st.fontSize = '';
10072                 el.afterFx(o);
10073             };
10074
10075             var width = this.getWidth();
10076             var height = this.getHeight();
10077
10078             arguments.callee.anim = this.fxanim({
10079                     width : {to: this.adjustWidth(width * 2)},
10080                     height : {to: this.adjustHeight(height * 2)},
10081                     points : {by: [-(width * .5), -(height * .5)]},
10082                     opacity : {to: 0},
10083                     fontSize: {to:200, unit: "%"}
10084                 },
10085                 o,
10086                 'motion',
10087                 .5,
10088                 "easeOut", after);
10089         });
10090         return this;
10091     },
10092
10093         /**
10094          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10095          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10096          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10097          * Usage:
10098          *<pre><code>
10099 // default
10100 el.switchOff();
10101
10102 // all config options shown with default values
10103 el.switchOff({
10104     easing: 'easeIn',
10105     duration: .3,
10106     remove: false,
10107     useDisplay: false
10108 });
10109 </code></pre>
10110          * @param {Object} options (optional) Object literal with any of the Fx config options
10111          * @return {Roo.Element} The Element
10112          */
10113     switchOff : function(o){
10114         var el = this.getFxEl();
10115         o = o || {};
10116
10117         el.queueFx(o, function(){
10118             this.clearOpacity();
10119             this.clip();
10120
10121             // restore values after effect
10122             var r = this.getFxRestore();
10123             var st = this.dom.style;
10124
10125             var after = function(){
10126                 if(o.useDisplay){
10127                     el.setDisplayed(false);
10128                 }else{
10129                     el.hide();
10130                 }
10131
10132                 el.clearOpacity();
10133                 el.setPositioning(r.pos);
10134                 st.width = r.width;
10135                 st.height = r.height;
10136
10137                 el.afterFx(o);
10138             };
10139
10140             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10141                 this.clearOpacity();
10142                 (function(){
10143                     this.fxanim({
10144                         height:{to:1},
10145                         points:{by:[0, this.getHeight() * .5]}
10146                     }, o, 'motion', 0.3, 'easeIn', after);
10147                 }).defer(100, this);
10148             });
10149         });
10150         return this;
10151     },
10152
10153     /**
10154      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10155      * changed using the "attr" config option) and then fading back to the original color. If no original
10156      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10157      * Usage:
10158 <pre><code>
10159 // default: highlight background to yellow
10160 el.highlight();
10161
10162 // custom: highlight foreground text to blue for 2 seconds
10163 el.highlight("0000ff", { attr: 'color', duration: 2 });
10164
10165 // common config options shown with default values
10166 el.highlight("ffff9c", {
10167     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10168     endColor: (current color) or "ffffff",
10169     easing: 'easeIn',
10170     duration: 1
10171 });
10172 </code></pre>
10173      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10174      * @param {Object} options (optional) Object literal with any of the Fx config options
10175      * @return {Roo.Element} The Element
10176      */ 
10177     highlight : function(color, o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182             color = color || "ffff9c";
10183             attr = o.attr || "backgroundColor";
10184
10185             this.clearOpacity();
10186             this.show();
10187
10188             var origColor = this.getColor(attr);
10189             var restoreColor = this.dom.style[attr];
10190             endColor = (o.endColor || origColor) || "ffffff";
10191
10192             var after = function(){
10193                 el.dom.style[attr] = restoreColor;
10194                 el.afterFx(o);
10195             };
10196
10197             var a = {};
10198             a[attr] = {from: color, to: endColor};
10199             arguments.callee.anim = this.fxanim(a,
10200                 o,
10201                 'color',
10202                 1,
10203                 'easeIn', after);
10204         });
10205         return this;
10206     },
10207
10208    /**
10209     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10210     * Usage:
10211 <pre><code>
10212 // default: a single light blue ripple
10213 el.frame();
10214
10215 // custom: 3 red ripples lasting 3 seconds total
10216 el.frame("ff0000", 3, { duration: 3 });
10217
10218 // common config options shown with default values
10219 el.frame("C3DAF9", 1, {
10220     duration: 1 //duration of entire animation (not each individual ripple)
10221     // Note: Easing is not configurable and will be ignored if included
10222 });
10223 </code></pre>
10224     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10225     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10226     * @param {Object} options (optional) Object literal with any of the Fx config options
10227     * @return {Roo.Element} The Element
10228     */
10229     frame : function(color, count, o){
10230         var el = this.getFxEl();
10231         o = o || {};
10232
10233         el.queueFx(o, function(){
10234             color = color || "#C3DAF9";
10235             if(color.length == 6){
10236                 color = "#" + color;
10237             }
10238             count = count || 1;
10239             duration = o.duration || 1;
10240             this.show();
10241
10242             var b = this.getBox();
10243             var animFn = function(){
10244                 var proxy = this.createProxy({
10245
10246                      style:{
10247                         visbility:"hidden",
10248                         position:"absolute",
10249                         "z-index":"35000", // yee haw
10250                         border:"0px solid " + color
10251                      }
10252                   });
10253                 var scale = Roo.isBorderBox ? 2 : 1;
10254                 proxy.animate({
10255                     top:{from:b.y, to:b.y - 20},
10256                     left:{from:b.x, to:b.x - 20},
10257                     borderWidth:{from:0, to:10},
10258                     opacity:{from:1, to:0},
10259                     height:{from:b.height, to:(b.height + (20*scale))},
10260                     width:{from:b.width, to:(b.width + (20*scale))}
10261                 }, duration, function(){
10262                     proxy.remove();
10263                 });
10264                 if(--count > 0){
10265                      animFn.defer((duration/2)*1000, this);
10266                 }else{
10267                     el.afterFx(o);
10268                 }
10269             };
10270             animFn.call(this);
10271         });
10272         return this;
10273     },
10274
10275    /**
10276     * Creates a pause before any subsequent queued effects begin.  If there are
10277     * no effects queued after the pause it will have no effect.
10278     * Usage:
10279 <pre><code>
10280 el.pause(1);
10281 </code></pre>
10282     * @param {Number} seconds The length of time to pause (in seconds)
10283     * @return {Roo.Element} The Element
10284     */
10285     pause : function(seconds){
10286         var el = this.getFxEl();
10287         var o = {};
10288
10289         el.queueFx(o, function(){
10290             setTimeout(function(){
10291                 el.afterFx(o);
10292             }, seconds * 1000);
10293         });
10294         return this;
10295     },
10296
10297    /**
10298     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10299     * using the "endOpacity" config option.
10300     * Usage:
10301 <pre><code>
10302 // default: fade in from opacity 0 to 100%
10303 el.fadeIn();
10304
10305 // custom: fade in from opacity 0 to 75% over 2 seconds
10306 el.fadeIn({ endOpacity: .75, duration: 2});
10307
10308 // common config options shown with default values
10309 el.fadeIn({
10310     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10311     easing: 'easeOut',
10312     duration: .5
10313 });
10314 </code></pre>
10315     * @param {Object} options (optional) Object literal with any of the Fx config options
10316     * @return {Roo.Element} The Element
10317     */
10318     fadeIn : function(o){
10319         var el = this.getFxEl();
10320         o = o || {};
10321         el.queueFx(o, function(){
10322             this.setOpacity(0);
10323             this.fixDisplay();
10324             this.dom.style.visibility = 'visible';
10325             var to = o.endOpacity || 1;
10326             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10327                 o, null, .5, "easeOut", function(){
10328                 if(to == 1){
10329                     this.clearOpacity();
10330                 }
10331                 el.afterFx(o);
10332             });
10333         });
10334         return this;
10335     },
10336
10337    /**
10338     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10339     * using the "endOpacity" config option.
10340     * Usage:
10341 <pre><code>
10342 // default: fade out from the element's current opacity to 0
10343 el.fadeOut();
10344
10345 // custom: fade out from the element's current opacity to 25% over 2 seconds
10346 el.fadeOut({ endOpacity: .25, duration: 2});
10347
10348 // common config options shown with default values
10349 el.fadeOut({
10350     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10351     easing: 'easeOut',
10352     duration: .5
10353     remove: false,
10354     useDisplay: false
10355 });
10356 </code></pre>
10357     * @param {Object} options (optional) Object literal with any of the Fx config options
10358     * @return {Roo.Element} The Element
10359     */
10360     fadeOut : function(o){
10361         var el = this.getFxEl();
10362         o = o || {};
10363         el.queueFx(o, function(){
10364             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10365                 o, null, .5, "easeOut", function(){
10366                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10367                      this.dom.style.display = "none";
10368                 }else{
10369                      this.dom.style.visibility = "hidden";
10370                 }
10371                 this.clearOpacity();
10372                 el.afterFx(o);
10373             });
10374         });
10375         return this;
10376     },
10377
10378    /**
10379     * Animates the transition of an element's dimensions from a starting height/width
10380     * to an ending height/width.
10381     * Usage:
10382 <pre><code>
10383 // change height and width to 100x100 pixels
10384 el.scale(100, 100);
10385
10386 // common config options shown with default values.  The height and width will default to
10387 // the element's existing values if passed as null.
10388 el.scale(
10389     [element's width],
10390     [element's height], {
10391     easing: 'easeOut',
10392     duration: .35
10393 });
10394 </code></pre>
10395     * @param {Number} width  The new width (pass undefined to keep the original width)
10396     * @param {Number} height  The new height (pass undefined to keep the original height)
10397     * @param {Object} options (optional) Object literal with any of the Fx config options
10398     * @return {Roo.Element} The Element
10399     */
10400     scale : function(w, h, o){
10401         this.shift(Roo.apply({}, o, {
10402             width: w,
10403             height: h
10404         }));
10405         return this;
10406     },
10407
10408    /**
10409     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10410     * Any of these properties not specified in the config object will not be changed.  This effect 
10411     * requires that at least one new dimension, position or opacity setting must be passed in on
10412     * the config object in order for the function to have any effect.
10413     * Usage:
10414 <pre><code>
10415 // slide the element horizontally to x position 200 while changing the height and opacity
10416 el.shift({ x: 200, height: 50, opacity: .8 });
10417
10418 // common config options shown with default values.
10419 el.shift({
10420     width: [element's width],
10421     height: [element's height],
10422     x: [element's x position],
10423     y: [element's y position],
10424     opacity: [element's opacity],
10425     easing: 'easeOut',
10426     duration: .35
10427 });
10428 </code></pre>
10429     * @param {Object} options  Object literal with any of the Fx config options
10430     * @return {Roo.Element} The Element
10431     */
10432     shift : function(o){
10433         var el = this.getFxEl();
10434         o = o || {};
10435         el.queueFx(o, function(){
10436             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10437             if(w !== undefined){
10438                 a.width = {to: this.adjustWidth(w)};
10439             }
10440             if(h !== undefined){
10441                 a.height = {to: this.adjustHeight(h)};
10442             }
10443             if(x !== undefined || y !== undefined){
10444                 a.points = {to: [
10445                     x !== undefined ? x : this.getX(),
10446                     y !== undefined ? y : this.getY()
10447                 ]};
10448             }
10449             if(op !== undefined){
10450                 a.opacity = {to: op};
10451             }
10452             if(o.xy !== undefined){
10453                 a.points = {to: o.xy};
10454             }
10455             arguments.callee.anim = this.fxanim(a,
10456                 o, 'motion', .35, "easeOut", function(){
10457                 el.afterFx(o);
10458             });
10459         });
10460         return this;
10461     },
10462
10463         /**
10464          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10465          * ending point of the effect.
10466          * Usage:
10467          *<pre><code>
10468 // default: slide the element downward while fading out
10469 el.ghost();
10470
10471 // custom: slide the element out to the right with a 2-second duration
10472 el.ghost('r', { duration: 2 });
10473
10474 // common config options shown with default values
10475 el.ghost('b', {
10476     easing: 'easeOut',
10477     duration: .5
10478     remove: false,
10479     useDisplay: false
10480 });
10481 </code></pre>
10482          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10483          * @param {Object} options (optional) Object literal with any of the Fx config options
10484          * @return {Roo.Element} The Element
10485          */
10486     ghost : function(anchor, o){
10487         var el = this.getFxEl();
10488         o = o || {};
10489
10490         el.queueFx(o, function(){
10491             anchor = anchor || "b";
10492
10493             // restore values after effect
10494             var r = this.getFxRestore();
10495             var w = this.getWidth(),
10496                 h = this.getHeight();
10497
10498             var st = this.dom.style;
10499
10500             var after = function(){
10501                 if(o.useDisplay){
10502                     el.setDisplayed(false);
10503                 }else{
10504                     el.hide();
10505                 }
10506
10507                 el.clearOpacity();
10508                 el.setPositioning(r.pos);
10509                 st.width = r.width;
10510                 st.height = r.height;
10511
10512                 el.afterFx(o);
10513             };
10514
10515             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10516             switch(anchor.toLowerCase()){
10517                 case "t":
10518                     pt.by = [0, -h];
10519                 break;
10520                 case "l":
10521                     pt.by = [-w, 0];
10522                 break;
10523                 case "r":
10524                     pt.by = [w, 0];
10525                 break;
10526                 case "b":
10527                     pt.by = [0, h];
10528                 break;
10529                 case "tl":
10530                     pt.by = [-w, -h];
10531                 break;
10532                 case "bl":
10533                     pt.by = [-w, h];
10534                 break;
10535                 case "br":
10536                     pt.by = [w, h];
10537                 break;
10538                 case "tr":
10539                     pt.by = [w, -h];
10540                 break;
10541             }
10542
10543             arguments.callee.anim = this.fxanim(a,
10544                 o,
10545                 'motion',
10546                 .5,
10547                 "easeOut", after);
10548         });
10549         return this;
10550     },
10551
10552         /**
10553          * Ensures that all effects queued after syncFx is called on the element are
10554          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10555          * @return {Roo.Element} The Element
10556          */
10557     syncFx : function(){
10558         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10559             block : false,
10560             concurrent : true,
10561             stopFx : false
10562         });
10563         return this;
10564     },
10565
10566         /**
10567          * Ensures that all effects queued after sequenceFx is called on the element are
10568          * run in sequence.  This is the opposite of {@link #syncFx}.
10569          * @return {Roo.Element} The Element
10570          */
10571     sequenceFx : function(){
10572         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10573             block : false,
10574             concurrent : false,
10575             stopFx : false
10576         });
10577         return this;
10578     },
10579
10580         /* @private */
10581     nextFx : function(){
10582         var ef = this.fxQueue[0];
10583         if(ef){
10584             ef.call(this);
10585         }
10586     },
10587
10588         /**
10589          * Returns true if the element has any effects actively running or queued, else returns false.
10590          * @return {Boolean} True if element has active effects, else false
10591          */
10592     hasActiveFx : function(){
10593         return this.fxQueue && this.fxQueue[0];
10594     },
10595
10596         /**
10597          * Stops any running effects and clears the element's internal effects queue if it contains
10598          * any additional effects that haven't started yet.
10599          * @return {Roo.Element} The Element
10600          */
10601     stopFx : function(){
10602         if(this.hasActiveFx()){
10603             var cur = this.fxQueue[0];
10604             if(cur && cur.anim && cur.anim.isAnimated()){
10605                 this.fxQueue = [cur]; // clear out others
10606                 cur.anim.stop(true);
10607             }
10608         }
10609         return this;
10610     },
10611
10612         /* @private */
10613     beforeFx : function(o){
10614         if(this.hasActiveFx() && !o.concurrent){
10615            if(o.stopFx){
10616                this.stopFx();
10617                return true;
10618            }
10619            return false;
10620         }
10621         return true;
10622     },
10623
10624         /**
10625          * Returns true if the element is currently blocking so that no other effect can be queued
10626          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10627          * used to ensure that an effect initiated by a user action runs to completion prior to the
10628          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10629          * @return {Boolean} True if blocking, else false
10630          */
10631     hasFxBlock : function(){
10632         var q = this.fxQueue;
10633         return q && q[0] && q[0].block;
10634     },
10635
10636         /* @private */
10637     queueFx : function(o, fn){
10638         if(!this.fxQueue){
10639             this.fxQueue = [];
10640         }
10641         if(!this.hasFxBlock()){
10642             Roo.applyIf(o, this.fxDefaults);
10643             if(!o.concurrent){
10644                 var run = this.beforeFx(o);
10645                 fn.block = o.block;
10646                 this.fxQueue.push(fn);
10647                 if(run){
10648                     this.nextFx();
10649                 }
10650             }else{
10651                 fn.call(this);
10652             }
10653         }
10654         return this;
10655     },
10656
10657         /* @private */
10658     fxWrap : function(pos, o, vis){
10659         var wrap;
10660         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10661             var wrapXY;
10662             if(o.fixPosition){
10663                 wrapXY = this.getXY();
10664             }
10665             var div = document.createElement("div");
10666             div.style.visibility = vis;
10667             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10668             wrap.setPositioning(pos);
10669             if(wrap.getStyle("position") == "static"){
10670                 wrap.position("relative");
10671             }
10672             this.clearPositioning('auto');
10673             wrap.clip();
10674             wrap.dom.appendChild(this.dom);
10675             if(wrapXY){
10676                 wrap.setXY(wrapXY);
10677             }
10678         }
10679         return wrap;
10680     },
10681
10682         /* @private */
10683     fxUnwrap : function(wrap, pos, o){
10684         this.clearPositioning();
10685         this.setPositioning(pos);
10686         if(!o.wrap){
10687             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10688             wrap.remove();
10689         }
10690     },
10691
10692         /* @private */
10693     getFxRestore : function(){
10694         var st = this.dom.style;
10695         return {pos: this.getPositioning(), width: st.width, height : st.height};
10696     },
10697
10698         /* @private */
10699     afterFx : function(o){
10700         if(o.afterStyle){
10701             this.applyStyles(o.afterStyle);
10702         }
10703         if(o.afterCls){
10704             this.addClass(o.afterCls);
10705         }
10706         if(o.remove === true){
10707             this.remove();
10708         }
10709         Roo.callback(o.callback, o.scope, [this]);
10710         if(!o.concurrent){
10711             this.fxQueue.shift();
10712             this.nextFx();
10713         }
10714     },
10715
10716         /* @private */
10717     getFxEl : function(){ // support for composite element fx
10718         return Roo.get(this.dom);
10719     },
10720
10721         /* @private */
10722     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10723         animType = animType || 'run';
10724         opt = opt || {};
10725         var anim = Roo.lib.Anim[animType](
10726             this.dom, args,
10727             (opt.duration || defaultDur) || .35,
10728             (opt.easing || defaultEase) || 'easeOut',
10729             function(){
10730                 Roo.callback(cb, this);
10731             },
10732             this
10733         );
10734         opt.anim = anim;
10735         return anim;
10736     }
10737 };
10738
10739 // backwords compat
10740 Roo.Fx.resize = Roo.Fx.scale;
10741
10742 //When included, Roo.Fx is automatically applied to Element so that all basic
10743 //effects are available directly via the Element API
10744 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10745  * Based on:
10746  * Ext JS Library 1.1.1
10747  * Copyright(c) 2006-2007, Ext JS, LLC.
10748  *
10749  * Originally Released Under LGPL - original licence link has changed is not relivant.
10750  *
10751  * Fork - LGPL
10752  * <script type="text/javascript">
10753  */
10754
10755
10756 /**
10757  * @class Roo.CompositeElement
10758  * Standard composite class. Creates a Roo.Element for every element in the collection.
10759  * <br><br>
10760  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10761  * actions will be performed on all the elements in this collection.</b>
10762  * <br><br>
10763  * All methods return <i>this</i> and can be chained.
10764  <pre><code>
10765  var els = Roo.select("#some-el div.some-class", true);
10766  // or select directly from an existing element
10767  var el = Roo.get('some-el');
10768  el.select('div.some-class', true);
10769
10770  els.setWidth(100); // all elements become 100 width
10771  els.hide(true); // all elements fade out and hide
10772  // or
10773  els.setWidth(100).hide(true);
10774  </code></pre>
10775  */
10776 Roo.CompositeElement = function(els){
10777     this.elements = [];
10778     this.addElements(els);
10779 };
10780 Roo.CompositeElement.prototype = {
10781     isComposite: true,
10782     addElements : function(els){
10783         if(!els) return this;
10784         if(typeof els == "string"){
10785             els = Roo.Element.selectorFunction(els);
10786         }
10787         var yels = this.elements;
10788         var index = yels.length-1;
10789         for(var i = 0, len = els.length; i < len; i++) {
10790                 yels[++index] = Roo.get(els[i]);
10791         }
10792         return this;
10793     },
10794
10795     /**
10796     * Clears this composite and adds the elements returned by the passed selector.
10797     * @param {String/Array} els A string CSS selector, an array of elements or an element
10798     * @return {CompositeElement} this
10799     */
10800     fill : function(els){
10801         this.elements = [];
10802         this.add(els);
10803         return this;
10804     },
10805
10806     /**
10807     * Filters this composite to only elements that match the passed selector.
10808     * @param {String} selector A string CSS selector
10809     * @return {CompositeElement} this
10810     */
10811     filter : function(selector){
10812         var els = [];
10813         this.each(function(el){
10814             if(el.is(selector)){
10815                 els[els.length] = el.dom;
10816             }
10817         });
10818         this.fill(els);
10819         return this;
10820     },
10821
10822     invoke : function(fn, args){
10823         var els = this.elements;
10824         for(var i = 0, len = els.length; i < len; i++) {
10825                 Roo.Element.prototype[fn].apply(els[i], args);
10826         }
10827         return this;
10828     },
10829     /**
10830     * Adds elements to this composite.
10831     * @param {String/Array} els A string CSS selector, an array of elements or an element
10832     * @return {CompositeElement} this
10833     */
10834     add : function(els){
10835         if(typeof els == "string"){
10836             this.addElements(Roo.Element.selectorFunction(els));
10837         }else if(els.length !== undefined){
10838             this.addElements(els);
10839         }else{
10840             this.addElements([els]);
10841         }
10842         return this;
10843     },
10844     /**
10845     * Calls the passed function passing (el, this, index) for each element in this composite.
10846     * @param {Function} fn The function to call
10847     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10848     * @return {CompositeElement} this
10849     */
10850     each : function(fn, scope){
10851         var els = this.elements;
10852         for(var i = 0, len = els.length; i < len; i++){
10853             if(fn.call(scope || els[i], els[i], this, i) === false) {
10854                 break;
10855             }
10856         }
10857         return this;
10858     },
10859
10860     /**
10861      * Returns the Element object at the specified index
10862      * @param {Number} index
10863      * @return {Roo.Element}
10864      */
10865     item : function(index){
10866         return this.elements[index] || null;
10867     },
10868
10869     /**
10870      * Returns the first Element
10871      * @return {Roo.Element}
10872      */
10873     first : function(){
10874         return this.item(0);
10875     },
10876
10877     /**
10878      * Returns the last Element
10879      * @return {Roo.Element}
10880      */
10881     last : function(){
10882         return this.item(this.elements.length-1);
10883     },
10884
10885     /**
10886      * Returns the number of elements in this composite
10887      * @return Number
10888      */
10889     getCount : function(){
10890         return this.elements.length;
10891     },
10892
10893     /**
10894      * Returns true if this composite contains the passed element
10895      * @return Boolean
10896      */
10897     contains : function(el){
10898         return this.indexOf(el) !== -1;
10899     },
10900
10901     /**
10902      * Returns true if this composite contains the passed element
10903      * @return Boolean
10904      */
10905     indexOf : function(el){
10906         return this.elements.indexOf(Roo.get(el));
10907     },
10908
10909
10910     /**
10911     * Removes the specified element(s).
10912     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10913     * or an array of any of those.
10914     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10915     * @return {CompositeElement} this
10916     */
10917     removeElement : function(el, removeDom){
10918         if(el instanceof Array){
10919             for(var i = 0, len = el.length; i < len; i++){
10920                 this.removeElement(el[i]);
10921             }
10922             return this;
10923         }
10924         var index = typeof el == 'number' ? el : this.indexOf(el);
10925         if(index !== -1){
10926             if(removeDom){
10927                 var d = this.elements[index];
10928                 if(d.dom){
10929                     d.remove();
10930                 }else{
10931                     d.parentNode.removeChild(d);
10932                 }
10933             }
10934             this.elements.splice(index, 1);
10935         }
10936         return this;
10937     },
10938
10939     /**
10940     * Replaces the specified element with the passed element.
10941     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10942     * to replace.
10943     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10944     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10945     * @return {CompositeElement} this
10946     */
10947     replaceElement : function(el, replacement, domReplace){
10948         var index = typeof el == 'number' ? el : this.indexOf(el);
10949         if(index !== -1){
10950             if(domReplace){
10951                 this.elements[index].replaceWith(replacement);
10952             }else{
10953                 this.elements.splice(index, 1, Roo.get(replacement))
10954             }
10955         }
10956         return this;
10957     },
10958
10959     /**
10960      * Removes all elements.
10961      */
10962     clear : function(){
10963         this.elements = [];
10964     }
10965 };
10966 (function(){
10967     Roo.CompositeElement.createCall = function(proto, fnName){
10968         if(!proto[fnName]){
10969             proto[fnName] = function(){
10970                 return this.invoke(fnName, arguments);
10971             };
10972         }
10973     };
10974     for(var fnName in Roo.Element.prototype){
10975         if(typeof Roo.Element.prototype[fnName] == "function"){
10976             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10977         }
10978     };
10979 })();
10980 /*
10981  * Based on:
10982  * Ext JS Library 1.1.1
10983  * Copyright(c) 2006-2007, Ext JS, LLC.
10984  *
10985  * Originally Released Under LGPL - original licence link has changed is not relivant.
10986  *
10987  * Fork - LGPL
10988  * <script type="text/javascript">
10989  */
10990
10991 /**
10992  * @class Roo.CompositeElementLite
10993  * @extends Roo.CompositeElement
10994  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10995  <pre><code>
10996  var els = Roo.select("#some-el div.some-class");
10997  // or select directly from an existing element
10998  var el = Roo.get('some-el');
10999  el.select('div.some-class');
11000
11001  els.setWidth(100); // all elements become 100 width
11002  els.hide(true); // all elements fade out and hide
11003  // or
11004  els.setWidth(100).hide(true);
11005  </code></pre><br><br>
11006  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11007  * actions will be performed on all the elements in this collection.</b>
11008  */
11009 Roo.CompositeElementLite = function(els){
11010     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11011     this.el = new Roo.Element.Flyweight();
11012 };
11013 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11014     addElements : function(els){
11015         if(els){
11016             if(els instanceof Array){
11017                 this.elements = this.elements.concat(els);
11018             }else{
11019                 var yels = this.elements;
11020                 var index = yels.length-1;
11021                 for(var i = 0, len = els.length; i < len; i++) {
11022                     yels[++index] = els[i];
11023                 }
11024             }
11025         }
11026         return this;
11027     },
11028     invoke : function(fn, args){
11029         var els = this.elements;
11030         var el = this.el;
11031         for(var i = 0, len = els.length; i < len; i++) {
11032             el.dom = els[i];
11033                 Roo.Element.prototype[fn].apply(el, args);
11034         }
11035         return this;
11036     },
11037     /**
11038      * Returns a flyweight Element of the dom element object at the specified index
11039      * @param {Number} index
11040      * @return {Roo.Element}
11041      */
11042     item : function(index){
11043         if(!this.elements[index]){
11044             return null;
11045         }
11046         this.el.dom = this.elements[index];
11047         return this.el;
11048     },
11049
11050     // fixes scope with flyweight
11051     addListener : function(eventName, handler, scope, opt){
11052         var els = this.elements;
11053         for(var i = 0, len = els.length; i < len; i++) {
11054             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11055         }
11056         return this;
11057     },
11058
11059     /**
11060     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11061     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11062     * a reference to the dom node, use el.dom.</b>
11063     * @param {Function} fn The function to call
11064     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11065     * @return {CompositeElement} this
11066     */
11067     each : function(fn, scope){
11068         var els = this.elements;
11069         var el = this.el;
11070         for(var i = 0, len = els.length; i < len; i++){
11071             el.dom = els[i];
11072                 if(fn.call(scope || el, el, this, i) === false){
11073                 break;
11074             }
11075         }
11076         return this;
11077     },
11078
11079     indexOf : function(el){
11080         return this.elements.indexOf(Roo.getDom(el));
11081     },
11082
11083     replaceElement : function(el, replacement, domReplace){
11084         var index = typeof el == 'number' ? el : this.indexOf(el);
11085         if(index !== -1){
11086             replacement = Roo.getDom(replacement);
11087             if(domReplace){
11088                 var d = this.elements[index];
11089                 d.parentNode.insertBefore(replacement, d);
11090                 d.parentNode.removeChild(d);
11091             }
11092             this.elements.splice(index, 1, replacement);
11093         }
11094         return this;
11095     }
11096 });
11097 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11098
11099 /*
11100  * Based on:
11101  * Ext JS Library 1.1.1
11102  * Copyright(c) 2006-2007, Ext JS, LLC.
11103  *
11104  * Originally Released Under LGPL - original licence link has changed is not relivant.
11105  *
11106  * Fork - LGPL
11107  * <script type="text/javascript">
11108  */
11109
11110  
11111
11112 /**
11113  * @class Roo.data.Connection
11114  * @extends Roo.util.Observable
11115  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11116  * either to a configured URL, or to a URL specified at request time.<br><br>
11117  * <p>
11118  * Requests made by this class are asynchronous, and will return immediately. No data from
11119  * the server will be available to the statement immediately following the {@link #request} call.
11120  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11121  * <p>
11122  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11123  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11124  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11125  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11126  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11127  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11128  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11129  * standard DOM methods.
11130  * @constructor
11131  * @param {Object} config a configuration object.
11132  */
11133 Roo.data.Connection = function(config){
11134     Roo.apply(this, config);
11135     this.addEvents({
11136         /**
11137          * @event beforerequest
11138          * Fires before a network request is made to retrieve a data object.
11139          * @param {Connection} conn This Connection object.
11140          * @param {Object} options The options config object passed to the {@link #request} method.
11141          */
11142         "beforerequest" : true,
11143         /**
11144          * @event requestcomplete
11145          * Fires if the request was successfully completed.
11146          * @param {Connection} conn This Connection object.
11147          * @param {Object} response The XHR object containing the response data.
11148          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11149          * @param {Object} options The options config object passed to the {@link #request} method.
11150          */
11151         "requestcomplete" : true,
11152         /**
11153          * @event requestexception
11154          * Fires if an error HTTP status was returned from the server.
11155          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11156          * @param {Connection} conn This Connection object.
11157          * @param {Object} response The XHR object containing the response data.
11158          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11159          * @param {Object} options The options config object passed to the {@link #request} method.
11160          */
11161         "requestexception" : true
11162     });
11163     Roo.data.Connection.superclass.constructor.call(this);
11164 };
11165
11166 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11167     /**
11168      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11169      */
11170     /**
11171      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11172      * extra parameters to each request made by this object. (defaults to undefined)
11173      */
11174     /**
11175      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11176      *  to each request made by this object. (defaults to undefined)
11177      */
11178     /**
11179      * @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)
11180      */
11181     /**
11182      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11183      */
11184     timeout : 30000,
11185     /**
11186      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11187      * @type Boolean
11188      */
11189     autoAbort:false,
11190
11191     /**
11192      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11193      * @type Boolean
11194      */
11195     disableCaching: true,
11196
11197     /**
11198      * Sends an HTTP request to a remote server.
11199      * @param {Object} options An object which may contain the following properties:<ul>
11200      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11201      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11202      * request, a url encoded string or a function to call to get either.</li>
11203      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11204      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11205      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11206      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11207      * <li>options {Object} The parameter to the request call.</li>
11208      * <li>success {Boolean} True if the request succeeded.</li>
11209      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11210      * </ul></li>
11211      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11212      * The callback is passed the following parameters:<ul>
11213      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11214      * <li>options {Object} The parameter to the request call.</li>
11215      * </ul></li>
11216      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11217      * The callback is passed the following parameters:<ul>
11218      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11219      * <li>options {Object} The parameter to the request call.</li>
11220      * </ul></li>
11221      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11222      * for the callback function. Defaults to the browser window.</li>
11223      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11224      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11225      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11226      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11227      * params for the post data. Any params will be appended to the URL.</li>
11228      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11229      * </ul>
11230      * @return {Number} transactionId
11231      */
11232     request : function(o){
11233         if(this.fireEvent("beforerequest", this, o) !== false){
11234             var p = o.params;
11235
11236             if(typeof p == "function"){
11237                 p = p.call(o.scope||window, o);
11238             }
11239             if(typeof p == "object"){
11240                 p = Roo.urlEncode(o.params);
11241             }
11242             if(this.extraParams){
11243                 var extras = Roo.urlEncode(this.extraParams);
11244                 p = p ? (p + '&' + extras) : extras;
11245             }
11246
11247             var url = o.url || this.url;
11248             if(typeof url == 'function'){
11249                 url = url.call(o.scope||window, o);
11250             }
11251
11252             if(o.form){
11253                 var form = Roo.getDom(o.form);
11254                 url = url || form.action;
11255
11256                 var enctype = form.getAttribute("enctype");
11257                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11258                     return this.doFormUpload(o, p, url);
11259                 }
11260                 var f = Roo.lib.Ajax.serializeForm(form);
11261                 p = p ? (p + '&' + f) : f;
11262             }
11263
11264             var hs = o.headers;
11265             if(this.defaultHeaders){
11266                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11267                 if(!o.headers){
11268                     o.headers = hs;
11269                 }
11270             }
11271
11272             var cb = {
11273                 success: this.handleResponse,
11274                 failure: this.handleFailure,
11275                 scope: this,
11276                 argument: {options: o},
11277                 timeout : this.timeout
11278             };
11279
11280             var method = o.method||this.method||(p ? "POST" : "GET");
11281
11282             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11283                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11284             }
11285
11286             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11287                 if(o.autoAbort){
11288                     this.abort();
11289                 }
11290             }else if(this.autoAbort !== false){
11291                 this.abort();
11292             }
11293
11294             if((method == 'GET' && p) || o.xmlData){
11295                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11296                 p = '';
11297             }
11298             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11299             return this.transId;
11300         }else{
11301             Roo.callback(o.callback, o.scope, [o, null, null]);
11302             return null;
11303         }
11304     },
11305
11306     /**
11307      * Determine whether this object has a request outstanding.
11308      * @param {Number} transactionId (Optional) defaults to the last transaction
11309      * @return {Boolean} True if there is an outstanding request.
11310      */
11311     isLoading : function(transId){
11312         if(transId){
11313             return Roo.lib.Ajax.isCallInProgress(transId);
11314         }else{
11315             return this.transId ? true : false;
11316         }
11317     },
11318
11319     /**
11320      * Aborts any outstanding request.
11321      * @param {Number} transactionId (Optional) defaults to the last transaction
11322      */
11323     abort : function(transId){
11324         if(transId || this.isLoading()){
11325             Roo.lib.Ajax.abort(transId || this.transId);
11326         }
11327     },
11328
11329     // private
11330     handleResponse : function(response){
11331         this.transId = false;
11332         var options = response.argument.options;
11333         response.argument = options ? options.argument : null;
11334         this.fireEvent("requestcomplete", this, response, options);
11335         Roo.callback(options.success, options.scope, [response, options]);
11336         Roo.callback(options.callback, options.scope, [options, true, response]);
11337     },
11338
11339     // private
11340     handleFailure : function(response, e){
11341         this.transId = false;
11342         var options = response.argument.options;
11343         response.argument = options ? options.argument : null;
11344         this.fireEvent("requestexception", this, response, options, e);
11345         Roo.callback(options.failure, options.scope, [response, options]);
11346         Roo.callback(options.callback, options.scope, [options, false, response]);
11347     },
11348
11349     // private
11350     doFormUpload : function(o, ps, url){
11351         var id = Roo.id();
11352         var frame = document.createElement('iframe');
11353         frame.id = id;
11354         frame.name = id;
11355         frame.className = 'x-hidden';
11356         if(Roo.isIE){
11357             frame.src = Roo.SSL_SECURE_URL;
11358         }
11359         document.body.appendChild(frame);
11360
11361         if(Roo.isIE){
11362            document.frames[id].name = id;
11363         }
11364
11365         var form = Roo.getDom(o.form);
11366         form.target = id;
11367         form.method = 'POST';
11368         form.enctype = form.encoding = 'multipart/form-data';
11369         if(url){
11370             form.action = url;
11371         }
11372
11373         var hiddens, hd;
11374         if(ps){ // add dynamic params
11375             hiddens = [];
11376             ps = Roo.urlDecode(ps, false);
11377             for(var k in ps){
11378                 if(ps.hasOwnProperty(k)){
11379                     hd = document.createElement('input');
11380                     hd.type = 'hidden';
11381                     hd.name = k;
11382                     hd.value = ps[k];
11383                     form.appendChild(hd);
11384                     hiddens.push(hd);
11385                 }
11386             }
11387         }
11388
11389         function cb(){
11390             var r = {  // bogus response object
11391                 responseText : '',
11392                 responseXML : null
11393             };
11394
11395             r.argument = o ? o.argument : null;
11396
11397             try { //
11398                 var doc;
11399                 if(Roo.isIE){
11400                     doc = frame.contentWindow.document;
11401                 }else {
11402                     doc = (frame.contentDocument || window.frames[id].document);
11403                 }
11404                 if(doc && doc.body){
11405                     r.responseText = doc.body.innerHTML;
11406                 }
11407                 if(doc && doc.XMLDocument){
11408                     r.responseXML = doc.XMLDocument;
11409                 }else {
11410                     r.responseXML = doc;
11411                 }
11412             }
11413             catch(e) {
11414                 // ignore
11415             }
11416
11417             Roo.EventManager.removeListener(frame, 'load', cb, this);
11418
11419             this.fireEvent("requestcomplete", this, r, o);
11420             Roo.callback(o.success, o.scope, [r, o]);
11421             Roo.callback(o.callback, o.scope, [o, true, r]);
11422
11423             setTimeout(function(){document.body.removeChild(frame);}, 100);
11424         }
11425
11426         Roo.EventManager.on(frame, 'load', cb, this);
11427         form.submit();
11428
11429         if(hiddens){ // remove dynamic params
11430             for(var i = 0, len = hiddens.length; i < len; i++){
11431                 form.removeChild(hiddens[i]);
11432             }
11433         }
11434     }
11435 });
11436
11437 /**
11438  * @class Roo.Ajax
11439  * @extends Roo.data.Connection
11440  * Global Ajax request class.
11441  *
11442  * @singleton
11443  */
11444 Roo.Ajax = new Roo.data.Connection({
11445     // fix up the docs
11446    /**
11447      * @cfg {String} url @hide
11448      */
11449     /**
11450      * @cfg {Object} extraParams @hide
11451      */
11452     /**
11453      * @cfg {Object} defaultHeaders @hide
11454      */
11455     /**
11456      * @cfg {String} method (Optional) @hide
11457      */
11458     /**
11459      * @cfg {Number} timeout (Optional) @hide
11460      */
11461     /**
11462      * @cfg {Boolean} autoAbort (Optional) @hide
11463      */
11464
11465     /**
11466      * @cfg {Boolean} disableCaching (Optional) @hide
11467      */
11468
11469     /**
11470      * @property  disableCaching
11471      * True to add a unique cache-buster param to GET requests. (defaults to true)
11472      * @type Boolean
11473      */
11474     /**
11475      * @property  url
11476      * The default URL to be used for requests to the server. (defaults to undefined)
11477      * @type String
11478      */
11479     /**
11480      * @property  extraParams
11481      * An object containing properties which are used as
11482      * extra parameters to each request made by this object. (defaults to undefined)
11483      * @type Object
11484      */
11485     /**
11486      * @property  defaultHeaders
11487      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11488      * @type Object
11489      */
11490     /**
11491      * @property  method
11492      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11493      * @type String
11494      */
11495     /**
11496      * @property  timeout
11497      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11498      * @type Number
11499      */
11500
11501     /**
11502      * @property  autoAbort
11503      * Whether a new request should abort any pending requests. (defaults to false)
11504      * @type Boolean
11505      */
11506     autoAbort : false,
11507
11508     /**
11509      * Serialize the passed form into a url encoded string
11510      * @param {String/HTMLElement} form
11511      * @return {String}
11512      */
11513     serializeForm : function(form){
11514         return Roo.lib.Ajax.serializeForm(form);
11515     }
11516 });/*
11517  * Based on:
11518  * Ext JS Library 1.1.1
11519  * Copyright(c) 2006-2007, Ext JS, LLC.
11520  *
11521  * Originally Released Under LGPL - original licence link has changed is not relivant.
11522  *
11523  * Fork - LGPL
11524  * <script type="text/javascript">
11525  */
11526  
11527 /**
11528  * @class Roo.Ajax
11529  * @extends Roo.data.Connection
11530  * Global Ajax request class.
11531  *
11532  * @instanceOf  Roo.data.Connection
11533  */
11534 Roo.Ajax = new Roo.data.Connection({
11535     // fix up the docs
11536     
11537     /**
11538      * fix up scoping
11539      * @scope Roo.Ajax
11540      */
11541     
11542    /**
11543      * @cfg {String} url @hide
11544      */
11545     /**
11546      * @cfg {Object} extraParams @hide
11547      */
11548     /**
11549      * @cfg {Object} defaultHeaders @hide
11550      */
11551     /**
11552      * @cfg {String} method (Optional) @hide
11553      */
11554     /**
11555      * @cfg {Number} timeout (Optional) @hide
11556      */
11557     /**
11558      * @cfg {Boolean} autoAbort (Optional) @hide
11559      */
11560
11561     /**
11562      * @cfg {Boolean} disableCaching (Optional) @hide
11563      */
11564
11565     /**
11566      * @property  disableCaching
11567      * True to add a unique cache-buster param to GET requests. (defaults to true)
11568      * @type Boolean
11569      */
11570     /**
11571      * @property  url
11572      * The default URL to be used for requests to the server. (defaults to undefined)
11573      * @type String
11574      */
11575     /**
11576      * @property  extraParams
11577      * An object containing properties which are used as
11578      * extra parameters to each request made by this object. (defaults to undefined)
11579      * @type Object
11580      */
11581     /**
11582      * @property  defaultHeaders
11583      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11584      * @type Object
11585      */
11586     /**
11587      * @property  method
11588      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11589      * @type String
11590      */
11591     /**
11592      * @property  timeout
11593      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11594      * @type Number
11595      */
11596
11597     /**
11598      * @property  autoAbort
11599      * Whether a new request should abort any pending requests. (defaults to false)
11600      * @type Boolean
11601      */
11602     autoAbort : false,
11603
11604     /**
11605      * Serialize the passed form into a url encoded string
11606      * @param {String/HTMLElement} form
11607      * @return {String}
11608      */
11609     serializeForm : function(form){
11610         return Roo.lib.Ajax.serializeForm(form);
11611     }
11612 });/*
11613  * Based on:
11614  * Ext JS Library 1.1.1
11615  * Copyright(c) 2006-2007, Ext JS, LLC.
11616  *
11617  * Originally Released Under LGPL - original licence link has changed is not relivant.
11618  *
11619  * Fork - LGPL
11620  * <script type="text/javascript">
11621  */
11622
11623  
11624 /**
11625  * @class Roo.UpdateManager
11626  * @extends Roo.util.Observable
11627  * Provides AJAX-style update for Element object.<br><br>
11628  * Usage:<br>
11629  * <pre><code>
11630  * // Get it from a Roo.Element object
11631  * var el = Roo.get("foo");
11632  * var mgr = el.getUpdateManager();
11633  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11634  * ...
11635  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11636  * <br>
11637  * // or directly (returns the same UpdateManager instance)
11638  * var mgr = new Roo.UpdateManager("myElementId");
11639  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11640  * mgr.on("update", myFcnNeedsToKnow);
11641  * <br>
11642    // short handed call directly from the element object
11643    Roo.get("foo").load({
11644         url: "bar.php",
11645         scripts:true,
11646         params: "for=bar",
11647         text: "Loading Foo..."
11648    });
11649  * </code></pre>
11650  * @constructor
11651  * Create new UpdateManager directly.
11652  * @param {String/HTMLElement/Roo.Element} el The element to update
11653  * @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).
11654  */
11655 Roo.UpdateManager = function(el, forceNew){
11656     el = Roo.get(el);
11657     if(!forceNew && el.updateManager){
11658         return el.updateManager;
11659     }
11660     /**
11661      * The Element object
11662      * @type Roo.Element
11663      */
11664     this.el = el;
11665     /**
11666      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11667      * @type String
11668      */
11669     this.defaultUrl = null;
11670
11671     this.addEvents({
11672         /**
11673          * @event beforeupdate
11674          * Fired before an update is made, return false from your handler and the update is cancelled.
11675          * @param {Roo.Element} el
11676          * @param {String/Object/Function} url
11677          * @param {String/Object} params
11678          */
11679         "beforeupdate": true,
11680         /**
11681          * @event update
11682          * Fired after successful update is made.
11683          * @param {Roo.Element} el
11684          * @param {Object} oResponseObject The response Object
11685          */
11686         "update": true,
11687         /**
11688          * @event failure
11689          * Fired on update failure.
11690          * @param {Roo.Element} el
11691          * @param {Object} oResponseObject The response Object
11692          */
11693         "failure": true
11694     });
11695     var d = Roo.UpdateManager.defaults;
11696     /**
11697      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11698      * @type String
11699      */
11700     this.sslBlankUrl = d.sslBlankUrl;
11701     /**
11702      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11703      * @type Boolean
11704      */
11705     this.disableCaching = d.disableCaching;
11706     /**
11707      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11708      * @type String
11709      */
11710     this.indicatorText = d.indicatorText;
11711     /**
11712      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11713      * @type String
11714      */
11715     this.showLoadIndicator = d.showLoadIndicator;
11716     /**
11717      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11718      * @type Number
11719      */
11720     this.timeout = d.timeout;
11721
11722     /**
11723      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11724      * @type Boolean
11725      */
11726     this.loadScripts = d.loadScripts;
11727
11728     /**
11729      * Transaction object of current executing transaction
11730      */
11731     this.transaction = null;
11732
11733     /**
11734      * @private
11735      */
11736     this.autoRefreshProcId = null;
11737     /**
11738      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11739      * @type Function
11740      */
11741     this.refreshDelegate = this.refresh.createDelegate(this);
11742     /**
11743      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11744      * @type Function
11745      */
11746     this.updateDelegate = this.update.createDelegate(this);
11747     /**
11748      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11749      * @type Function
11750      */
11751     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11752     /**
11753      * @private
11754      */
11755     this.successDelegate = this.processSuccess.createDelegate(this);
11756     /**
11757      * @private
11758      */
11759     this.failureDelegate = this.processFailure.createDelegate(this);
11760
11761     if(!this.renderer){
11762      /**
11763       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11764       */
11765     this.renderer = new Roo.UpdateManager.BasicRenderer();
11766     }
11767     
11768     Roo.UpdateManager.superclass.constructor.call(this);
11769 };
11770
11771 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11772     /**
11773      * Get the Element this UpdateManager is bound to
11774      * @return {Roo.Element} The element
11775      */
11776     getEl : function(){
11777         return this.el;
11778     },
11779     /**
11780      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11781      * @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:
11782 <pre><code>
11783 um.update({<br/>
11784     url: "your-url.php",<br/>
11785     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11786     callback: yourFunction,<br/>
11787     scope: yourObject, //(optional scope)  <br/>
11788     discardUrl: false, <br/>
11789     nocache: false,<br/>
11790     text: "Loading...",<br/>
11791     timeout: 30,<br/>
11792     scripts: false<br/>
11793 });
11794 </code></pre>
11795      * The only required property is url. The optional properties nocache, text and scripts
11796      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11797      * @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}
11798      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11799      * @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.
11800      */
11801     update : function(url, params, callback, discardUrl){
11802         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11803             var method = this.method, cfg;
11804             if(typeof url == "object"){ // must be config object
11805                 cfg = url;
11806                 url = cfg.url;
11807                 params = params || cfg.params;
11808                 callback = callback || cfg.callback;
11809                 discardUrl = discardUrl || cfg.discardUrl;
11810                 if(callback && cfg.scope){
11811                     callback = callback.createDelegate(cfg.scope);
11812                 }
11813                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11814                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11815                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11816                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11817                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11818             }
11819             this.showLoading();
11820             if(!discardUrl){
11821                 this.defaultUrl = url;
11822             }
11823             if(typeof url == "function"){
11824                 url = url.call(this);
11825             }
11826
11827             method = method || (params ? "POST" : "GET");
11828             if(method == "GET"){
11829                 url = this.prepareUrl(url);
11830             }
11831
11832             var o = Roo.apply(cfg ||{}, {
11833                 url : url,
11834                 params: params,
11835                 success: this.successDelegate,
11836                 failure: this.failureDelegate,
11837                 callback: undefined,
11838                 timeout: (this.timeout*1000),
11839                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11840             });
11841
11842             this.transaction = Roo.Ajax.request(o);
11843         }
11844     },
11845
11846     /**
11847      * 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.
11848      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11849      * @param {String/HTMLElement} form The form Id or form element
11850      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11851      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11852      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11853      */
11854     formUpdate : function(form, url, reset, callback){
11855         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11856             if(typeof url == "function"){
11857                 url = url.call(this);
11858             }
11859             form = Roo.getDom(form);
11860             this.transaction = Roo.Ajax.request({
11861                 form: form,
11862                 url:url,
11863                 success: this.successDelegate,
11864                 failure: this.failureDelegate,
11865                 timeout: (this.timeout*1000),
11866                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11867             });
11868             this.showLoading.defer(1, this);
11869         }
11870     },
11871
11872     /**
11873      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11875      */
11876     refresh : function(callback){
11877         if(this.defaultUrl == null){
11878             return;
11879         }
11880         this.update(this.defaultUrl, null, callback, true);
11881     },
11882
11883     /**
11884      * Set this element to auto refresh.
11885      * @param {Number} interval How often to update (in seconds).
11886      * @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)
11887      * @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}
11888      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11889      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11890      */
11891     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11892         if(refreshNow){
11893             this.update(url || this.defaultUrl, params, callback, true);
11894         }
11895         if(this.autoRefreshProcId){
11896             clearInterval(this.autoRefreshProcId);
11897         }
11898         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11899     },
11900
11901     /**
11902      * Stop auto refresh on this element.
11903      */
11904      stopAutoRefresh : function(){
11905         if(this.autoRefreshProcId){
11906             clearInterval(this.autoRefreshProcId);
11907             delete this.autoRefreshProcId;
11908         }
11909     },
11910
11911     isAutoRefreshing : function(){
11912        return this.autoRefreshProcId ? true : false;
11913     },
11914     /**
11915      * Called to update the element to "Loading" state. Override to perform custom action.
11916      */
11917     showLoading : function(){
11918         if(this.showLoadIndicator){
11919             this.el.update(this.indicatorText);
11920         }
11921     },
11922
11923     /**
11924      * Adds unique parameter to query string if disableCaching = true
11925      * @private
11926      */
11927     prepareUrl : function(url){
11928         if(this.disableCaching){
11929             var append = "_dc=" + (new Date().getTime());
11930             if(url.indexOf("?") !== -1){
11931                 url += "&" + append;
11932             }else{
11933                 url += "?" + append;
11934             }
11935         }
11936         return url;
11937     },
11938
11939     /**
11940      * @private
11941      */
11942     processSuccess : function(response){
11943         this.transaction = null;
11944         if(response.argument.form && response.argument.reset){
11945             try{ // put in try/catch since some older FF releases had problems with this
11946                 response.argument.form.reset();
11947             }catch(e){}
11948         }
11949         if(this.loadScripts){
11950             this.renderer.render(this.el, response, this,
11951                 this.updateComplete.createDelegate(this, [response]));
11952         }else{
11953             this.renderer.render(this.el, response, this);
11954             this.updateComplete(response);
11955         }
11956     },
11957
11958     updateComplete : function(response){
11959         this.fireEvent("update", this.el, response);
11960         if(typeof response.argument.callback == "function"){
11961             response.argument.callback(this.el, true, response);
11962         }
11963     },
11964
11965     /**
11966      * @private
11967      */
11968     processFailure : function(response){
11969         this.transaction = null;
11970         this.fireEvent("failure", this.el, response);
11971         if(typeof response.argument.callback == "function"){
11972             response.argument.callback(this.el, false, response);
11973         }
11974     },
11975
11976     /**
11977      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11978      * @param {Object} renderer The object implementing the render() method
11979      */
11980     setRenderer : function(renderer){
11981         this.renderer = renderer;
11982     },
11983
11984     getRenderer : function(){
11985        return this.renderer;
11986     },
11987
11988     /**
11989      * Set the defaultUrl used for updates
11990      * @param {String/Function} defaultUrl The url or a function to call to get the url
11991      */
11992     setDefaultUrl : function(defaultUrl){
11993         this.defaultUrl = defaultUrl;
11994     },
11995
11996     /**
11997      * Aborts the executing transaction
11998      */
11999     abort : function(){
12000         if(this.transaction){
12001             Roo.Ajax.abort(this.transaction);
12002         }
12003     },
12004
12005     /**
12006      * Returns true if an update is in progress
12007      * @return {Boolean}
12008      */
12009     isUpdating : function(){
12010         if(this.transaction){
12011             return Roo.Ajax.isLoading(this.transaction);
12012         }
12013         return false;
12014     }
12015 });
12016
12017 /**
12018  * @class Roo.UpdateManager.defaults
12019  * @static (not really - but it helps the doc tool)
12020  * The defaults collection enables customizing the default properties of UpdateManager
12021  */
12022    Roo.UpdateManager.defaults = {
12023        /**
12024          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12025          * @type Number
12026          */
12027          timeout : 30,
12028
12029          /**
12030          * True to process scripts by default (Defaults to false).
12031          * @type Boolean
12032          */
12033         loadScripts : false,
12034
12035         /**
12036         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12037         * @type String
12038         */
12039         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12040         /**
12041          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12042          * @type Boolean
12043          */
12044         disableCaching : false,
12045         /**
12046          * Whether to show indicatorText when loading (Defaults to true).
12047          * @type Boolean
12048          */
12049         showLoadIndicator : true,
12050         /**
12051          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12052          * @type String
12053          */
12054         indicatorText : '<div class="loading-indicator">Loading...</div>'
12055    };
12056
12057 /**
12058  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12059  *Usage:
12060  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12061  * @param {String/HTMLElement/Roo.Element} el The element to update
12062  * @param {String} url The url
12063  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12064  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12065  * @static
12066  * @deprecated
12067  * @member Roo.UpdateManager
12068  */
12069 Roo.UpdateManager.updateElement = function(el, url, params, options){
12070     var um = Roo.get(el, true).getUpdateManager();
12071     Roo.apply(um, options);
12072     um.update(url, params, options ? options.callback : null);
12073 };
12074 // alias for backwards compat
12075 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12076 /**
12077  * @class Roo.UpdateManager.BasicRenderer
12078  * Default Content renderer. Updates the elements innerHTML with the responseText.
12079  */
12080 Roo.UpdateManager.BasicRenderer = function(){};
12081
12082 Roo.UpdateManager.BasicRenderer.prototype = {
12083     /**
12084      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12085      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12086      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12087      * @param {Roo.Element} el The element being rendered
12088      * @param {Object} response The YUI Connect response object
12089      * @param {UpdateManager} updateManager The calling update manager
12090      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12091      */
12092      render : function(el, response, updateManager, callback){
12093         el.update(response.responseText, updateManager.loadScripts, callback);
12094     }
12095 };
12096 /*
12097  * Based on:
12098  * Ext JS Library 1.1.1
12099  * Copyright(c) 2006-2007, Ext JS, LLC.
12100  *
12101  * Originally Released Under LGPL - original licence link has changed is not relivant.
12102  *
12103  * Fork - LGPL
12104  * <script type="text/javascript">
12105  */
12106
12107 /**
12108  * @class Roo.util.DelayedTask
12109  * Provides a convenient method of performing setTimeout where a new
12110  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12111  * You can use this class to buffer
12112  * the keypress events for a certain number of milliseconds, and perform only if they stop
12113  * for that amount of time.
12114  * @constructor The parameters to this constructor serve as defaults and are not required.
12115  * @param {Function} fn (optional) The default function to timeout
12116  * @param {Object} scope (optional) The default scope of that timeout
12117  * @param {Array} args (optional) The default Array of arguments
12118  */
12119 Roo.util.DelayedTask = function(fn, scope, args){
12120     var id = null, d, t;
12121
12122     var call = function(){
12123         var now = new Date().getTime();
12124         if(now - t >= d){
12125             clearInterval(id);
12126             id = null;
12127             fn.apply(scope, args || []);
12128         }
12129     };
12130     /**
12131      * Cancels any pending timeout and queues a new one
12132      * @param {Number} delay The milliseconds to delay
12133      * @param {Function} newFn (optional) Overrides function passed to constructor
12134      * @param {Object} newScope (optional) Overrides scope passed to constructor
12135      * @param {Array} newArgs (optional) Overrides args passed to constructor
12136      */
12137     this.delay = function(delay, newFn, newScope, newArgs){
12138         if(id && delay != d){
12139             this.cancel();
12140         }
12141         d = delay;
12142         t = new Date().getTime();
12143         fn = newFn || fn;
12144         scope = newScope || scope;
12145         args = newArgs || args;
12146         if(!id){
12147             id = setInterval(call, d);
12148         }
12149     };
12150
12151     /**
12152      * Cancel the last queued timeout
12153      */
12154     this.cancel = function(){
12155         if(id){
12156             clearInterval(id);
12157             id = null;
12158         }
12159     };
12160 };/*
12161  * Based on:
12162  * Ext JS Library 1.1.1
12163  * Copyright(c) 2006-2007, Ext JS, LLC.
12164  *
12165  * Originally Released Under LGPL - original licence link has changed is not relivant.
12166  *
12167  * Fork - LGPL
12168  * <script type="text/javascript">
12169  */
12170  
12171  
12172 Roo.util.TaskRunner = function(interval){
12173     interval = interval || 10;
12174     var tasks = [], removeQueue = [];
12175     var id = 0;
12176     var running = false;
12177
12178     var stopThread = function(){
12179         running = false;
12180         clearInterval(id);
12181         id = 0;
12182     };
12183
12184     var startThread = function(){
12185         if(!running){
12186             running = true;
12187             id = setInterval(runTasks, interval);
12188         }
12189     };
12190
12191     var removeTask = function(task){
12192         removeQueue.push(task);
12193         if(task.onStop){
12194             task.onStop();
12195         }
12196     };
12197
12198     var runTasks = function(){
12199         if(removeQueue.length > 0){
12200             for(var i = 0, len = removeQueue.length; i < len; i++){
12201                 tasks.remove(removeQueue[i]);
12202             }
12203             removeQueue = [];
12204             if(tasks.length < 1){
12205                 stopThread();
12206                 return;
12207             }
12208         }
12209         var now = new Date().getTime();
12210         for(var i = 0, len = tasks.length; i < len; ++i){
12211             var t = tasks[i];
12212             var itime = now - t.taskRunTime;
12213             if(t.interval <= itime){
12214                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12215                 t.taskRunTime = now;
12216                 if(rt === false || t.taskRunCount === t.repeat){
12217                     removeTask(t);
12218                     return;
12219                 }
12220             }
12221             if(t.duration && t.duration <= (now - t.taskStartTime)){
12222                 removeTask(t);
12223             }
12224         }
12225     };
12226
12227     /**
12228      * Queues a new task.
12229      * @param {Object} task
12230      */
12231     this.start = function(task){
12232         tasks.push(task);
12233         task.taskStartTime = new Date().getTime();
12234         task.taskRunTime = 0;
12235         task.taskRunCount = 0;
12236         startThread();
12237         return task;
12238     };
12239
12240     this.stop = function(task){
12241         removeTask(task);
12242         return task;
12243     };
12244
12245     this.stopAll = function(){
12246         stopThread();
12247         for(var i = 0, len = tasks.length; i < len; i++){
12248             if(tasks[i].onStop){
12249                 tasks[i].onStop();
12250             }
12251         }
12252         tasks = [];
12253         removeQueue = [];
12254     };
12255 };
12256
12257 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12258  * Based on:
12259  * Ext JS Library 1.1.1
12260  * Copyright(c) 2006-2007, Ext JS, LLC.
12261  *
12262  * Originally Released Under LGPL - original licence link has changed is not relivant.
12263  *
12264  * Fork - LGPL
12265  * <script type="text/javascript">
12266  */
12267
12268  
12269 /**
12270  * @class Roo.util.MixedCollection
12271  * @extends Roo.util.Observable
12272  * A Collection class that maintains both numeric indexes and keys and exposes events.
12273  * @constructor
12274  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12275  * collection (defaults to false)
12276  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12277  * and return the key value for that item.  This is used when available to look up the key on items that
12278  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12279  * equivalent to providing an implementation for the {@link #getKey} method.
12280  */
12281 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12282     this.items = [];
12283     this.map = {};
12284     this.keys = [];
12285     this.length = 0;
12286     this.addEvents({
12287         /**
12288          * @event clear
12289          * Fires when the collection is cleared.
12290          */
12291         "clear" : true,
12292         /**
12293          * @event add
12294          * Fires when an item is added to the collection.
12295          * @param {Number} index The index at which the item was added.
12296          * @param {Object} o The item added.
12297          * @param {String} key The key associated with the added item.
12298          */
12299         "add" : true,
12300         /**
12301          * @event replace
12302          * Fires when an item is replaced in the collection.
12303          * @param {String} key he key associated with the new added.
12304          * @param {Object} old The item being replaced.
12305          * @param {Object} new The new item.
12306          */
12307         "replace" : true,
12308         /**
12309          * @event remove
12310          * Fires when an item is removed from the collection.
12311          * @param {Object} o The item being removed.
12312          * @param {String} key (optional) The key associated with the removed item.
12313          */
12314         "remove" : true,
12315         "sort" : true
12316     });
12317     this.allowFunctions = allowFunctions === true;
12318     if(keyFn){
12319         this.getKey = keyFn;
12320     }
12321     Roo.util.MixedCollection.superclass.constructor.call(this);
12322 };
12323
12324 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12325     allowFunctions : false,
12326     
12327 /**
12328  * Adds an item to the collection.
12329  * @param {String} key The key to associate with the item
12330  * @param {Object} o The item to add.
12331  * @return {Object} The item added.
12332  */
12333     add : function(key, o){
12334         if(arguments.length == 1){
12335             o = arguments[0];
12336             key = this.getKey(o);
12337         }
12338         if(typeof key == "undefined" || key === null){
12339             this.length++;
12340             this.items.push(o);
12341             this.keys.push(null);
12342         }else{
12343             var old = this.map[key];
12344             if(old){
12345                 return this.replace(key, o);
12346             }
12347             this.length++;
12348             this.items.push(o);
12349             this.map[key] = o;
12350             this.keys.push(key);
12351         }
12352         this.fireEvent("add", this.length-1, o, key);
12353         return o;
12354     },
12355        
12356 /**
12357   * MixedCollection has a generic way to fetch keys if you implement getKey.
12358 <pre><code>
12359 // normal way
12360 var mc = new Roo.util.MixedCollection();
12361 mc.add(someEl.dom.id, someEl);
12362 mc.add(otherEl.dom.id, otherEl);
12363 //and so on
12364
12365 // using getKey
12366 var mc = new Roo.util.MixedCollection();
12367 mc.getKey = function(el){
12368    return el.dom.id;
12369 };
12370 mc.add(someEl);
12371 mc.add(otherEl);
12372
12373 // or via the constructor
12374 var mc = new Roo.util.MixedCollection(false, function(el){
12375    return el.dom.id;
12376 });
12377 mc.add(someEl);
12378 mc.add(otherEl);
12379 </code></pre>
12380  * @param o {Object} The item for which to find the key.
12381  * @return {Object} The key for the passed item.
12382  */
12383     getKey : function(o){
12384          return o.id; 
12385     },
12386    
12387 /**
12388  * Replaces an item in the collection.
12389  * @param {String} key The key associated with the item to replace, or the item to replace.
12390  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12391  * @return {Object}  The new item.
12392  */
12393     replace : function(key, o){
12394         if(arguments.length == 1){
12395             o = arguments[0];
12396             key = this.getKey(o);
12397         }
12398         var old = this.item(key);
12399         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12400              return this.add(key, o);
12401         }
12402         var index = this.indexOfKey(key);
12403         this.items[index] = o;
12404         this.map[key] = o;
12405         this.fireEvent("replace", key, old, o);
12406         return o;
12407     },
12408    
12409 /**
12410  * Adds all elements of an Array or an Object to the collection.
12411  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12412  * an Array of values, each of which are added to the collection.
12413  */
12414     addAll : function(objs){
12415         if(arguments.length > 1 || objs instanceof Array){
12416             var args = arguments.length > 1 ? arguments : objs;
12417             for(var i = 0, len = args.length; i < len; i++){
12418                 this.add(args[i]);
12419             }
12420         }else{
12421             for(var key in objs){
12422                 if(this.allowFunctions || typeof objs[key] != "function"){
12423                     this.add(key, objs[key]);
12424                 }
12425             }
12426         }
12427     },
12428    
12429 /**
12430  * Executes the specified function once for every item in the collection, passing each
12431  * item as the first and only parameter. returning false from the function will stop the iteration.
12432  * @param {Function} fn The function to execute for each item.
12433  * @param {Object} scope (optional) The scope in which to execute the function.
12434  */
12435     each : function(fn, scope){
12436         var items = [].concat(this.items); // each safe for removal
12437         for(var i = 0, len = items.length; i < len; i++){
12438             if(fn.call(scope || items[i], items[i], i, len) === false){
12439                 break;
12440             }
12441         }
12442     },
12443    
12444 /**
12445  * Executes the specified function once for every key in the collection, passing each
12446  * key, and its associated item as the first two parameters.
12447  * @param {Function} fn The function to execute for each item.
12448  * @param {Object} scope (optional) The scope in which to execute the function.
12449  */
12450     eachKey : function(fn, scope){
12451         for(var i = 0, len = this.keys.length; i < len; i++){
12452             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12453         }
12454     },
12455    
12456 /**
12457  * Returns the first item in the collection which elicits a true return value from the
12458  * passed selection function.
12459  * @param {Function} fn The selection function to execute for each item.
12460  * @param {Object} scope (optional) The scope in which to execute the function.
12461  * @return {Object} The first item in the collection which returned true from the selection function.
12462  */
12463     find : function(fn, scope){
12464         for(var i = 0, len = this.items.length; i < len; i++){
12465             if(fn.call(scope || window, this.items[i], this.keys[i])){
12466                 return this.items[i];
12467             }
12468         }
12469         return null;
12470     },
12471    
12472 /**
12473  * Inserts an item at the specified index in the collection.
12474  * @param {Number} index The index to insert the item at.
12475  * @param {String} key The key to associate with the new item, or the item itself.
12476  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12477  * @return {Object} The item inserted.
12478  */
12479     insert : function(index, key, o){
12480         if(arguments.length == 2){
12481             o = arguments[1];
12482             key = this.getKey(o);
12483         }
12484         if(index >= this.length){
12485             return this.add(key, o);
12486         }
12487         this.length++;
12488         this.items.splice(index, 0, o);
12489         if(typeof key != "undefined" && key != null){
12490             this.map[key] = o;
12491         }
12492         this.keys.splice(index, 0, key);
12493         this.fireEvent("add", index, o, key);
12494         return o;
12495     },
12496    
12497 /**
12498  * Removed an item from the collection.
12499  * @param {Object} o The item to remove.
12500  * @return {Object} The item removed.
12501  */
12502     remove : function(o){
12503         return this.removeAt(this.indexOf(o));
12504     },
12505    
12506 /**
12507  * Remove an item from a specified index in the collection.
12508  * @param {Number} index The index within the collection of the item to remove.
12509  */
12510     removeAt : function(index){
12511         if(index < this.length && index >= 0){
12512             this.length--;
12513             var o = this.items[index];
12514             this.items.splice(index, 1);
12515             var key = this.keys[index];
12516             if(typeof key != "undefined"){
12517                 delete this.map[key];
12518             }
12519             this.keys.splice(index, 1);
12520             this.fireEvent("remove", o, key);
12521         }
12522     },
12523    
12524 /**
12525  * Removed an item associated with the passed key fom the collection.
12526  * @param {String} key The key of the item to remove.
12527  */
12528     removeKey : function(key){
12529         return this.removeAt(this.indexOfKey(key));
12530     },
12531    
12532 /**
12533  * Returns the number of items in the collection.
12534  * @return {Number} the number of items in the collection.
12535  */
12536     getCount : function(){
12537         return this.length; 
12538     },
12539    
12540 /**
12541  * Returns index within the collection of the passed Object.
12542  * @param {Object} o The item to find the index of.
12543  * @return {Number} index of the item.
12544  */
12545     indexOf : function(o){
12546         if(!this.items.indexOf){
12547             for(var i = 0, len = this.items.length; i < len; i++){
12548                 if(this.items[i] == o) return i;
12549             }
12550             return -1;
12551         }else{
12552             return this.items.indexOf(o);
12553         }
12554     },
12555    
12556 /**
12557  * Returns index within the collection of the passed key.
12558  * @param {String} key The key to find the index of.
12559  * @return {Number} index of the key.
12560  */
12561     indexOfKey : function(key){
12562         if(!this.keys.indexOf){
12563             for(var i = 0, len = this.keys.length; i < len; i++){
12564                 if(this.keys[i] == key) return i;
12565             }
12566             return -1;
12567         }else{
12568             return this.keys.indexOf(key);
12569         }
12570     },
12571    
12572 /**
12573  * Returns the item associated with the passed key OR index. Key has priority over index.
12574  * @param {String/Number} key The key or index of the item.
12575  * @return {Object} The item associated with the passed key.
12576  */
12577     item : function(key){
12578         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12579         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12580     },
12581     
12582 /**
12583  * Returns the item at the specified index.
12584  * @param {Number} index The index of the item.
12585  * @return {Object}
12586  */
12587     itemAt : function(index){
12588         return this.items[index];
12589     },
12590     
12591 /**
12592  * Returns the item associated with the passed key.
12593  * @param {String/Number} key The key of the item.
12594  * @return {Object} The item associated with the passed key.
12595  */
12596     key : function(key){
12597         return this.map[key];
12598     },
12599    
12600 /**
12601  * Returns true if the collection contains the passed Object as an item.
12602  * @param {Object} o  The Object to look for in the collection.
12603  * @return {Boolean} True if the collection contains the Object as an item.
12604  */
12605     contains : function(o){
12606         return this.indexOf(o) != -1;
12607     },
12608    
12609 /**
12610  * Returns true if the collection contains the passed Object as a key.
12611  * @param {String} key The key to look for in the collection.
12612  * @return {Boolean} True if the collection contains the Object as a key.
12613  */
12614     containsKey : function(key){
12615         return typeof this.map[key] != "undefined";
12616     },
12617    
12618 /**
12619  * Removes all items from the collection.
12620  */
12621     clear : function(){
12622         this.length = 0;
12623         this.items = [];
12624         this.keys = [];
12625         this.map = {};
12626         this.fireEvent("clear");
12627     },
12628    
12629 /**
12630  * Returns the first item in the collection.
12631  * @return {Object} the first item in the collection..
12632  */
12633     first : function(){
12634         return this.items[0]; 
12635     },
12636    
12637 /**
12638  * Returns the last item in the collection.
12639  * @return {Object} the last item in the collection..
12640  */
12641     last : function(){
12642         return this.items[this.length-1];   
12643     },
12644     
12645     _sort : function(property, dir, fn){
12646         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12647         fn = fn || function(a, b){
12648             return a-b;
12649         };
12650         var c = [], k = this.keys, items = this.items;
12651         for(var i = 0, len = items.length; i < len; i++){
12652             c[c.length] = {key: k[i], value: items[i], index: i};
12653         }
12654         c.sort(function(a, b){
12655             var v = fn(a[property], b[property]) * dsc;
12656             if(v == 0){
12657                 v = (a.index < b.index ? -1 : 1);
12658             }
12659             return v;
12660         });
12661         for(var i = 0, len = c.length; i < len; i++){
12662             items[i] = c[i].value;
12663             k[i] = c[i].key;
12664         }
12665         this.fireEvent("sort", this);
12666     },
12667     
12668     /**
12669      * Sorts this collection with the passed comparison function
12670      * @param {String} direction (optional) "ASC" or "DESC"
12671      * @param {Function} fn (optional) comparison function
12672      */
12673     sort : function(dir, fn){
12674         this._sort("value", dir, fn);
12675     },
12676     
12677     /**
12678      * Sorts this collection by keys
12679      * @param {String} direction (optional) "ASC" or "DESC"
12680      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12681      */
12682     keySort : function(dir, fn){
12683         this._sort("key", dir, fn || function(a, b){
12684             return String(a).toUpperCase()-String(b).toUpperCase();
12685         });
12686     },
12687     
12688     /**
12689      * Returns a range of items in this collection
12690      * @param {Number} startIndex (optional) defaults to 0
12691      * @param {Number} endIndex (optional) default to the last item
12692      * @return {Array} An array of items
12693      */
12694     getRange : function(start, end){
12695         var items = this.items;
12696         if(items.length < 1){
12697             return [];
12698         }
12699         start = start || 0;
12700         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12701         var r = [];
12702         if(start <= end){
12703             for(var i = start; i <= end; i++) {
12704                     r[r.length] = items[i];
12705             }
12706         }else{
12707             for(var i = start; i >= end; i--) {
12708                     r[r.length] = items[i];
12709             }
12710         }
12711         return r;
12712     },
12713         
12714     /**
12715      * Filter the <i>objects</i> in this collection by a specific property. 
12716      * Returns a new collection that has been filtered.
12717      * @param {String} property A property on your objects
12718      * @param {String/RegExp} value Either string that the property values 
12719      * should start with or a RegExp to test against the property
12720      * @return {MixedCollection} The new filtered collection
12721      */
12722     filter : function(property, value){
12723         if(!value.exec){ // not a regex
12724             value = String(value);
12725             if(value.length == 0){
12726                 return this.clone();
12727             }
12728             value = new RegExp("^" + Roo.escapeRe(value), "i");
12729         }
12730         return this.filterBy(function(o){
12731             return o && value.test(o[property]);
12732         });
12733         },
12734     
12735     /**
12736      * Filter by a function. * Returns a new collection that has been filtered.
12737      * The passed function will be called with each 
12738      * object in the collection. If the function returns true, the value is included 
12739      * otherwise it is filtered.
12740      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12741      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12742      * @return {MixedCollection} The new filtered collection
12743      */
12744     filterBy : function(fn, scope){
12745         var r = new Roo.util.MixedCollection();
12746         r.getKey = this.getKey;
12747         var k = this.keys, it = this.items;
12748         for(var i = 0, len = it.length; i < len; i++){
12749             if(fn.call(scope||this, it[i], k[i])){
12750                                 r.add(k[i], it[i]);
12751                         }
12752         }
12753         return r;
12754     },
12755     
12756     /**
12757      * Creates a duplicate of this collection
12758      * @return {MixedCollection}
12759      */
12760     clone : function(){
12761         var r = new Roo.util.MixedCollection();
12762         var k = this.keys, it = this.items;
12763         for(var i = 0, len = it.length; i < len; i++){
12764             r.add(k[i], it[i]);
12765         }
12766         r.getKey = this.getKey;
12767         return r;
12768     }
12769 });
12770 /**
12771  * Returns the item associated with the passed key or index.
12772  * @method
12773  * @param {String/Number} key The key or index of the item.
12774  * @return {Object} The item associated with the passed key.
12775  */
12776 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12777  * Based on:
12778  * Ext JS Library 1.1.1
12779  * Copyright(c) 2006-2007, Ext JS, LLC.
12780  *
12781  * Originally Released Under LGPL - original licence link has changed is not relivant.
12782  *
12783  * Fork - LGPL
12784  * <script type="text/javascript">
12785  */
12786 /**
12787  * @class Roo.util.JSON
12788  * Modified version of Douglas Crockford"s json.js that doesn"t
12789  * mess with the Object prototype 
12790  * http://www.json.org/js.html
12791  * @singleton
12792  */
12793 Roo.util.JSON = new (function(){
12794     var useHasOwn = {}.hasOwnProperty ? true : false;
12795     
12796     // crashes Safari in some instances
12797     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12798     
12799     var pad = function(n) {
12800         return n < 10 ? "0" + n : n;
12801     };
12802     
12803     var m = {
12804         "\b": '\\b',
12805         "\t": '\\t',
12806         "\n": '\\n',
12807         "\f": '\\f',
12808         "\r": '\\r',
12809         '"' : '\\"',
12810         "\\": '\\\\'
12811     };
12812
12813     var encodeString = function(s){
12814         if (/["\\\x00-\x1f]/.test(s)) {
12815             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12816                 var c = m[b];
12817                 if(c){
12818                     return c;
12819                 }
12820                 c = b.charCodeAt();
12821                 return "\\u00" +
12822                     Math.floor(c / 16).toString(16) +
12823                     (c % 16).toString(16);
12824             }) + '"';
12825         }
12826         return '"' + s + '"';
12827     };
12828     
12829     var encodeArray = function(o){
12830         var a = ["["], b, i, l = o.length, v;
12831             for (i = 0; i < l; i += 1) {
12832                 v = o[i];
12833                 switch (typeof v) {
12834                     case "undefined":
12835                     case "function":
12836                     case "unknown":
12837                         break;
12838                     default:
12839                         if (b) {
12840                             a.push(',');
12841                         }
12842                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12843                         b = true;
12844                 }
12845             }
12846             a.push("]");
12847             return a.join("");
12848     };
12849     
12850     var encodeDate = function(o){
12851         return '"' + o.getFullYear() + "-" +
12852                 pad(o.getMonth() + 1) + "-" +
12853                 pad(o.getDate()) + "T" +
12854                 pad(o.getHours()) + ":" +
12855                 pad(o.getMinutes()) + ":" +
12856                 pad(o.getSeconds()) + '"';
12857     };
12858     
12859     /**
12860      * Encodes an Object, Array or other value
12861      * @param {Mixed} o The variable to encode
12862      * @return {String} The JSON string
12863      */
12864     this.encode = function(o)
12865     {
12866         // should this be extended to fully wrap stringify..
12867         
12868         if(typeof o == "undefined" || o === null){
12869             return "null";
12870         }else if(o instanceof Array){
12871             return encodeArray(o);
12872         }else if(o instanceof Date){
12873             return encodeDate(o);
12874         }else if(typeof o == "string"){
12875             return encodeString(o);
12876         }else if(typeof o == "number"){
12877             return isFinite(o) ? String(o) : "null";
12878         }else if(typeof o == "boolean"){
12879             return String(o);
12880         }else {
12881             var a = ["{"], b, i, v;
12882             for (i in o) {
12883                 if(!useHasOwn || o.hasOwnProperty(i)) {
12884                     v = o[i];
12885                     switch (typeof v) {
12886                     case "undefined":
12887                     case "function":
12888                     case "unknown":
12889                         break;
12890                     default:
12891                         if(b){
12892                             a.push(',');
12893                         }
12894                         a.push(this.encode(i), ":",
12895                                 v === null ? "null" : this.encode(v));
12896                         b = true;
12897                     }
12898                 }
12899             }
12900             a.push("}");
12901             return a.join("");
12902         }
12903     };
12904     
12905     /**
12906      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12907      * @param {String} json The JSON string
12908      * @return {Object} The resulting object
12909      */
12910     this.decode = function(json){
12911         
12912         return  /** eval:var:json */ eval("(" + json + ')');
12913     };
12914 })();
12915 /** 
12916  * Shorthand for {@link Roo.util.JSON#encode}
12917  * @member Roo encode 
12918  * @method */
12919 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12920 /** 
12921  * Shorthand for {@link Roo.util.JSON#decode}
12922  * @member Roo decode 
12923  * @method */
12924 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12925 /*
12926  * Based on:
12927  * Ext JS Library 1.1.1
12928  * Copyright(c) 2006-2007, Ext JS, LLC.
12929  *
12930  * Originally Released Under LGPL - original licence link has changed is not relivant.
12931  *
12932  * Fork - LGPL
12933  * <script type="text/javascript">
12934  */
12935  
12936 /**
12937  * @class Roo.util.Format
12938  * Reusable data formatting functions
12939  * @singleton
12940  */
12941 Roo.util.Format = function(){
12942     var trimRe = /^\s+|\s+$/g;
12943     return {
12944         /**
12945          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12946          * @param {String} value The string to truncate
12947          * @param {Number} length The maximum length to allow before truncating
12948          * @return {String} The converted text
12949          */
12950         ellipsis : function(value, len){
12951             if(value && value.length > len){
12952                 return value.substr(0, len-3)+"...";
12953             }
12954             return value;
12955         },
12956
12957         /**
12958          * Checks a reference and converts it to empty string if it is undefined
12959          * @param {Mixed} value Reference to check
12960          * @return {Mixed} Empty string if converted, otherwise the original value
12961          */
12962         undef : function(value){
12963             return typeof value != "undefined" ? value : "";
12964         },
12965
12966         /**
12967          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12968          * @param {String} value The string to encode
12969          * @return {String} The encoded text
12970          */
12971         htmlEncode : function(value){
12972             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12973         },
12974
12975         /**
12976          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12977          * @param {String} value The string to decode
12978          * @return {String} The decoded text
12979          */
12980         htmlDecode : function(value){
12981             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12982         },
12983
12984         /**
12985          * Trims any whitespace from either side of a string
12986          * @param {String} value The text to trim
12987          * @return {String} The trimmed text
12988          */
12989         trim : function(value){
12990             return String(value).replace(trimRe, "");
12991         },
12992
12993         /**
12994          * Returns a substring from within an original string
12995          * @param {String} value The original text
12996          * @param {Number} start The start index of the substring
12997          * @param {Number} length The length of the substring
12998          * @return {String} The substring
12999          */
13000         substr : function(value, start, length){
13001             return String(value).substr(start, length);
13002         },
13003
13004         /**
13005          * Converts a string to all lower case letters
13006          * @param {String} value The text to convert
13007          * @return {String} The converted text
13008          */
13009         lowercase : function(value){
13010             return String(value).toLowerCase();
13011         },
13012
13013         /**
13014          * Converts a string to all upper case letters
13015          * @param {String} value The text to convert
13016          * @return {String} The converted text
13017          */
13018         uppercase : function(value){
13019             return String(value).toUpperCase();
13020         },
13021
13022         /**
13023          * Converts the first character only of a string to upper case
13024          * @param {String} value The text to convert
13025          * @return {String} The converted text
13026          */
13027         capitalize : function(value){
13028             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13029         },
13030
13031         // private
13032         call : function(value, fn){
13033             if(arguments.length > 2){
13034                 var args = Array.prototype.slice.call(arguments, 2);
13035                 args.unshift(value);
13036                  
13037                 return /** eval:var:value */  eval(fn).apply(window, args);
13038             }else{
13039                 /** eval:var:value */
13040                 return /** eval:var:value */ eval(fn).call(window, value);
13041             }
13042         },
13043
13044        
13045         /**
13046          * safer version of Math.toFixed..??/
13047          * @param {Number/String} value The numeric value to format
13048          * @param {Number/String} value Decimal places 
13049          * @return {String} The formatted currency string
13050          */
13051         toFixed : function(v, n)
13052         {
13053             // why not use to fixed - precision is buggered???
13054             if (!n) {
13055                 return Math.round(v-0);
13056             }
13057             var fact = Math.pow(10,n+1);
13058             v = (Math.round((v-0)*fact))/fact;
13059             var z = (''+fact).substring(2);
13060             if (v == Math.floor(v)) {
13061                 return Math.floor(v) + '.' + z;
13062             }
13063             
13064             // now just padd decimals..
13065             var ps = String(v).split('.');
13066             var fd = (ps[1] + z);
13067             var r = fd.substring(0,n); 
13068             var rm = fd.substring(n); 
13069             if (rm < 5) {
13070                 return ps[0] + '.' + r;
13071             }
13072             r*=1; // turn it into a number;
13073             r++;
13074             if (String(r).length != n) {
13075                 ps[0]*=1;
13076                 ps[0]++;
13077                 r = String(r).substring(1); // chop the end off.
13078             }
13079             
13080             return ps[0] + '.' + r;
13081              
13082         },
13083         
13084         /**
13085          * Format a number as US currency
13086          * @param {Number/String} value The numeric value to format
13087          * @return {String} The formatted currency string
13088          */
13089         usMoney : function(v){
13090             v = (Math.round((v-0)*100))/100;
13091             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13092             v = String(v);
13093             var ps = v.split('.');
13094             var whole = ps[0];
13095             var sub = ps[1] ? '.'+ ps[1] : '.00';
13096             var r = /(\d+)(\d{3})/;
13097             while (r.test(whole)) {
13098                 whole = whole.replace(r, '$1' + ',' + '$2');
13099             }
13100             return "$" + whole + sub ;
13101         },
13102         
13103         /**
13104          * Parse a value into a formatted date using the specified format pattern.
13105          * @param {Mixed} value The value to format
13106          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13107          * @return {String} The formatted date string
13108          */
13109         date : function(v, format){
13110             if(!v){
13111                 return "";
13112             }
13113             if(!(v instanceof Date)){
13114                 v = new Date(Date.parse(v));
13115             }
13116             return v.dateFormat(format || "m/d/Y");
13117         },
13118
13119         /**
13120          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13121          * @param {String} format Any valid date format string
13122          * @return {Function} The date formatting function
13123          */
13124         dateRenderer : function(format){
13125             return function(v){
13126                 return Roo.util.Format.date(v, format);  
13127             };
13128         },
13129
13130         // private
13131         stripTagsRE : /<\/?[^>]+>/gi,
13132         
13133         /**
13134          * Strips all HTML tags
13135          * @param {Mixed} value The text from which to strip tags
13136          * @return {String} The stripped text
13137          */
13138         stripTags : function(v){
13139             return !v ? v : String(v).replace(this.stripTagsRE, "");
13140         }
13141     };
13142 }();/*
13143  * Based on:
13144  * Ext JS Library 1.1.1
13145  * Copyright(c) 2006-2007, Ext JS, LLC.
13146  *
13147  * Originally Released Under LGPL - original licence link has changed is not relivant.
13148  *
13149  * Fork - LGPL
13150  * <script type="text/javascript">
13151  */
13152
13153
13154  
13155
13156 /**
13157  * @class Roo.MasterTemplate
13158  * @extends Roo.Template
13159  * Provides a template that can have child templates. The syntax is:
13160 <pre><code>
13161 var t = new Roo.MasterTemplate(
13162         '&lt;select name="{name}"&gt;',
13163                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13164         '&lt;/select&gt;'
13165 );
13166 t.add('options', {value: 'foo', text: 'bar'});
13167 // or you can add multiple child elements in one shot
13168 t.addAll('options', [
13169     {value: 'foo', text: 'bar'},
13170     {value: 'foo2', text: 'bar2'},
13171     {value: 'foo3', text: 'bar3'}
13172 ]);
13173 // then append, applying the master template values
13174 t.append('my-form', {name: 'my-select'});
13175 </code></pre>
13176 * A name attribute for the child template is not required if you have only one child
13177 * template or you want to refer to them by index.
13178  */
13179 Roo.MasterTemplate = function(){
13180     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13181     this.originalHtml = this.html;
13182     var st = {};
13183     var m, re = this.subTemplateRe;
13184     re.lastIndex = 0;
13185     var subIndex = 0;
13186     while(m = re.exec(this.html)){
13187         var name = m[1], content = m[2];
13188         st[subIndex] = {
13189             name: name,
13190             index: subIndex,
13191             buffer: [],
13192             tpl : new Roo.Template(content)
13193         };
13194         if(name){
13195             st[name] = st[subIndex];
13196         }
13197         st[subIndex].tpl.compile();
13198         st[subIndex].tpl.call = this.call.createDelegate(this);
13199         subIndex++;
13200     }
13201     this.subCount = subIndex;
13202     this.subs = st;
13203 };
13204 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13205     /**
13206     * The regular expression used to match sub templates
13207     * @type RegExp
13208     * @property
13209     */
13210     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13211
13212     /**
13213      * Applies the passed values to a child template.
13214      * @param {String/Number} name (optional) The name or index of the child template
13215      * @param {Array/Object} values The values to be applied to the template
13216      * @return {MasterTemplate} this
13217      */
13218      add : function(name, values){
13219         if(arguments.length == 1){
13220             values = arguments[0];
13221             name = 0;
13222         }
13223         var s = this.subs[name];
13224         s.buffer[s.buffer.length] = s.tpl.apply(values);
13225         return this;
13226     },
13227
13228     /**
13229      * Applies all the passed values to a child template.
13230      * @param {String/Number} name (optional) The name or index of the child template
13231      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13232      * @param {Boolean} reset (optional) True to reset the template first
13233      * @return {MasterTemplate} this
13234      */
13235     fill : function(name, values, reset){
13236         var a = arguments;
13237         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13238             values = a[0];
13239             name = 0;
13240             reset = a[1];
13241         }
13242         if(reset){
13243             this.reset();
13244         }
13245         for(var i = 0, len = values.length; i < len; i++){
13246             this.add(name, values[i]);
13247         }
13248         return this;
13249     },
13250
13251     /**
13252      * Resets the template for reuse
13253      * @return {MasterTemplate} this
13254      */
13255      reset : function(){
13256         var s = this.subs;
13257         for(var i = 0; i < this.subCount; i++){
13258             s[i].buffer = [];
13259         }
13260         return this;
13261     },
13262
13263     applyTemplate : function(values){
13264         var s = this.subs;
13265         var replaceIndex = -1;
13266         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13267             return s[++replaceIndex].buffer.join("");
13268         });
13269         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13270     },
13271
13272     apply : function(){
13273         return this.applyTemplate.apply(this, arguments);
13274     },
13275
13276     compile : function(){return this;}
13277 });
13278
13279 /**
13280  * Alias for fill().
13281  * @method
13282  */
13283 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13284  /**
13285  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13286  * var tpl = Roo.MasterTemplate.from('element-id');
13287  * @param {String/HTMLElement} el
13288  * @param {Object} config
13289  * @static
13290  */
13291 Roo.MasterTemplate.from = function(el, config){
13292     el = Roo.getDom(el);
13293     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13294 };/*
13295  * Based on:
13296  * Ext JS Library 1.1.1
13297  * Copyright(c) 2006-2007, Ext JS, LLC.
13298  *
13299  * Originally Released Under LGPL - original licence link has changed is not relivant.
13300  *
13301  * Fork - LGPL
13302  * <script type="text/javascript">
13303  */
13304
13305  
13306 /**
13307  * @class Roo.util.CSS
13308  * Utility class for manipulating CSS rules
13309  * @singleton
13310  */
13311 Roo.util.CSS = function(){
13312         var rules = null;
13313         var doc = document;
13314
13315     var camelRe = /(-[a-z])/gi;
13316     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13317
13318    return {
13319    /**
13320     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13321     * tag and appended to the HEAD of the document.
13322     * @param {String|Object} cssText The text containing the css rules
13323     * @param {String} id An id to add to the stylesheet for later removal
13324     * @return {StyleSheet}
13325     */
13326     createStyleSheet : function(cssText, id){
13327         var ss;
13328         var head = doc.getElementsByTagName("head")[0];
13329         var nrules = doc.createElement("style");
13330         nrules.setAttribute("type", "text/css");
13331         if(id){
13332             nrules.setAttribute("id", id);
13333         }
13334         if (typeof(cssText) != 'string') {
13335             // support object maps..
13336             // not sure if this a good idea.. 
13337             // perhaps it should be merged with the general css handling
13338             // and handle js style props.
13339             var cssTextNew = [];
13340             for(var n in cssText) {
13341                 var citems = [];
13342                 for(var k in cssText[n]) {
13343                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13344                 }
13345                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13346                 
13347             }
13348             cssText = cssTextNew.join("\n");
13349             
13350         }
13351        
13352        
13353        if(Roo.isIE){
13354            head.appendChild(nrules);
13355            ss = nrules.styleSheet;
13356            ss.cssText = cssText;
13357        }else{
13358            try{
13359                 nrules.appendChild(doc.createTextNode(cssText));
13360            }catch(e){
13361                nrules.cssText = cssText; 
13362            }
13363            head.appendChild(nrules);
13364            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13365        }
13366        this.cacheStyleSheet(ss);
13367        return ss;
13368    },
13369
13370    /**
13371     * Removes a style or link tag by id
13372     * @param {String} id The id of the tag
13373     */
13374    removeStyleSheet : function(id){
13375        var existing = doc.getElementById(id);
13376        if(existing){
13377            existing.parentNode.removeChild(existing);
13378        }
13379    },
13380
13381    /**
13382     * Dynamically swaps an existing stylesheet reference for a new one
13383     * @param {String} id The id of an existing link tag to remove
13384     * @param {String} url The href of the new stylesheet to include
13385     */
13386    swapStyleSheet : function(id, url){
13387        this.removeStyleSheet(id);
13388        var ss = doc.createElement("link");
13389        ss.setAttribute("rel", "stylesheet");
13390        ss.setAttribute("type", "text/css");
13391        ss.setAttribute("id", id);
13392        ss.setAttribute("href", url);
13393        doc.getElementsByTagName("head")[0].appendChild(ss);
13394    },
13395    
13396    /**
13397     * Refresh the rule cache if you have dynamically added stylesheets
13398     * @return {Object} An object (hash) of rules indexed by selector
13399     */
13400    refreshCache : function(){
13401        return this.getRules(true);
13402    },
13403
13404    // private
13405    cacheStyleSheet : function(stylesheet){
13406        if(!rules){
13407            rules = {};
13408        }
13409        try{// try catch for cross domain access issue
13410            var ssRules = stylesheet.cssRules || stylesheet.rules;
13411            for(var j = ssRules.length-1; j >= 0; --j){
13412                rules[ssRules[j].selectorText] = ssRules[j];
13413            }
13414        }catch(e){}
13415    },
13416    
13417    /**
13418     * Gets all css rules for the document
13419     * @param {Boolean} refreshCache true to refresh the internal cache
13420     * @return {Object} An object (hash) of rules indexed by selector
13421     */
13422    getRules : function(refreshCache){
13423                 if(rules == null || refreshCache){
13424                         rules = {};
13425                         var ds = doc.styleSheets;
13426                         for(var i =0, len = ds.length; i < len; i++){
13427                             try{
13428                         this.cacheStyleSheet(ds[i]);
13429                     }catch(e){} 
13430                 }
13431                 }
13432                 return rules;
13433         },
13434         
13435         /**
13436     * Gets an an individual CSS rule by selector(s)
13437     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13438     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13439     * @return {CSSRule} The CSS rule or null if one is not found
13440     */
13441    getRule : function(selector, refreshCache){
13442                 var rs = this.getRules(refreshCache);
13443                 if(!(selector instanceof Array)){
13444                     return rs[selector];
13445                 }
13446                 for(var i = 0; i < selector.length; i++){
13447                         if(rs[selector[i]]){
13448                                 return rs[selector[i]];
13449                         }
13450                 }
13451                 return null;
13452         },
13453         
13454         
13455         /**
13456     * Updates a rule property
13457     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13458     * @param {String} property The css property
13459     * @param {String} value The new value for the property
13460     * @return {Boolean} true If a rule was found and updated
13461     */
13462    updateRule : function(selector, property, value){
13463                 if(!(selector instanceof Array)){
13464                         var rule = this.getRule(selector);
13465                         if(rule){
13466                                 rule.style[property.replace(camelRe, camelFn)] = value;
13467                                 return true;
13468                         }
13469                 }else{
13470                         for(var i = 0; i < selector.length; i++){
13471                                 if(this.updateRule(selector[i], property, value)){
13472                                         return true;
13473                                 }
13474                         }
13475                 }
13476                 return false;
13477         }
13478    };   
13479 }();/*
13480  * Based on:
13481  * Ext JS Library 1.1.1
13482  * Copyright(c) 2006-2007, Ext JS, LLC.
13483  *
13484  * Originally Released Under LGPL - original licence link has changed is not relivant.
13485  *
13486  * Fork - LGPL
13487  * <script type="text/javascript">
13488  */
13489
13490  
13491
13492 /**
13493  * @class Roo.util.ClickRepeater
13494  * @extends Roo.util.Observable
13495  * 
13496  * A wrapper class which can be applied to any element. Fires a "click" event while the
13497  * mouse is pressed. The interval between firings may be specified in the config but
13498  * defaults to 10 milliseconds.
13499  * 
13500  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13501  * 
13502  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13503  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13504  * Similar to an autorepeat key delay.
13505  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13506  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13507  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13508  *           "interval" and "delay" are ignored. "immediate" is honored.
13509  * @cfg {Boolean} preventDefault True to prevent the default click event
13510  * @cfg {Boolean} stopDefault True to stop the default click event
13511  * 
13512  * @history
13513  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13514  *     2007-02-02 jvs Renamed to ClickRepeater
13515  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13516  *
13517  *  @constructor
13518  * @param {String/HTMLElement/Element} el The element to listen on
13519  * @param {Object} config
13520  **/
13521 Roo.util.ClickRepeater = function(el, config)
13522 {
13523     this.el = Roo.get(el);
13524     this.el.unselectable();
13525
13526     Roo.apply(this, config);
13527
13528     this.addEvents({
13529     /**
13530      * @event mousedown
13531      * Fires when the mouse button is depressed.
13532      * @param {Roo.util.ClickRepeater} this
13533      */
13534         "mousedown" : true,
13535     /**
13536      * @event click
13537      * Fires on a specified interval during the time the element is pressed.
13538      * @param {Roo.util.ClickRepeater} this
13539      */
13540         "click" : true,
13541     /**
13542      * @event mouseup
13543      * Fires when the mouse key is released.
13544      * @param {Roo.util.ClickRepeater} this
13545      */
13546         "mouseup" : true
13547     });
13548
13549     this.el.on("mousedown", this.handleMouseDown, this);
13550     if(this.preventDefault || this.stopDefault){
13551         this.el.on("click", function(e){
13552             if(this.preventDefault){
13553                 e.preventDefault();
13554             }
13555             if(this.stopDefault){
13556                 e.stopEvent();
13557             }
13558         }, this);
13559     }
13560
13561     // allow inline handler
13562     if(this.handler){
13563         this.on("click", this.handler,  this.scope || this);
13564     }
13565
13566     Roo.util.ClickRepeater.superclass.constructor.call(this);
13567 };
13568
13569 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13570     interval : 20,
13571     delay: 250,
13572     preventDefault : true,
13573     stopDefault : false,
13574     timer : 0,
13575
13576     // private
13577     handleMouseDown : function(){
13578         clearTimeout(this.timer);
13579         this.el.blur();
13580         if(this.pressClass){
13581             this.el.addClass(this.pressClass);
13582         }
13583         this.mousedownTime = new Date();
13584
13585         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13586         this.el.on("mouseout", this.handleMouseOut, this);
13587
13588         this.fireEvent("mousedown", this);
13589         this.fireEvent("click", this);
13590         
13591         this.timer = this.click.defer(this.delay || this.interval, this);
13592     },
13593
13594     // private
13595     click : function(){
13596         this.fireEvent("click", this);
13597         this.timer = this.click.defer(this.getInterval(), this);
13598     },
13599
13600     // private
13601     getInterval: function(){
13602         if(!this.accelerate){
13603             return this.interval;
13604         }
13605         var pressTime = this.mousedownTime.getElapsed();
13606         if(pressTime < 500){
13607             return 400;
13608         }else if(pressTime < 1700){
13609             return 320;
13610         }else if(pressTime < 2600){
13611             return 250;
13612         }else if(pressTime < 3500){
13613             return 180;
13614         }else if(pressTime < 4400){
13615             return 140;
13616         }else if(pressTime < 5300){
13617             return 80;
13618         }else if(pressTime < 6200){
13619             return 50;
13620         }else{
13621             return 10;
13622         }
13623     },
13624
13625     // private
13626     handleMouseOut : function(){
13627         clearTimeout(this.timer);
13628         if(this.pressClass){
13629             this.el.removeClass(this.pressClass);
13630         }
13631         this.el.on("mouseover", this.handleMouseReturn, this);
13632     },
13633
13634     // private
13635     handleMouseReturn : function(){
13636         this.el.un("mouseover", this.handleMouseReturn);
13637         if(this.pressClass){
13638             this.el.addClass(this.pressClass);
13639         }
13640         this.click();
13641     },
13642
13643     // private
13644     handleMouseUp : function(){
13645         clearTimeout(this.timer);
13646         this.el.un("mouseover", this.handleMouseReturn);
13647         this.el.un("mouseout", this.handleMouseOut);
13648         Roo.get(document).un("mouseup", this.handleMouseUp);
13649         this.el.removeClass(this.pressClass);
13650         this.fireEvent("mouseup", this);
13651     }
13652 });/*
13653  * Based on:
13654  * Ext JS Library 1.1.1
13655  * Copyright(c) 2006-2007, Ext JS, LLC.
13656  *
13657  * Originally Released Under LGPL - original licence link has changed is not relivant.
13658  *
13659  * Fork - LGPL
13660  * <script type="text/javascript">
13661  */
13662
13663  
13664 /**
13665  * @class Roo.KeyNav
13666  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13667  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13668  * way to implement custom navigation schemes for any UI component.</p>
13669  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13670  * pageUp, pageDown, del, home, end.  Usage:</p>
13671  <pre><code>
13672 var nav = new Roo.KeyNav("my-element", {
13673     "left" : function(e){
13674         this.moveLeft(e.ctrlKey);
13675     },
13676     "right" : function(e){
13677         this.moveRight(e.ctrlKey);
13678     },
13679     "enter" : function(e){
13680         this.save();
13681     },
13682     scope : this
13683 });
13684 </code></pre>
13685  * @constructor
13686  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13687  * @param {Object} config The config
13688  */
13689 Roo.KeyNav = function(el, config){
13690     this.el = Roo.get(el);
13691     Roo.apply(this, config);
13692     if(!this.disabled){
13693         this.disabled = true;
13694         this.enable();
13695     }
13696 };
13697
13698 Roo.KeyNav.prototype = {
13699     /**
13700      * @cfg {Boolean} disabled
13701      * True to disable this KeyNav instance (defaults to false)
13702      */
13703     disabled : false,
13704     /**
13705      * @cfg {String} defaultEventAction
13706      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13707      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13708      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13709      */
13710     defaultEventAction: "stopEvent",
13711     /**
13712      * @cfg {Boolean} forceKeyDown
13713      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13714      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13715      * handle keydown instead of keypress.
13716      */
13717     forceKeyDown : false,
13718
13719     // private
13720     prepareEvent : function(e){
13721         var k = e.getKey();
13722         var h = this.keyToHandler[k];
13723         //if(h && this[h]){
13724         //    e.stopPropagation();
13725         //}
13726         if(Roo.isSafari && h && k >= 37 && k <= 40){
13727             e.stopEvent();
13728         }
13729     },
13730
13731     // private
13732     relay : function(e){
13733         var k = e.getKey();
13734         var h = this.keyToHandler[k];
13735         if(h && this[h]){
13736             if(this.doRelay(e, this[h], h) !== true){
13737                 e[this.defaultEventAction]();
13738             }
13739         }
13740     },
13741
13742     // private
13743     doRelay : function(e, h, hname){
13744         return h.call(this.scope || this, e);
13745     },
13746
13747     // possible handlers
13748     enter : false,
13749     left : false,
13750     right : false,
13751     up : false,
13752     down : false,
13753     tab : false,
13754     esc : false,
13755     pageUp : false,
13756     pageDown : false,
13757     del : false,
13758     home : false,
13759     end : false,
13760
13761     // quick lookup hash
13762     keyToHandler : {
13763         37 : "left",
13764         39 : "right",
13765         38 : "up",
13766         40 : "down",
13767         33 : "pageUp",
13768         34 : "pageDown",
13769         46 : "del",
13770         36 : "home",
13771         35 : "end",
13772         13 : "enter",
13773         27 : "esc",
13774         9  : "tab"
13775     },
13776
13777         /**
13778          * Enable this KeyNav
13779          */
13780         enable: function(){
13781                 if(this.disabled){
13782             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13783             // the EventObject will normalize Safari automatically
13784             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13785                 this.el.on("keydown", this.relay,  this);
13786             }else{
13787                 this.el.on("keydown", this.prepareEvent,  this);
13788                 this.el.on("keypress", this.relay,  this);
13789             }
13790                     this.disabled = false;
13791                 }
13792         },
13793
13794         /**
13795          * Disable this KeyNav
13796          */
13797         disable: function(){
13798                 if(!this.disabled){
13799                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13800                 this.el.un("keydown", this.relay);
13801             }else{
13802                 this.el.un("keydown", this.prepareEvent);
13803                 this.el.un("keypress", this.relay);
13804             }
13805                     this.disabled = true;
13806                 }
13807         }
13808 };/*
13809  * Based on:
13810  * Ext JS Library 1.1.1
13811  * Copyright(c) 2006-2007, Ext JS, LLC.
13812  *
13813  * Originally Released Under LGPL - original licence link has changed is not relivant.
13814  *
13815  * Fork - LGPL
13816  * <script type="text/javascript">
13817  */
13818
13819  
13820 /**
13821  * @class Roo.KeyMap
13822  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13823  * The constructor accepts the same config object as defined by {@link #addBinding}.
13824  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13825  * combination it will call the function with this signature (if the match is a multi-key
13826  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13827  * A KeyMap can also handle a string representation of keys.<br />
13828  * Usage:
13829  <pre><code>
13830 // map one key by key code
13831 var map = new Roo.KeyMap("my-element", {
13832     key: 13, // or Roo.EventObject.ENTER
13833     fn: myHandler,
13834     scope: myObject
13835 });
13836
13837 // map multiple keys to one action by string
13838 var map = new Roo.KeyMap("my-element", {
13839     key: "a\r\n\t",
13840     fn: myHandler,
13841     scope: myObject
13842 });
13843
13844 // map multiple keys to multiple actions by strings and array of codes
13845 var map = new Roo.KeyMap("my-element", [
13846     {
13847         key: [10,13],
13848         fn: function(){ alert("Return was pressed"); }
13849     }, {
13850         key: "abc",
13851         fn: function(){ alert('a, b or c was pressed'); }
13852     }, {
13853         key: "\t",
13854         ctrl:true,
13855         shift:true,
13856         fn: function(){ alert('Control + shift + tab was pressed.'); }
13857     }
13858 ]);
13859 </code></pre>
13860  * <b>Note: A KeyMap starts enabled</b>
13861  * @constructor
13862  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13863  * @param {Object} config The config (see {@link #addBinding})
13864  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13865  */
13866 Roo.KeyMap = function(el, config, eventName){
13867     this.el  = Roo.get(el);
13868     this.eventName = eventName || "keydown";
13869     this.bindings = [];
13870     if(config){
13871         this.addBinding(config);
13872     }
13873     this.enable();
13874 };
13875
13876 Roo.KeyMap.prototype = {
13877     /**
13878      * True to stop the event from bubbling and prevent the default browser action if the
13879      * key was handled by the KeyMap (defaults to false)
13880      * @type Boolean
13881      */
13882     stopEvent : false,
13883
13884     /**
13885      * Add a new binding to this KeyMap. The following config object properties are supported:
13886      * <pre>
13887 Property    Type             Description
13888 ----------  ---------------  ----------------------------------------------------------------------
13889 key         String/Array     A single keycode or an array of keycodes to handle
13890 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13891 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13892 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13893 fn          Function         The function to call when KeyMap finds the expected key combination
13894 scope       Object           The scope of the callback function
13895 </pre>
13896      *
13897      * Usage:
13898      * <pre><code>
13899 // Create a KeyMap
13900 var map = new Roo.KeyMap(document, {
13901     key: Roo.EventObject.ENTER,
13902     fn: handleKey,
13903     scope: this
13904 });
13905
13906 //Add a new binding to the existing KeyMap later
13907 map.addBinding({
13908     key: 'abc',
13909     shift: true,
13910     fn: handleKey,
13911     scope: this
13912 });
13913 </code></pre>
13914      * @param {Object/Array} config A single KeyMap config or an array of configs
13915      */
13916         addBinding : function(config){
13917         if(config instanceof Array){
13918             for(var i = 0, len = config.length; i < len; i++){
13919                 this.addBinding(config[i]);
13920             }
13921             return;
13922         }
13923         var keyCode = config.key,
13924             shift = config.shift, 
13925             ctrl = config.ctrl, 
13926             alt = config.alt,
13927             fn = config.fn,
13928             scope = config.scope;
13929         if(typeof keyCode == "string"){
13930             var ks = [];
13931             var keyString = keyCode.toUpperCase();
13932             for(var j = 0, len = keyString.length; j < len; j++){
13933                 ks.push(keyString.charCodeAt(j));
13934             }
13935             keyCode = ks;
13936         }
13937         var keyArray = keyCode instanceof Array;
13938         var handler = function(e){
13939             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13940                 var k = e.getKey();
13941                 if(keyArray){
13942                     for(var i = 0, len = keyCode.length; i < len; i++){
13943                         if(keyCode[i] == k){
13944                           if(this.stopEvent){
13945                               e.stopEvent();
13946                           }
13947                           fn.call(scope || window, k, e);
13948                           return;
13949                         }
13950                     }
13951                 }else{
13952                     if(k == keyCode){
13953                         if(this.stopEvent){
13954                            e.stopEvent();
13955                         }
13956                         fn.call(scope || window, k, e);
13957                     }
13958                 }
13959             }
13960         };
13961         this.bindings.push(handler);  
13962         },
13963
13964     /**
13965      * Shorthand for adding a single key listener
13966      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13967      * following options:
13968      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13969      * @param {Function} fn The function to call
13970      * @param {Object} scope (optional) The scope of the function
13971      */
13972     on : function(key, fn, scope){
13973         var keyCode, shift, ctrl, alt;
13974         if(typeof key == "object" && !(key instanceof Array)){
13975             keyCode = key.key;
13976             shift = key.shift;
13977             ctrl = key.ctrl;
13978             alt = key.alt;
13979         }else{
13980             keyCode = key;
13981         }
13982         this.addBinding({
13983             key: keyCode,
13984             shift: shift,
13985             ctrl: ctrl,
13986             alt: alt,
13987             fn: fn,
13988             scope: scope
13989         })
13990     },
13991
13992     // private
13993     handleKeyDown : function(e){
13994             if(this.enabled){ //just in case
13995             var b = this.bindings;
13996             for(var i = 0, len = b.length; i < len; i++){
13997                 b[i].call(this, e);
13998             }
13999             }
14000         },
14001         
14002         /**
14003          * Returns true if this KeyMap is enabled
14004          * @return {Boolean} 
14005          */
14006         isEnabled : function(){
14007             return this.enabled;  
14008         },
14009         
14010         /**
14011          * Enables this KeyMap
14012          */
14013         enable: function(){
14014                 if(!this.enabled){
14015                     this.el.on(this.eventName, this.handleKeyDown, this);
14016                     this.enabled = true;
14017                 }
14018         },
14019
14020         /**
14021          * Disable this KeyMap
14022          */
14023         disable: function(){
14024                 if(this.enabled){
14025                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14026                     this.enabled = false;
14027                 }
14028         }
14029 };/*
14030  * Based on:
14031  * Ext JS Library 1.1.1
14032  * Copyright(c) 2006-2007, Ext JS, LLC.
14033  *
14034  * Originally Released Under LGPL - original licence link has changed is not relivant.
14035  *
14036  * Fork - LGPL
14037  * <script type="text/javascript">
14038  */
14039
14040  
14041 /**
14042  * @class Roo.util.TextMetrics
14043  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14044  * wide, in pixels, a given block of text will be.
14045  * @singleton
14046  */
14047 Roo.util.TextMetrics = function(){
14048     var shared;
14049     return {
14050         /**
14051          * Measures the size of the specified text
14052          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14053          * that can affect the size of the rendered text
14054          * @param {String} text The text to measure
14055          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14056          * in order to accurately measure the text height
14057          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14058          */
14059         measure : function(el, text, fixedWidth){
14060             if(!shared){
14061                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14062             }
14063             shared.bind(el);
14064             shared.setFixedWidth(fixedWidth || 'auto');
14065             return shared.getSize(text);
14066         },
14067
14068         /**
14069          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14070          * the overhead of multiple calls to initialize the style properties on each measurement.
14071          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14072          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14073          * in order to accurately measure the text height
14074          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14075          */
14076         createInstance : function(el, fixedWidth){
14077             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14078         }
14079     };
14080 }();
14081
14082  
14083
14084 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14085     var ml = new Roo.Element(document.createElement('div'));
14086     document.body.appendChild(ml.dom);
14087     ml.position('absolute');
14088     ml.setLeftTop(-1000, -1000);
14089     ml.hide();
14090
14091     if(fixedWidth){
14092         ml.setWidth(fixedWidth);
14093     }
14094      
14095     var instance = {
14096         /**
14097          * Returns the size of the specified text based on the internal element's style and width properties
14098          * @memberOf Roo.util.TextMetrics.Instance#
14099          * @param {String} text The text to measure
14100          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14101          */
14102         getSize : function(text){
14103             ml.update(text);
14104             var s = ml.getSize();
14105             ml.update('');
14106             return s;
14107         },
14108
14109         /**
14110          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14111          * that can affect the size of the rendered text
14112          * @memberOf Roo.util.TextMetrics.Instance#
14113          * @param {String/HTMLElement} el The element, dom node or id
14114          */
14115         bind : function(el){
14116             ml.setStyle(
14117                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14118             );
14119         },
14120
14121         /**
14122          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14123          * to set a fixed width in order to accurately measure the text height.
14124          * @memberOf Roo.util.TextMetrics.Instance#
14125          * @param {Number} width The width to set on the element
14126          */
14127         setFixedWidth : function(width){
14128             ml.setWidth(width);
14129         },
14130
14131         /**
14132          * Returns the measured width of the specified text
14133          * @memberOf Roo.util.TextMetrics.Instance#
14134          * @param {String} text The text to measure
14135          * @return {Number} width The width in pixels
14136          */
14137         getWidth : function(text){
14138             ml.dom.style.width = 'auto';
14139             return this.getSize(text).width;
14140         },
14141
14142         /**
14143          * Returns the measured height of the specified text.  For multiline text, be sure to call
14144          * {@link #setFixedWidth} if necessary.
14145          * @memberOf Roo.util.TextMetrics.Instance#
14146          * @param {String} text The text to measure
14147          * @return {Number} height The height in pixels
14148          */
14149         getHeight : function(text){
14150             return this.getSize(text).height;
14151         }
14152     };
14153
14154     instance.bind(bindTo);
14155
14156     return instance;
14157 };
14158
14159 // backwards compat
14160 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14161  * Based on:
14162  * Ext JS Library 1.1.1
14163  * Copyright(c) 2006-2007, Ext JS, LLC.
14164  *
14165  * Originally Released Under LGPL - original licence link has changed is not relivant.
14166  *
14167  * Fork - LGPL
14168  * <script type="text/javascript">
14169  */
14170
14171 /**
14172  * @class Roo.state.Provider
14173  * Abstract base class for state provider implementations. This class provides methods
14174  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14175  * Provider interface.
14176  */
14177 Roo.state.Provider = function(){
14178     /**
14179      * @event statechange
14180      * Fires when a state change occurs.
14181      * @param {Provider} this This state provider
14182      * @param {String} key The state key which was changed
14183      * @param {String} value The encoded value for the state
14184      */
14185     this.addEvents({
14186         "statechange": true
14187     });
14188     this.state = {};
14189     Roo.state.Provider.superclass.constructor.call(this);
14190 };
14191 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14192     /**
14193      * Returns the current value for a key
14194      * @param {String} name The key name
14195      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14196      * @return {Mixed} The state data
14197      */
14198     get : function(name, defaultValue){
14199         return typeof this.state[name] == "undefined" ?
14200             defaultValue : this.state[name];
14201     },
14202     
14203     /**
14204      * Clears a value from the state
14205      * @param {String} name The key name
14206      */
14207     clear : function(name){
14208         delete this.state[name];
14209         this.fireEvent("statechange", this, name, null);
14210     },
14211     
14212     /**
14213      * Sets the value for a key
14214      * @param {String} name The key name
14215      * @param {Mixed} value The value to set
14216      */
14217     set : function(name, value){
14218         this.state[name] = value;
14219         this.fireEvent("statechange", this, name, value);
14220     },
14221     
14222     /**
14223      * Decodes a string previously encoded with {@link #encodeValue}.
14224      * @param {String} value The value to decode
14225      * @return {Mixed} The decoded value
14226      */
14227     decodeValue : function(cookie){
14228         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14229         var matches = re.exec(unescape(cookie));
14230         if(!matches || !matches[1]) return; // non state cookie
14231         var type = matches[1];
14232         var v = matches[2];
14233         switch(type){
14234             case "n":
14235                 return parseFloat(v);
14236             case "d":
14237                 return new Date(Date.parse(v));
14238             case "b":
14239                 return (v == "1");
14240             case "a":
14241                 var all = [];
14242                 var values = v.split("^");
14243                 for(var i = 0, len = values.length; i < len; i++){
14244                     all.push(this.decodeValue(values[i]));
14245                 }
14246                 return all;
14247            case "o":
14248                 var all = {};
14249                 var values = v.split("^");
14250                 for(var i = 0, len = values.length; i < len; i++){
14251                     var kv = values[i].split("=");
14252                     all[kv[0]] = this.decodeValue(kv[1]);
14253                 }
14254                 return all;
14255            default:
14256                 return v;
14257         }
14258     },
14259     
14260     /**
14261      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14262      * @param {Mixed} value The value to encode
14263      * @return {String} The encoded value
14264      */
14265     encodeValue : function(v){
14266         var enc;
14267         if(typeof v == "number"){
14268             enc = "n:" + v;
14269         }else if(typeof v == "boolean"){
14270             enc = "b:" + (v ? "1" : "0");
14271         }else if(v instanceof Date){
14272             enc = "d:" + v.toGMTString();
14273         }else if(v instanceof Array){
14274             var flat = "";
14275             for(var i = 0, len = v.length; i < len; i++){
14276                 flat += this.encodeValue(v[i]);
14277                 if(i != len-1) flat += "^";
14278             }
14279             enc = "a:" + flat;
14280         }else if(typeof v == "object"){
14281             var flat = "";
14282             for(var key in v){
14283                 if(typeof v[key] != "function"){
14284                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14285                 }
14286             }
14287             enc = "o:" + flat.substring(0, flat.length-1);
14288         }else{
14289             enc = "s:" + v;
14290         }
14291         return escape(enc);        
14292     }
14293 });
14294
14295 /*
14296  * Based on:
14297  * Ext JS Library 1.1.1
14298  * Copyright(c) 2006-2007, Ext JS, LLC.
14299  *
14300  * Originally Released Under LGPL - original licence link has changed is not relivant.
14301  *
14302  * Fork - LGPL
14303  * <script type="text/javascript">
14304  */
14305 /**
14306  * @class Roo.state.Manager
14307  * This is the global state manager. By default all components that are "state aware" check this class
14308  * for state information if you don't pass them a custom state provider. In order for this class
14309  * to be useful, it must be initialized with a provider when your application initializes.
14310  <pre><code>
14311 // in your initialization function
14312 init : function(){
14313    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14314    ...
14315    // supposed you have a {@link Roo.BorderLayout}
14316    var layout = new Roo.BorderLayout(...);
14317    layout.restoreState();
14318    // or a {Roo.BasicDialog}
14319    var dialog = new Roo.BasicDialog(...);
14320    dialog.restoreState();
14321  </code></pre>
14322  * @singleton
14323  */
14324 Roo.state.Manager = function(){
14325     var provider = new Roo.state.Provider();
14326     
14327     return {
14328         /**
14329          * Configures the default state provider for your application
14330          * @param {Provider} stateProvider The state provider to set
14331          */
14332         setProvider : function(stateProvider){
14333             provider = stateProvider;
14334         },
14335         
14336         /**
14337          * Returns the current value for a key
14338          * @param {String} name The key name
14339          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14340          * @return {Mixed} The state data
14341          */
14342         get : function(key, defaultValue){
14343             return provider.get(key, defaultValue);
14344         },
14345         
14346         /**
14347          * Sets the value for a key
14348          * @param {String} name The key name
14349          * @param {Mixed} value The state data
14350          */
14351          set : function(key, value){
14352             provider.set(key, value);
14353         },
14354         
14355         /**
14356          * Clears a value from the state
14357          * @param {String} name The key name
14358          */
14359         clear : function(key){
14360             provider.clear(key);
14361         },
14362         
14363         /**
14364          * Gets the currently configured state provider
14365          * @return {Provider} The state provider
14366          */
14367         getProvider : function(){
14368             return provider;
14369         }
14370     };
14371 }();
14372 /*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382 /**
14383  * @class Roo.state.CookieProvider
14384  * @extends Roo.state.Provider
14385  * The default Provider implementation which saves state via cookies.
14386  * <br />Usage:
14387  <pre><code>
14388    var cp = new Roo.state.CookieProvider({
14389        path: "/cgi-bin/",
14390        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14391        domain: "roojs.com"
14392    })
14393    Roo.state.Manager.setProvider(cp);
14394  </code></pre>
14395  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14396  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14397  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14398  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14399  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14400  * domain the page is running on including the 'www' like 'www.roojs.com')
14401  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14402  * @constructor
14403  * Create a new CookieProvider
14404  * @param {Object} config The configuration object
14405  */
14406 Roo.state.CookieProvider = function(config){
14407     Roo.state.CookieProvider.superclass.constructor.call(this);
14408     this.path = "/";
14409     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14410     this.domain = null;
14411     this.secure = false;
14412     Roo.apply(this, config);
14413     this.state = this.readCookies();
14414 };
14415
14416 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14417     // private
14418     set : function(name, value){
14419         if(typeof value == "undefined" || value === null){
14420             this.clear(name);
14421             return;
14422         }
14423         this.setCookie(name, value);
14424         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14425     },
14426
14427     // private
14428     clear : function(name){
14429         this.clearCookie(name);
14430         Roo.state.CookieProvider.superclass.clear.call(this, name);
14431     },
14432
14433     // private
14434     readCookies : function(){
14435         var cookies = {};
14436         var c = document.cookie + ";";
14437         var re = /\s?(.*?)=(.*?);/g;
14438         var matches;
14439         while((matches = re.exec(c)) != null){
14440             var name = matches[1];
14441             var value = matches[2];
14442             if(name && name.substring(0,3) == "ys-"){
14443                 cookies[name.substr(3)] = this.decodeValue(value);
14444             }
14445         }
14446         return cookies;
14447     },
14448
14449     // private
14450     setCookie : function(name, value){
14451         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14452            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14453            ((this.path == null) ? "" : ("; path=" + this.path)) +
14454            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14455            ((this.secure == true) ? "; secure" : "");
14456     },
14457
14458     // private
14459     clearCookie : function(name){
14460         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14461            ((this.path == null) ? "" : ("; path=" + this.path)) +
14462            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14463            ((this.secure == true) ? "; secure" : "");
14464     }
14465 });/*
14466  * Based on:
14467  * Ext JS Library 1.1.1
14468  * Copyright(c) 2006-2007, Ext JS, LLC.
14469  *
14470  * Originally Released Under LGPL - original licence link has changed is not relivant.
14471  *
14472  * Fork - LGPL
14473  * <script type="text/javascript">
14474  */
14475
14476
14477
14478 /*
14479  * These classes are derivatives of the similarly named classes in the YUI Library.
14480  * The original license:
14481  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14482  * Code licensed under the BSD License:
14483  * http://developer.yahoo.net/yui/license.txt
14484  */
14485
14486 (function() {
14487
14488 var Event=Roo.EventManager;
14489 var Dom=Roo.lib.Dom;
14490
14491 /**
14492  * @class Roo.dd.DragDrop
14493  * @extends Roo.util.Observable
14494  * Defines the interface and base operation of items that that can be
14495  * dragged or can be drop targets.  It was designed to be extended, overriding
14496  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14497  * Up to three html elements can be associated with a DragDrop instance:
14498  * <ul>
14499  * <li>linked element: the element that is passed into the constructor.
14500  * This is the element which defines the boundaries for interaction with
14501  * other DragDrop objects.</li>
14502  * <li>handle element(s): The drag operation only occurs if the element that
14503  * was clicked matches a handle element.  By default this is the linked
14504  * element, but there are times that you will want only a portion of the
14505  * linked element to initiate the drag operation, and the setHandleElId()
14506  * method provides a way to define this.</li>
14507  * <li>drag element: this represents the element that would be moved along
14508  * with the cursor during a drag operation.  By default, this is the linked
14509  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14510  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14511  * </li>
14512  * </ul>
14513  * This class should not be instantiated until the onload event to ensure that
14514  * the associated elements are available.
14515  * The following would define a DragDrop obj that would interact with any
14516  * other DragDrop obj in the "group1" group:
14517  * <pre>
14518  *  dd = new Roo.dd.DragDrop("div1", "group1");
14519  * </pre>
14520  * Since none of the event handlers have been implemented, nothing would
14521  * actually happen if you were to run the code above.  Normally you would
14522  * override this class or one of the default implementations, but you can
14523  * also override the methods you want on an instance of the class...
14524  * <pre>
14525  *  dd.onDragDrop = function(e, id) {
14526  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14527  *  }
14528  * </pre>
14529  * @constructor
14530  * @param {String} id of the element that is linked to this instance
14531  * @param {String} sGroup the group of related DragDrop objects
14532  * @param {object} config an object containing configurable attributes
14533  *                Valid properties for DragDrop:
14534  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14535  */
14536 Roo.dd.DragDrop = function(id, sGroup, config) {
14537     if (id) {
14538         this.init(id, sGroup, config);
14539     }
14540     
14541 };
14542
14543 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14544
14545     /**
14546      * The id of the element associated with this object.  This is what we
14547      * refer to as the "linked element" because the size and position of
14548      * this element is used to determine when the drag and drop objects have
14549      * interacted.
14550      * @property id
14551      * @type String
14552      */
14553     id: null,
14554
14555     /**
14556      * Configuration attributes passed into the constructor
14557      * @property config
14558      * @type object
14559      */
14560     config: null,
14561
14562     /**
14563      * The id of the element that will be dragged.  By default this is same
14564      * as the linked element , but could be changed to another element. Ex:
14565      * Roo.dd.DDProxy
14566      * @property dragElId
14567      * @type String
14568      * @private
14569      */
14570     dragElId: null,
14571
14572     /**
14573      * the id of the element that initiates the drag operation.  By default
14574      * this is the linked element, but could be changed to be a child of this
14575      * element.  This lets us do things like only starting the drag when the
14576      * header element within the linked html element is clicked.
14577      * @property handleElId
14578      * @type String
14579      * @private
14580      */
14581     handleElId: null,
14582
14583     /**
14584      * An associative array of HTML tags that will be ignored if clicked.
14585      * @property invalidHandleTypes
14586      * @type {string: string}
14587      */
14588     invalidHandleTypes: null,
14589
14590     /**
14591      * An associative array of ids for elements that will be ignored if clicked
14592      * @property invalidHandleIds
14593      * @type {string: string}
14594      */
14595     invalidHandleIds: null,
14596
14597     /**
14598      * An indexted array of css class names for elements that will be ignored
14599      * if clicked.
14600      * @property invalidHandleClasses
14601      * @type string[]
14602      */
14603     invalidHandleClasses: null,
14604
14605     /**
14606      * The linked element's absolute X position at the time the drag was
14607      * started
14608      * @property startPageX
14609      * @type int
14610      * @private
14611      */
14612     startPageX: 0,
14613
14614     /**
14615      * The linked element's absolute X position at the time the drag was
14616      * started
14617      * @property startPageY
14618      * @type int
14619      * @private
14620      */
14621     startPageY: 0,
14622
14623     /**
14624      * The group defines a logical collection of DragDrop objects that are
14625      * related.  Instances only get events when interacting with other
14626      * DragDrop object in the same group.  This lets us define multiple
14627      * groups using a single DragDrop subclass if we want.
14628      * @property groups
14629      * @type {string: string}
14630      */
14631     groups: null,
14632
14633     /**
14634      * Individual drag/drop instances can be locked.  This will prevent
14635      * onmousedown start drag.
14636      * @property locked
14637      * @type boolean
14638      * @private
14639      */
14640     locked: false,
14641
14642     /**
14643      * Lock this instance
14644      * @method lock
14645      */
14646     lock: function() { this.locked = true; },
14647
14648     /**
14649      * Unlock this instace
14650      * @method unlock
14651      */
14652     unlock: function() { this.locked = false; },
14653
14654     /**
14655      * By default, all insances can be a drop target.  This can be disabled by
14656      * setting isTarget to false.
14657      * @method isTarget
14658      * @type boolean
14659      */
14660     isTarget: true,
14661
14662     /**
14663      * The padding configured for this drag and drop object for calculating
14664      * the drop zone intersection with this object.
14665      * @method padding
14666      * @type int[]
14667      */
14668     padding: null,
14669
14670     /**
14671      * Cached reference to the linked element
14672      * @property _domRef
14673      * @private
14674      */
14675     _domRef: null,
14676
14677     /**
14678      * Internal typeof flag
14679      * @property __ygDragDrop
14680      * @private
14681      */
14682     __ygDragDrop: true,
14683
14684     /**
14685      * Set to true when horizontal contraints are applied
14686      * @property constrainX
14687      * @type boolean
14688      * @private
14689      */
14690     constrainX: false,
14691
14692     /**
14693      * Set to true when vertical contraints are applied
14694      * @property constrainY
14695      * @type boolean
14696      * @private
14697      */
14698     constrainY: false,
14699
14700     /**
14701      * The left constraint
14702      * @property minX
14703      * @type int
14704      * @private
14705      */
14706     minX: 0,
14707
14708     /**
14709      * The right constraint
14710      * @property maxX
14711      * @type int
14712      * @private
14713      */
14714     maxX: 0,
14715
14716     /**
14717      * The up constraint
14718      * @property minY
14719      * @type int
14720      * @type int
14721      * @private
14722      */
14723     minY: 0,
14724
14725     /**
14726      * The down constraint
14727      * @property maxY
14728      * @type int
14729      * @private
14730      */
14731     maxY: 0,
14732
14733     /**
14734      * Maintain offsets when we resetconstraints.  Set to true when you want
14735      * the position of the element relative to its parent to stay the same
14736      * when the page changes
14737      *
14738      * @property maintainOffset
14739      * @type boolean
14740      */
14741     maintainOffset: false,
14742
14743     /**
14744      * Array of pixel locations the element will snap to if we specified a
14745      * horizontal graduation/interval.  This array is generated automatically
14746      * when you define a tick interval.
14747      * @property xTicks
14748      * @type int[]
14749      */
14750     xTicks: null,
14751
14752     /**
14753      * Array of pixel locations the element will snap to if we specified a
14754      * vertical graduation/interval.  This array is generated automatically
14755      * when you define a tick interval.
14756      * @property yTicks
14757      * @type int[]
14758      */
14759     yTicks: null,
14760
14761     /**
14762      * By default the drag and drop instance will only respond to the primary
14763      * button click (left button for a right-handed mouse).  Set to true to
14764      * allow drag and drop to start with any mouse click that is propogated
14765      * by the browser
14766      * @property primaryButtonOnly
14767      * @type boolean
14768      */
14769     primaryButtonOnly: true,
14770
14771     /**
14772      * The availabe property is false until the linked dom element is accessible.
14773      * @property available
14774      * @type boolean
14775      */
14776     available: false,
14777
14778     /**
14779      * By default, drags can only be initiated if the mousedown occurs in the
14780      * region the linked element is.  This is done in part to work around a
14781      * bug in some browsers that mis-report the mousedown if the previous
14782      * mouseup happened outside of the window.  This property is set to true
14783      * if outer handles are defined.
14784      *
14785      * @property hasOuterHandles
14786      * @type boolean
14787      * @default false
14788      */
14789     hasOuterHandles: false,
14790
14791     /**
14792      * Code that executes immediately before the startDrag event
14793      * @method b4StartDrag
14794      * @private
14795      */
14796     b4StartDrag: function(x, y) { },
14797
14798     /**
14799      * Abstract method called after a drag/drop object is clicked
14800      * and the drag or mousedown time thresholds have beeen met.
14801      * @method startDrag
14802      * @param {int} X click location
14803      * @param {int} Y click location
14804      */
14805     startDrag: function(x, y) { /* override this */ },
14806
14807     /**
14808      * Code that executes immediately before the onDrag event
14809      * @method b4Drag
14810      * @private
14811      */
14812     b4Drag: function(e) { },
14813
14814     /**
14815      * Abstract method called during the onMouseMove event while dragging an
14816      * object.
14817      * @method onDrag
14818      * @param {Event} e the mousemove event
14819      */
14820     onDrag: function(e) { /* override this */ },
14821
14822     /**
14823      * Abstract method called when this element fist begins hovering over
14824      * another DragDrop obj
14825      * @method onDragEnter
14826      * @param {Event} e the mousemove event
14827      * @param {String|DragDrop[]} id In POINT mode, the element
14828      * id this is hovering over.  In INTERSECT mode, an array of one or more
14829      * dragdrop items being hovered over.
14830      */
14831     onDragEnter: function(e, id) { /* override this */ },
14832
14833     /**
14834      * Code that executes immediately before the onDragOver event
14835      * @method b4DragOver
14836      * @private
14837      */
14838     b4DragOver: function(e) { },
14839
14840     /**
14841      * Abstract method called when this element is hovering over another
14842      * DragDrop obj
14843      * @method onDragOver
14844      * @param {Event} e the mousemove event
14845      * @param {String|DragDrop[]} id In POINT mode, the element
14846      * id this is hovering over.  In INTERSECT mode, an array of dd items
14847      * being hovered over.
14848      */
14849     onDragOver: function(e, id) { /* override this */ },
14850
14851     /**
14852      * Code that executes immediately before the onDragOut event
14853      * @method b4DragOut
14854      * @private
14855      */
14856     b4DragOut: function(e) { },
14857
14858     /**
14859      * Abstract method called when we are no longer hovering over an element
14860      * @method onDragOut
14861      * @param {Event} e the mousemove event
14862      * @param {String|DragDrop[]} id In POINT mode, the element
14863      * id this was hovering over.  In INTERSECT mode, an array of dd items
14864      * that the mouse is no longer over.
14865      */
14866     onDragOut: function(e, id) { /* override this */ },
14867
14868     /**
14869      * Code that executes immediately before the onDragDrop event
14870      * @method b4DragDrop
14871      * @private
14872      */
14873     b4DragDrop: function(e) { },
14874
14875     /**
14876      * Abstract method called when this item is dropped on another DragDrop
14877      * obj
14878      * @method onDragDrop
14879      * @param {Event} e the mouseup event
14880      * @param {String|DragDrop[]} id In POINT mode, the element
14881      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14882      * was dropped on.
14883      */
14884     onDragDrop: function(e, id) { /* override this */ },
14885
14886     /**
14887      * Abstract method called when this item is dropped on an area with no
14888      * drop target
14889      * @method onInvalidDrop
14890      * @param {Event} e the mouseup event
14891      */
14892     onInvalidDrop: function(e) { /* override this */ },
14893
14894     /**
14895      * Code that executes immediately before the endDrag event
14896      * @method b4EndDrag
14897      * @private
14898      */
14899     b4EndDrag: function(e) { },
14900
14901     /**
14902      * Fired when we are done dragging the object
14903      * @method endDrag
14904      * @param {Event} e the mouseup event
14905      */
14906     endDrag: function(e) { /* override this */ },
14907
14908     /**
14909      * Code executed immediately before the onMouseDown event
14910      * @method b4MouseDown
14911      * @param {Event} e the mousedown event
14912      * @private
14913      */
14914     b4MouseDown: function(e) {  },
14915
14916     /**
14917      * Event handler that fires when a drag/drop obj gets a mousedown
14918      * @method onMouseDown
14919      * @param {Event} e the mousedown event
14920      */
14921     onMouseDown: function(e) { /* override this */ },
14922
14923     /**
14924      * Event handler that fires when a drag/drop obj gets a mouseup
14925      * @method onMouseUp
14926      * @param {Event} e the mouseup event
14927      */
14928     onMouseUp: function(e) { /* override this */ },
14929
14930     /**
14931      * Override the onAvailable method to do what is needed after the initial
14932      * position was determined.
14933      * @method onAvailable
14934      */
14935     onAvailable: function () {
14936     },
14937
14938     /*
14939      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14940      * @type Object
14941      */
14942     defaultPadding : {left:0, right:0, top:0, bottom:0},
14943
14944     /*
14945      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14946  *
14947  * Usage:
14948  <pre><code>
14949  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14950                 { dragElId: "existingProxyDiv" });
14951  dd.startDrag = function(){
14952      this.constrainTo("parent-id");
14953  };
14954  </code></pre>
14955  * Or you can initalize it using the {@link Roo.Element} object:
14956  <pre><code>
14957  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14958      startDrag : function(){
14959          this.constrainTo("parent-id");
14960      }
14961  });
14962  </code></pre>
14963      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14964      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14965      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14966      * an object containing the sides to pad. For example: {right:10, bottom:10}
14967      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14968      */
14969     constrainTo : function(constrainTo, pad, inContent){
14970         if(typeof pad == "number"){
14971             pad = {left: pad, right:pad, top:pad, bottom:pad};
14972         }
14973         pad = pad || this.defaultPadding;
14974         var b = Roo.get(this.getEl()).getBox();
14975         var ce = Roo.get(constrainTo);
14976         var s = ce.getScroll();
14977         var c, cd = ce.dom;
14978         if(cd == document.body){
14979             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14980         }else{
14981             xy = ce.getXY();
14982             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14983         }
14984
14985
14986         var topSpace = b.y - c.y;
14987         var leftSpace = b.x - c.x;
14988
14989         this.resetConstraints();
14990         this.setXConstraint(leftSpace - (pad.left||0), // left
14991                 c.width - leftSpace - b.width - (pad.right||0) //right
14992         );
14993         this.setYConstraint(topSpace - (pad.top||0), //top
14994                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14995         );
14996     },
14997
14998     /**
14999      * Returns a reference to the linked element
15000      * @method getEl
15001      * @return {HTMLElement} the html element
15002      */
15003     getEl: function() {
15004         if (!this._domRef) {
15005             this._domRef = Roo.getDom(this.id);
15006         }
15007
15008         return this._domRef;
15009     },
15010
15011     /**
15012      * Returns a reference to the actual element to drag.  By default this is
15013      * the same as the html element, but it can be assigned to another
15014      * element. An example of this can be found in Roo.dd.DDProxy
15015      * @method getDragEl
15016      * @return {HTMLElement} the html element
15017      */
15018     getDragEl: function() {
15019         return Roo.getDom(this.dragElId);
15020     },
15021
15022     /**
15023      * Sets up the DragDrop object.  Must be called in the constructor of any
15024      * Roo.dd.DragDrop subclass
15025      * @method init
15026      * @param id the id of the linked element
15027      * @param {String} sGroup the group of related items
15028      * @param {object} config configuration attributes
15029      */
15030     init: function(id, sGroup, config) {
15031         this.initTarget(id, sGroup, config);
15032         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15033         // Event.on(this.id, "selectstart", Event.preventDefault);
15034     },
15035
15036     /**
15037      * Initializes Targeting functionality only... the object does not
15038      * get a mousedown handler.
15039      * @method initTarget
15040      * @param id the id of the linked element
15041      * @param {String} sGroup the group of related items
15042      * @param {object} config configuration attributes
15043      */
15044     initTarget: function(id, sGroup, config) {
15045
15046         // configuration attributes
15047         this.config = config || {};
15048
15049         // create a local reference to the drag and drop manager
15050         this.DDM = Roo.dd.DDM;
15051         // initialize the groups array
15052         this.groups = {};
15053
15054         // assume that we have an element reference instead of an id if the
15055         // parameter is not a string
15056         if (typeof id !== "string") {
15057             id = Roo.id(id);
15058         }
15059
15060         // set the id
15061         this.id = id;
15062
15063         // add to an interaction group
15064         this.addToGroup((sGroup) ? sGroup : "default");
15065
15066         // We don't want to register this as the handle with the manager
15067         // so we just set the id rather than calling the setter.
15068         this.handleElId = id;
15069
15070         // the linked element is the element that gets dragged by default
15071         this.setDragElId(id);
15072
15073         // by default, clicked anchors will not start drag operations.
15074         this.invalidHandleTypes = { A: "A" };
15075         this.invalidHandleIds = {};
15076         this.invalidHandleClasses = [];
15077
15078         this.applyConfig();
15079
15080         this.handleOnAvailable();
15081     },
15082
15083     /**
15084      * Applies the configuration parameters that were passed into the constructor.
15085      * This is supposed to happen at each level through the inheritance chain.  So
15086      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15087      * DragDrop in order to get all of the parameters that are available in
15088      * each object.
15089      * @method applyConfig
15090      */
15091     applyConfig: function() {
15092
15093         // configurable properties:
15094         //    padding, isTarget, maintainOffset, primaryButtonOnly
15095         this.padding           = this.config.padding || [0, 0, 0, 0];
15096         this.isTarget          = (this.config.isTarget !== false);
15097         this.maintainOffset    = (this.config.maintainOffset);
15098         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15099
15100     },
15101
15102     /**
15103      * Executed when the linked element is available
15104      * @method handleOnAvailable
15105      * @private
15106      */
15107     handleOnAvailable: function() {
15108         this.available = true;
15109         this.resetConstraints();
15110         this.onAvailable();
15111     },
15112
15113      /**
15114      * Configures the padding for the target zone in px.  Effectively expands
15115      * (or reduces) the virtual object size for targeting calculations.
15116      * Supports css-style shorthand; if only one parameter is passed, all sides
15117      * will have that padding, and if only two are passed, the top and bottom
15118      * will have the first param, the left and right the second.
15119      * @method setPadding
15120      * @param {int} iTop    Top pad
15121      * @param {int} iRight  Right pad
15122      * @param {int} iBot    Bot pad
15123      * @param {int} iLeft   Left pad
15124      */
15125     setPadding: function(iTop, iRight, iBot, iLeft) {
15126         // this.padding = [iLeft, iRight, iTop, iBot];
15127         if (!iRight && 0 !== iRight) {
15128             this.padding = [iTop, iTop, iTop, iTop];
15129         } else if (!iBot && 0 !== iBot) {
15130             this.padding = [iTop, iRight, iTop, iRight];
15131         } else {
15132             this.padding = [iTop, iRight, iBot, iLeft];
15133         }
15134     },
15135
15136     /**
15137      * Stores the initial placement of the linked element.
15138      * @method setInitialPosition
15139      * @param {int} diffX   the X offset, default 0
15140      * @param {int} diffY   the Y offset, default 0
15141      */
15142     setInitPosition: function(diffX, diffY) {
15143         var el = this.getEl();
15144
15145         if (!this.DDM.verifyEl(el)) {
15146             return;
15147         }
15148
15149         var dx = diffX || 0;
15150         var dy = diffY || 0;
15151
15152         var p = Dom.getXY( el );
15153
15154         this.initPageX = p[0] - dx;
15155         this.initPageY = p[1] - dy;
15156
15157         this.lastPageX = p[0];
15158         this.lastPageY = p[1];
15159
15160
15161         this.setStartPosition(p);
15162     },
15163
15164     /**
15165      * Sets the start position of the element.  This is set when the obj
15166      * is initialized, the reset when a drag is started.
15167      * @method setStartPosition
15168      * @param pos current position (from previous lookup)
15169      * @private
15170      */
15171     setStartPosition: function(pos) {
15172         var p = pos || Dom.getXY( this.getEl() );
15173         this.deltaSetXY = null;
15174
15175         this.startPageX = p[0];
15176         this.startPageY = p[1];
15177     },
15178
15179     /**
15180      * Add this instance to a group of related drag/drop objects.  All
15181      * instances belong to at least one group, and can belong to as many
15182      * groups as needed.
15183      * @method addToGroup
15184      * @param sGroup {string} the name of the group
15185      */
15186     addToGroup: function(sGroup) {
15187         this.groups[sGroup] = true;
15188         this.DDM.regDragDrop(this, sGroup);
15189     },
15190
15191     /**
15192      * Remove's this instance from the supplied interaction group
15193      * @method removeFromGroup
15194      * @param {string}  sGroup  The group to drop
15195      */
15196     removeFromGroup: function(sGroup) {
15197         if (this.groups[sGroup]) {
15198             delete this.groups[sGroup];
15199         }
15200
15201         this.DDM.removeDDFromGroup(this, sGroup);
15202     },
15203
15204     /**
15205      * Allows you to specify that an element other than the linked element
15206      * will be moved with the cursor during a drag
15207      * @method setDragElId
15208      * @param id {string} the id of the element that will be used to initiate the drag
15209      */
15210     setDragElId: function(id) {
15211         this.dragElId = id;
15212     },
15213
15214     /**
15215      * Allows you to specify a child of the linked element that should be
15216      * used to initiate the drag operation.  An example of this would be if
15217      * you have a content div with text and links.  Clicking anywhere in the
15218      * content area would normally start the drag operation.  Use this method
15219      * to specify that an element inside of the content div is the element
15220      * that starts the drag operation.
15221      * @method setHandleElId
15222      * @param id {string} the id of the element that will be used to
15223      * initiate the drag.
15224      */
15225     setHandleElId: function(id) {
15226         if (typeof id !== "string") {
15227             id = Roo.id(id);
15228         }
15229         this.handleElId = id;
15230         this.DDM.regHandle(this.id, id);
15231     },
15232
15233     /**
15234      * Allows you to set an element outside of the linked element as a drag
15235      * handle
15236      * @method setOuterHandleElId
15237      * @param id the id of the element that will be used to initiate the drag
15238      */
15239     setOuterHandleElId: function(id) {
15240         if (typeof id !== "string") {
15241             id = Roo.id(id);
15242         }
15243         Event.on(id, "mousedown",
15244                 this.handleMouseDown, this);
15245         this.setHandleElId(id);
15246
15247         this.hasOuterHandles = true;
15248     },
15249
15250     /**
15251      * Remove all drag and drop hooks for this element
15252      * @method unreg
15253      */
15254     unreg: function() {
15255         Event.un(this.id, "mousedown",
15256                 this.handleMouseDown);
15257         this._domRef = null;
15258         this.DDM._remove(this);
15259     },
15260
15261     destroy : function(){
15262         this.unreg();
15263     },
15264
15265     /**
15266      * Returns true if this instance is locked, or the drag drop mgr is locked
15267      * (meaning that all drag/drop is disabled on the page.)
15268      * @method isLocked
15269      * @return {boolean} true if this obj or all drag/drop is locked, else
15270      * false
15271      */
15272     isLocked: function() {
15273         return (this.DDM.isLocked() || this.locked);
15274     },
15275
15276     /**
15277      * Fired when this object is clicked
15278      * @method handleMouseDown
15279      * @param {Event} e
15280      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15281      * @private
15282      */
15283     handleMouseDown: function(e, oDD){
15284         if (this.primaryButtonOnly && e.button != 0) {
15285             return;
15286         }
15287
15288         if (this.isLocked()) {
15289             return;
15290         }
15291
15292         this.DDM.refreshCache(this.groups);
15293
15294         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15295         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15296         } else {
15297             if (this.clickValidator(e)) {
15298
15299                 // set the initial element position
15300                 this.setStartPosition();
15301
15302
15303                 this.b4MouseDown(e);
15304                 this.onMouseDown(e);
15305
15306                 this.DDM.handleMouseDown(e, this);
15307
15308                 this.DDM.stopEvent(e);
15309             } else {
15310
15311
15312             }
15313         }
15314     },
15315
15316     clickValidator: function(e) {
15317         var target = e.getTarget();
15318         return ( this.isValidHandleChild(target) &&
15319                     (this.id == this.handleElId ||
15320                         this.DDM.handleWasClicked(target, this.id)) );
15321     },
15322
15323     /**
15324      * Allows you to specify a tag name that should not start a drag operation
15325      * when clicked.  This is designed to facilitate embedding links within a
15326      * drag handle that do something other than start the drag.
15327      * @method addInvalidHandleType
15328      * @param {string} tagName the type of element to exclude
15329      */
15330     addInvalidHandleType: function(tagName) {
15331         var type = tagName.toUpperCase();
15332         this.invalidHandleTypes[type] = type;
15333     },
15334
15335     /**
15336      * Lets you to specify an element id for a child of a drag handle
15337      * that should not initiate a drag
15338      * @method addInvalidHandleId
15339      * @param {string} id the element id of the element you wish to ignore
15340      */
15341     addInvalidHandleId: function(id) {
15342         if (typeof id !== "string") {
15343             id = Roo.id(id);
15344         }
15345         this.invalidHandleIds[id] = id;
15346     },
15347
15348     /**
15349      * Lets you specify a css class of elements that will not initiate a drag
15350      * @method addInvalidHandleClass
15351      * @param {string} cssClass the class of the elements you wish to ignore
15352      */
15353     addInvalidHandleClass: function(cssClass) {
15354         this.invalidHandleClasses.push(cssClass);
15355     },
15356
15357     /**
15358      * Unsets an excluded tag name set by addInvalidHandleType
15359      * @method removeInvalidHandleType
15360      * @param {string} tagName the type of element to unexclude
15361      */
15362     removeInvalidHandleType: function(tagName) {
15363         var type = tagName.toUpperCase();
15364         // this.invalidHandleTypes[type] = null;
15365         delete this.invalidHandleTypes[type];
15366     },
15367
15368     /**
15369      * Unsets an invalid handle id
15370      * @method removeInvalidHandleId
15371      * @param {string} id the id of the element to re-enable
15372      */
15373     removeInvalidHandleId: function(id) {
15374         if (typeof id !== "string") {
15375             id = Roo.id(id);
15376         }
15377         delete this.invalidHandleIds[id];
15378     },
15379
15380     /**
15381      * Unsets an invalid css class
15382      * @method removeInvalidHandleClass
15383      * @param {string} cssClass the class of the element(s) you wish to
15384      * re-enable
15385      */
15386     removeInvalidHandleClass: function(cssClass) {
15387         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15388             if (this.invalidHandleClasses[i] == cssClass) {
15389                 delete this.invalidHandleClasses[i];
15390             }
15391         }
15392     },
15393
15394     /**
15395      * Checks the tag exclusion list to see if this click should be ignored
15396      * @method isValidHandleChild
15397      * @param {HTMLElement} node the HTMLElement to evaluate
15398      * @return {boolean} true if this is a valid tag type, false if not
15399      */
15400     isValidHandleChild: function(node) {
15401
15402         var valid = true;
15403         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15404         var nodeName;
15405         try {
15406             nodeName = node.nodeName.toUpperCase();
15407         } catch(e) {
15408             nodeName = node.nodeName;
15409         }
15410         valid = valid && !this.invalidHandleTypes[nodeName];
15411         valid = valid && !this.invalidHandleIds[node.id];
15412
15413         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15414             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15415         }
15416
15417
15418         return valid;
15419
15420     },
15421
15422     /**
15423      * Create the array of horizontal tick marks if an interval was specified
15424      * in setXConstraint().
15425      * @method setXTicks
15426      * @private
15427      */
15428     setXTicks: function(iStartX, iTickSize) {
15429         this.xTicks = [];
15430         this.xTickSize = iTickSize;
15431
15432         var tickMap = {};
15433
15434         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15435             if (!tickMap[i]) {
15436                 this.xTicks[this.xTicks.length] = i;
15437                 tickMap[i] = true;
15438             }
15439         }
15440
15441         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15442             if (!tickMap[i]) {
15443                 this.xTicks[this.xTicks.length] = i;
15444                 tickMap[i] = true;
15445             }
15446         }
15447
15448         this.xTicks.sort(this.DDM.numericSort) ;
15449     },
15450
15451     /**
15452      * Create the array of vertical tick marks if an interval was specified in
15453      * setYConstraint().
15454      * @method setYTicks
15455      * @private
15456      */
15457     setYTicks: function(iStartY, iTickSize) {
15458         this.yTicks = [];
15459         this.yTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.yTicks[this.yTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.yTicks[this.yTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.yTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * By default, the element can be dragged any place on the screen.  Use
15482      * this method to limit the horizontal travel of the element.  Pass in
15483      * 0,0 for the parameters if you want to lock the drag to the y axis.
15484      * @method setXConstraint
15485      * @param {int} iLeft the number of pixels the element can move to the left
15486      * @param {int} iRight the number of pixels the element can move to the
15487      * right
15488      * @param {int} iTickSize optional parameter for specifying that the
15489      * element
15490      * should move iTickSize pixels at a time.
15491      */
15492     setXConstraint: function(iLeft, iRight, iTickSize) {
15493         this.leftConstraint = iLeft;
15494         this.rightConstraint = iRight;
15495
15496         this.minX = this.initPageX - iLeft;
15497         this.maxX = this.initPageX + iRight;
15498         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15499
15500         this.constrainX = true;
15501     },
15502
15503     /**
15504      * Clears any constraints applied to this instance.  Also clears ticks
15505      * since they can't exist independent of a constraint at this time.
15506      * @method clearConstraints
15507      */
15508     clearConstraints: function() {
15509         this.constrainX = false;
15510         this.constrainY = false;
15511         this.clearTicks();
15512     },
15513
15514     /**
15515      * Clears any tick interval defined for this instance
15516      * @method clearTicks
15517      */
15518     clearTicks: function() {
15519         this.xTicks = null;
15520         this.yTicks = null;
15521         this.xTickSize = 0;
15522         this.yTickSize = 0;
15523     },
15524
15525     /**
15526      * By default, the element can be dragged any place on the screen.  Set
15527      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15528      * parameters if you want to lock the drag to the x axis.
15529      * @method setYConstraint
15530      * @param {int} iUp the number of pixels the element can move up
15531      * @param {int} iDown the number of pixels the element can move down
15532      * @param {int} iTickSize optional parameter for specifying that the
15533      * element should move iTickSize pixels at a time.
15534      */
15535     setYConstraint: function(iUp, iDown, iTickSize) {
15536         this.topConstraint = iUp;
15537         this.bottomConstraint = iDown;
15538
15539         this.minY = this.initPageY - iUp;
15540         this.maxY = this.initPageY + iDown;
15541         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15542
15543         this.constrainY = true;
15544
15545     },
15546
15547     /**
15548      * resetConstraints must be called if you manually reposition a dd element.
15549      * @method resetConstraints
15550      * @param {boolean} maintainOffset
15551      */
15552     resetConstraints: function() {
15553
15554
15555         // Maintain offsets if necessary
15556         if (this.initPageX || this.initPageX === 0) {
15557             // figure out how much this thing has moved
15558             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15559             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15560
15561             this.setInitPosition(dx, dy);
15562
15563         // This is the first time we have detected the element's position
15564         } else {
15565             this.setInitPosition();
15566         }
15567
15568         if (this.constrainX) {
15569             this.setXConstraint( this.leftConstraint,
15570                                  this.rightConstraint,
15571                                  this.xTickSize        );
15572         }
15573
15574         if (this.constrainY) {
15575             this.setYConstraint( this.topConstraint,
15576                                  this.bottomConstraint,
15577                                  this.yTickSize         );
15578         }
15579     },
15580
15581     /**
15582      * Normally the drag element is moved pixel by pixel, but we can specify
15583      * that it move a number of pixels at a time.  This method resolves the
15584      * location when we have it set up like this.
15585      * @method getTick
15586      * @param {int} val where we want to place the object
15587      * @param {int[]} tickArray sorted array of valid points
15588      * @return {int} the closest tick
15589      * @private
15590      */
15591     getTick: function(val, tickArray) {
15592
15593         if (!tickArray) {
15594             // If tick interval is not defined, it is effectively 1 pixel,
15595             // so we return the value passed to us.
15596             return val;
15597         } else if (tickArray[0] >= val) {
15598             // The value is lower than the first tick, so we return the first
15599             // tick.
15600             return tickArray[0];
15601         } else {
15602             for (var i=0, len=tickArray.length; i<len; ++i) {
15603                 var next = i + 1;
15604                 if (tickArray[next] && tickArray[next] >= val) {
15605                     var diff1 = val - tickArray[i];
15606                     var diff2 = tickArray[next] - val;
15607                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15608                 }
15609             }
15610
15611             // The value is larger than the last tick, so we return the last
15612             // tick.
15613             return tickArray[tickArray.length - 1];
15614         }
15615     },
15616
15617     /**
15618      * toString method
15619      * @method toString
15620      * @return {string} string representation of the dd obj
15621      */
15622     toString: function() {
15623         return ("DragDrop " + this.id);
15624     }
15625
15626 });
15627
15628 })();
15629 /*
15630  * Based on:
15631  * Ext JS Library 1.1.1
15632  * Copyright(c) 2006-2007, Ext JS, LLC.
15633  *
15634  * Originally Released Under LGPL - original licence link has changed is not relivant.
15635  *
15636  * Fork - LGPL
15637  * <script type="text/javascript">
15638  */
15639
15640
15641 /**
15642  * The drag and drop utility provides a framework for building drag and drop
15643  * applications.  In addition to enabling drag and drop for specific elements,
15644  * the drag and drop elements are tracked by the manager class, and the
15645  * interactions between the various elements are tracked during the drag and
15646  * the implementing code is notified about these important moments.
15647  */
15648
15649 // Only load the library once.  Rewriting the manager class would orphan
15650 // existing drag and drop instances.
15651 if (!Roo.dd.DragDropMgr) {
15652
15653 /**
15654  * @class Roo.dd.DragDropMgr
15655  * DragDropMgr is a singleton that tracks the element interaction for
15656  * all DragDrop items in the window.  Generally, you will not call
15657  * this class directly, but it does have helper methods that could
15658  * be useful in your DragDrop implementations.
15659  * @singleton
15660  */
15661 Roo.dd.DragDropMgr = function() {
15662
15663     var Event = Roo.EventManager;
15664
15665     return {
15666
15667         /**
15668          * Two dimensional Array of registered DragDrop objects.  The first
15669          * dimension is the DragDrop item group, the second the DragDrop
15670          * object.
15671          * @property ids
15672          * @type {string: string}
15673          * @private
15674          * @static
15675          */
15676         ids: {},
15677
15678         /**
15679          * Array of element ids defined as drag handles.  Used to determine
15680          * if the element that generated the mousedown event is actually the
15681          * handle and not the html element itself.
15682          * @property handleIds
15683          * @type {string: string}
15684          * @private
15685          * @static
15686          */
15687         handleIds: {},
15688
15689         /**
15690          * the DragDrop object that is currently being dragged
15691          * @property dragCurrent
15692          * @type DragDrop
15693          * @private
15694          * @static
15695          **/
15696         dragCurrent: null,
15697
15698         /**
15699          * the DragDrop object(s) that are being hovered over
15700          * @property dragOvers
15701          * @type Array
15702          * @private
15703          * @static
15704          */
15705         dragOvers: {},
15706
15707         /**
15708          * the X distance between the cursor and the object being dragged
15709          * @property deltaX
15710          * @type int
15711          * @private
15712          * @static
15713          */
15714         deltaX: 0,
15715
15716         /**
15717          * the Y distance between the cursor and the object being dragged
15718          * @property deltaY
15719          * @type int
15720          * @private
15721          * @static
15722          */
15723         deltaY: 0,
15724
15725         /**
15726          * Flag to determine if we should prevent the default behavior of the
15727          * events we define. By default this is true, but this can be set to
15728          * false if you need the default behavior (not recommended)
15729          * @property preventDefault
15730          * @type boolean
15731          * @static
15732          */
15733         preventDefault: true,
15734
15735         /**
15736          * Flag to determine if we should stop the propagation of the events
15737          * we generate. This is true by default but you may want to set it to
15738          * false if the html element contains other features that require the
15739          * mouse click.
15740          * @property stopPropagation
15741          * @type boolean
15742          * @static
15743          */
15744         stopPropagation: true,
15745
15746         /**
15747          * Internal flag that is set to true when drag and drop has been
15748          * intialized
15749          * @property initialized
15750          * @private
15751          * @static
15752          */
15753         initalized: false,
15754
15755         /**
15756          * All drag and drop can be disabled.
15757          * @property locked
15758          * @private
15759          * @static
15760          */
15761         locked: false,
15762
15763         /**
15764          * Called the first time an element is registered.
15765          * @method init
15766          * @private
15767          * @static
15768          */
15769         init: function() {
15770             this.initialized = true;
15771         },
15772
15773         /**
15774          * In point mode, drag and drop interaction is defined by the
15775          * location of the cursor during the drag/drop
15776          * @property POINT
15777          * @type int
15778          * @static
15779          */
15780         POINT: 0,
15781
15782         /**
15783          * In intersect mode, drag and drop interactio nis defined by the
15784          * overlap of two or more drag and drop objects.
15785          * @property INTERSECT
15786          * @type int
15787          * @static
15788          */
15789         INTERSECT: 1,
15790
15791         /**
15792          * The current drag and drop mode.  Default: POINT
15793          * @property mode
15794          * @type int
15795          * @static
15796          */
15797         mode: 0,
15798
15799         /**
15800          * Runs method on all drag and drop objects
15801          * @method _execOnAll
15802          * @private
15803          * @static
15804          */
15805         _execOnAll: function(sMethod, args) {
15806             for (var i in this.ids) {
15807                 for (var j in this.ids[i]) {
15808                     var oDD = this.ids[i][j];
15809                     if (! this.isTypeOfDD(oDD)) {
15810                         continue;
15811                     }
15812                     oDD[sMethod].apply(oDD, args);
15813                 }
15814             }
15815         },
15816
15817         /**
15818          * Drag and drop initialization.  Sets up the global event handlers
15819          * @method _onLoad
15820          * @private
15821          * @static
15822          */
15823         _onLoad: function() {
15824
15825             this.init();
15826
15827
15828             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15829             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15830             Event.on(window,   "unload",    this._onUnload, this, true);
15831             Event.on(window,   "resize",    this._onResize, this, true);
15832             // Event.on(window,   "mouseout",    this._test);
15833
15834         },
15835
15836         /**
15837          * Reset constraints on all drag and drop objs
15838          * @method _onResize
15839          * @private
15840          * @static
15841          */
15842         _onResize: function(e) {
15843             this._execOnAll("resetConstraints", []);
15844         },
15845
15846         /**
15847          * Lock all drag and drop functionality
15848          * @method lock
15849          * @static
15850          */
15851         lock: function() { this.locked = true; },
15852
15853         /**
15854          * Unlock all drag and drop functionality
15855          * @method unlock
15856          * @static
15857          */
15858         unlock: function() { this.locked = false; },
15859
15860         /**
15861          * Is drag and drop locked?
15862          * @method isLocked
15863          * @return {boolean} True if drag and drop is locked, false otherwise.
15864          * @static
15865          */
15866         isLocked: function() { return this.locked; },
15867
15868         /**
15869          * Location cache that is set for all drag drop objects when a drag is
15870          * initiated, cleared when the drag is finished.
15871          * @property locationCache
15872          * @private
15873          * @static
15874          */
15875         locationCache: {},
15876
15877         /**
15878          * Set useCache to false if you want to force object the lookup of each
15879          * drag and drop linked element constantly during a drag.
15880          * @property useCache
15881          * @type boolean
15882          * @static
15883          */
15884         useCache: true,
15885
15886         /**
15887          * The number of pixels that the mouse needs to move after the
15888          * mousedown before the drag is initiated.  Default=3;
15889          * @property clickPixelThresh
15890          * @type int
15891          * @static
15892          */
15893         clickPixelThresh: 3,
15894
15895         /**
15896          * The number of milliseconds after the mousedown event to initiate the
15897          * drag if we don't get a mouseup event. Default=1000
15898          * @property clickTimeThresh
15899          * @type int
15900          * @static
15901          */
15902         clickTimeThresh: 350,
15903
15904         /**
15905          * Flag that indicates that either the drag pixel threshold or the
15906          * mousdown time threshold has been met
15907          * @property dragThreshMet
15908          * @type boolean
15909          * @private
15910          * @static
15911          */
15912         dragThreshMet: false,
15913
15914         /**
15915          * Timeout used for the click time threshold
15916          * @property clickTimeout
15917          * @type Object
15918          * @private
15919          * @static
15920          */
15921         clickTimeout: null,
15922
15923         /**
15924          * The X position of the mousedown event stored for later use when a
15925          * drag threshold is met.
15926          * @property startX
15927          * @type int
15928          * @private
15929          * @static
15930          */
15931         startX: 0,
15932
15933         /**
15934          * The Y position of the mousedown event stored for later use when a
15935          * drag threshold is met.
15936          * @property startY
15937          * @type int
15938          * @private
15939          * @static
15940          */
15941         startY: 0,
15942
15943         /**
15944          * Each DragDrop instance must be registered with the DragDropMgr.
15945          * This is executed in DragDrop.init()
15946          * @method regDragDrop
15947          * @param {DragDrop} oDD the DragDrop object to register
15948          * @param {String} sGroup the name of the group this element belongs to
15949          * @static
15950          */
15951         regDragDrop: function(oDD, sGroup) {
15952             if (!this.initialized) { this.init(); }
15953
15954             if (!this.ids[sGroup]) {
15955                 this.ids[sGroup] = {};
15956             }
15957             this.ids[sGroup][oDD.id] = oDD;
15958         },
15959
15960         /**
15961          * Removes the supplied dd instance from the supplied group. Executed
15962          * by DragDrop.removeFromGroup, so don't call this function directly.
15963          * @method removeDDFromGroup
15964          * @private
15965          * @static
15966          */
15967         removeDDFromGroup: function(oDD, sGroup) {
15968             if (!this.ids[sGroup]) {
15969                 this.ids[sGroup] = {};
15970             }
15971
15972             var obj = this.ids[sGroup];
15973             if (obj && obj[oDD.id]) {
15974                 delete obj[oDD.id];
15975             }
15976         },
15977
15978         /**
15979          * Unregisters a drag and drop item.  This is executed in
15980          * DragDrop.unreg, use that method instead of calling this directly.
15981          * @method _remove
15982          * @private
15983          * @static
15984          */
15985         _remove: function(oDD) {
15986             for (var g in oDD.groups) {
15987                 if (g && this.ids[g][oDD.id]) {
15988                     delete this.ids[g][oDD.id];
15989                 }
15990             }
15991             delete this.handleIds[oDD.id];
15992         },
15993
15994         /**
15995          * Each DragDrop handle element must be registered.  This is done
15996          * automatically when executing DragDrop.setHandleElId()
15997          * @method regHandle
15998          * @param {String} sDDId the DragDrop id this element is a handle for
15999          * @param {String} sHandleId the id of the element that is the drag
16000          * handle
16001          * @static
16002          */
16003         regHandle: function(sDDId, sHandleId) {
16004             if (!this.handleIds[sDDId]) {
16005                 this.handleIds[sDDId] = {};
16006             }
16007             this.handleIds[sDDId][sHandleId] = sHandleId;
16008         },
16009
16010         /**
16011          * Utility function to determine if a given element has been
16012          * registered as a drag drop item.
16013          * @method isDragDrop
16014          * @param {String} id the element id to check
16015          * @return {boolean} true if this element is a DragDrop item,
16016          * false otherwise
16017          * @static
16018          */
16019         isDragDrop: function(id) {
16020             return ( this.getDDById(id) ) ? true : false;
16021         },
16022
16023         /**
16024          * Returns the drag and drop instances that are in all groups the
16025          * passed in instance belongs to.
16026          * @method getRelated
16027          * @param {DragDrop} p_oDD the obj to get related data for
16028          * @param {boolean} bTargetsOnly if true, only return targetable objs
16029          * @return {DragDrop[]} the related instances
16030          * @static
16031          */
16032         getRelated: function(p_oDD, bTargetsOnly) {
16033             var oDDs = [];
16034             for (var i in p_oDD.groups) {
16035                 for (j in this.ids[i]) {
16036                     var dd = this.ids[i][j];
16037                     if (! this.isTypeOfDD(dd)) {
16038                         continue;
16039                     }
16040                     if (!bTargetsOnly || dd.isTarget) {
16041                         oDDs[oDDs.length] = dd;
16042                     }
16043                 }
16044             }
16045
16046             return oDDs;
16047         },
16048
16049         /**
16050          * Returns true if the specified dd target is a legal target for
16051          * the specifice drag obj
16052          * @method isLegalTarget
16053          * @param {DragDrop} the drag obj
16054          * @param {DragDrop} the target
16055          * @return {boolean} true if the target is a legal target for the
16056          * dd obj
16057          * @static
16058          */
16059         isLegalTarget: function (oDD, oTargetDD) {
16060             var targets = this.getRelated(oDD, true);
16061             for (var i=0, len=targets.length;i<len;++i) {
16062                 if (targets[i].id == oTargetDD.id) {
16063                     return true;
16064                 }
16065             }
16066
16067             return false;
16068         },
16069
16070         /**
16071          * My goal is to be able to transparently determine if an object is
16072          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16073          * returns "object", oDD.constructor.toString() always returns
16074          * "DragDrop" and not the name of the subclass.  So for now it just
16075          * evaluates a well-known variable in DragDrop.
16076          * @method isTypeOfDD
16077          * @param {Object} the object to evaluate
16078          * @return {boolean} true if typeof oDD = DragDrop
16079          * @static
16080          */
16081         isTypeOfDD: function (oDD) {
16082             return (oDD && oDD.__ygDragDrop);
16083         },
16084
16085         /**
16086          * Utility function to determine if a given element has been
16087          * registered as a drag drop handle for the given Drag Drop object.
16088          * @method isHandle
16089          * @param {String} id the element id to check
16090          * @return {boolean} true if this element is a DragDrop handle, false
16091          * otherwise
16092          * @static
16093          */
16094         isHandle: function(sDDId, sHandleId) {
16095             return ( this.handleIds[sDDId] &&
16096                             this.handleIds[sDDId][sHandleId] );
16097         },
16098
16099         /**
16100          * Returns the DragDrop instance for a given id
16101          * @method getDDById
16102          * @param {String} id the id of the DragDrop object
16103          * @return {DragDrop} the drag drop object, null if it is not found
16104          * @static
16105          */
16106         getDDById: function(id) {
16107             for (var i in this.ids) {
16108                 if (this.ids[i][id]) {
16109                     return this.ids[i][id];
16110                 }
16111             }
16112             return null;
16113         },
16114
16115         /**
16116          * Fired after a registered DragDrop object gets the mousedown event.
16117          * Sets up the events required to track the object being dragged
16118          * @method handleMouseDown
16119          * @param {Event} e the event
16120          * @param oDD the DragDrop object being dragged
16121          * @private
16122          * @static
16123          */
16124         handleMouseDown: function(e, oDD) {
16125             if(Roo.QuickTips){
16126                 Roo.QuickTips.disable();
16127             }
16128             this.currentTarget = e.getTarget();
16129
16130             this.dragCurrent = oDD;
16131
16132             var el = oDD.getEl();
16133
16134             // track start position
16135             this.startX = e.getPageX();
16136             this.startY = e.getPageY();
16137
16138             this.deltaX = this.startX - el.offsetLeft;
16139             this.deltaY = this.startY - el.offsetTop;
16140
16141             this.dragThreshMet = false;
16142
16143             this.clickTimeout = setTimeout(
16144                     function() {
16145                         var DDM = Roo.dd.DDM;
16146                         DDM.startDrag(DDM.startX, DDM.startY);
16147                     },
16148                     this.clickTimeThresh );
16149         },
16150
16151         /**
16152          * Fired when either the drag pixel threshol or the mousedown hold
16153          * time threshold has been met.
16154          * @method startDrag
16155          * @param x {int} the X position of the original mousedown
16156          * @param y {int} the Y position of the original mousedown
16157          * @static
16158          */
16159         startDrag: function(x, y) {
16160             clearTimeout(this.clickTimeout);
16161             if (this.dragCurrent) {
16162                 this.dragCurrent.b4StartDrag(x, y);
16163                 this.dragCurrent.startDrag(x, y);
16164             }
16165             this.dragThreshMet = true;
16166         },
16167
16168         /**
16169          * Internal function to handle the mouseup event.  Will be invoked
16170          * from the context of the document.
16171          * @method handleMouseUp
16172          * @param {Event} e the event
16173          * @private
16174          * @static
16175          */
16176         handleMouseUp: function(e) {
16177
16178             if(Roo.QuickTips){
16179                 Roo.QuickTips.enable();
16180             }
16181             if (! this.dragCurrent) {
16182                 return;
16183             }
16184
16185             clearTimeout(this.clickTimeout);
16186
16187             if (this.dragThreshMet) {
16188                 this.fireEvents(e, true);
16189             } else {
16190             }
16191
16192             this.stopDrag(e);
16193
16194             this.stopEvent(e);
16195         },
16196
16197         /**
16198          * Utility to stop event propagation and event default, if these
16199          * features are turned on.
16200          * @method stopEvent
16201          * @param {Event} e the event as returned by this.getEvent()
16202          * @static
16203          */
16204         stopEvent: function(e){
16205             if(this.stopPropagation) {
16206                 e.stopPropagation();
16207             }
16208
16209             if (this.preventDefault) {
16210                 e.preventDefault();
16211             }
16212         },
16213
16214         /**
16215          * Internal function to clean up event handlers after the drag
16216          * operation is complete
16217          * @method stopDrag
16218          * @param {Event} e the event
16219          * @private
16220          * @static
16221          */
16222         stopDrag: function(e) {
16223             // Fire the drag end event for the item that was dragged
16224             if (this.dragCurrent) {
16225                 if (this.dragThreshMet) {
16226                     this.dragCurrent.b4EndDrag(e);
16227                     this.dragCurrent.endDrag(e);
16228                 }
16229
16230                 this.dragCurrent.onMouseUp(e);
16231             }
16232
16233             this.dragCurrent = null;
16234             this.dragOvers = {};
16235         },
16236
16237         /**
16238          * Internal function to handle the mousemove event.  Will be invoked
16239          * from the context of the html element.
16240          *
16241          * @TODO figure out what we can do about mouse events lost when the
16242          * user drags objects beyond the window boundary.  Currently we can
16243          * detect this in internet explorer by verifying that the mouse is
16244          * down during the mousemove event.  Firefox doesn't give us the
16245          * button state on the mousemove event.
16246          * @method handleMouseMove
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         handleMouseMove: function(e) {
16252             if (! this.dragCurrent) {
16253                 return true;
16254             }
16255
16256             // var button = e.which || e.button;
16257
16258             // check for IE mouseup outside of page boundary
16259             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16260                 this.stopEvent(e);
16261                 return this.handleMouseUp(e);
16262             }
16263
16264             if (!this.dragThreshMet) {
16265                 var diffX = Math.abs(this.startX - e.getPageX());
16266                 var diffY = Math.abs(this.startY - e.getPageY());
16267                 if (diffX > this.clickPixelThresh ||
16268                             diffY > this.clickPixelThresh) {
16269                     this.startDrag(this.startX, this.startY);
16270                 }
16271             }
16272
16273             if (this.dragThreshMet) {
16274                 this.dragCurrent.b4Drag(e);
16275                 this.dragCurrent.onDrag(e);
16276                 if(!this.dragCurrent.moveOnly){
16277                     this.fireEvents(e, false);
16278                 }
16279             }
16280
16281             this.stopEvent(e);
16282
16283             return true;
16284         },
16285
16286         /**
16287          * Iterates over all of the DragDrop elements to find ones we are
16288          * hovering over or dropping on
16289          * @method fireEvents
16290          * @param {Event} e the event
16291          * @param {boolean} isDrop is this a drop op or a mouseover op?
16292          * @private
16293          * @static
16294          */
16295         fireEvents: function(e, isDrop) {
16296             var dc = this.dragCurrent;
16297
16298             // If the user did the mouse up outside of the window, we could
16299             // get here even though we have ended the drag.
16300             if (!dc || dc.isLocked()) {
16301                 return;
16302             }
16303
16304             var pt = e.getPoint();
16305
16306             // cache the previous dragOver array
16307             var oldOvers = [];
16308
16309             var outEvts   = [];
16310             var overEvts  = [];
16311             var dropEvts  = [];
16312             var enterEvts = [];
16313
16314             // Check to see if the object(s) we were hovering over is no longer
16315             // being hovered over so we can fire the onDragOut event
16316             for (var i in this.dragOvers) {
16317
16318                 var ddo = this.dragOvers[i];
16319
16320                 if (! this.isTypeOfDD(ddo)) {
16321                     continue;
16322                 }
16323
16324                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16325                     outEvts.push( ddo );
16326                 }
16327
16328                 oldOvers[i] = true;
16329                 delete this.dragOvers[i];
16330             }
16331
16332             for (var sGroup in dc.groups) {
16333
16334                 if ("string" != typeof sGroup) {
16335                     continue;
16336                 }
16337
16338                 for (i in this.ids[sGroup]) {
16339                     var oDD = this.ids[sGroup][i];
16340                     if (! this.isTypeOfDD(oDD)) {
16341                         continue;
16342                     }
16343
16344                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16345                         if (this.isOverTarget(pt, oDD, this.mode)) {
16346                             // look for drop interactions
16347                             if (isDrop) {
16348                                 dropEvts.push( oDD );
16349                             // look for drag enter and drag over interactions
16350                             } else {
16351
16352                                 // initial drag over: dragEnter fires
16353                                 if (!oldOvers[oDD.id]) {
16354                                     enterEvts.push( oDD );
16355                                 // subsequent drag overs: dragOver fires
16356                                 } else {
16357                                     overEvts.push( oDD );
16358                                 }
16359
16360                                 this.dragOvers[oDD.id] = oDD;
16361                             }
16362                         }
16363                     }
16364                 }
16365             }
16366
16367             if (this.mode) {
16368                 if (outEvts.length) {
16369                     dc.b4DragOut(e, outEvts);
16370                     dc.onDragOut(e, outEvts);
16371                 }
16372
16373                 if (enterEvts.length) {
16374                     dc.onDragEnter(e, enterEvts);
16375                 }
16376
16377                 if (overEvts.length) {
16378                     dc.b4DragOver(e, overEvts);
16379                     dc.onDragOver(e, overEvts);
16380                 }
16381
16382                 if (dropEvts.length) {
16383                     dc.b4DragDrop(e, dropEvts);
16384                     dc.onDragDrop(e, dropEvts);
16385                 }
16386
16387             } else {
16388                 // fire dragout events
16389                 var len = 0;
16390                 for (i=0, len=outEvts.length; i<len; ++i) {
16391                     dc.b4DragOut(e, outEvts[i].id);
16392                     dc.onDragOut(e, outEvts[i].id);
16393                 }
16394
16395                 // fire enter events
16396                 for (i=0,len=enterEvts.length; i<len; ++i) {
16397                     // dc.b4DragEnter(e, oDD.id);
16398                     dc.onDragEnter(e, enterEvts[i].id);
16399                 }
16400
16401                 // fire over events
16402                 for (i=0,len=overEvts.length; i<len; ++i) {
16403                     dc.b4DragOver(e, overEvts[i].id);
16404                     dc.onDragOver(e, overEvts[i].id);
16405                 }
16406
16407                 // fire drop events
16408                 for (i=0, len=dropEvts.length; i<len; ++i) {
16409                     dc.b4DragDrop(e, dropEvts[i].id);
16410                     dc.onDragDrop(e, dropEvts[i].id);
16411                 }
16412
16413             }
16414
16415             // notify about a drop that did not find a target
16416             if (isDrop && !dropEvts.length) {
16417                 dc.onInvalidDrop(e);
16418             }
16419
16420         },
16421
16422         /**
16423          * Helper function for getting the best match from the list of drag
16424          * and drop objects returned by the drag and drop events when we are
16425          * in INTERSECT mode.  It returns either the first object that the
16426          * cursor is over, or the object that has the greatest overlap with
16427          * the dragged element.
16428          * @method getBestMatch
16429          * @param  {DragDrop[]} dds The array of drag and drop objects
16430          * targeted
16431          * @return {DragDrop}       The best single match
16432          * @static
16433          */
16434         getBestMatch: function(dds) {
16435             var winner = null;
16436             // Return null if the input is not what we expect
16437             //if (!dds || !dds.length || dds.length == 0) {
16438                // winner = null;
16439             // If there is only one item, it wins
16440             //} else if (dds.length == 1) {
16441
16442             var len = dds.length;
16443
16444             if (len == 1) {
16445                 winner = dds[0];
16446             } else {
16447                 // Loop through the targeted items
16448                 for (var i=0; i<len; ++i) {
16449                     var dd = dds[i];
16450                     // If the cursor is over the object, it wins.  If the
16451                     // cursor is over multiple matches, the first one we come
16452                     // to wins.
16453                     if (dd.cursorIsOver) {
16454                         winner = dd;
16455                         break;
16456                     // Otherwise the object with the most overlap wins
16457                     } else {
16458                         if (!winner ||
16459                             winner.overlap.getArea() < dd.overlap.getArea()) {
16460                             winner = dd;
16461                         }
16462                     }
16463                 }
16464             }
16465
16466             return winner;
16467         },
16468
16469         /**
16470          * Refreshes the cache of the top-left and bottom-right points of the
16471          * drag and drop objects in the specified group(s).  This is in the
16472          * format that is stored in the drag and drop instance, so typical
16473          * usage is:
16474          * <code>
16475          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16476          * </code>
16477          * Alternatively:
16478          * <code>
16479          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16480          * </code>
16481          * @TODO this really should be an indexed array.  Alternatively this
16482          * method could accept both.
16483          * @method refreshCache
16484          * @param {Object} groups an associative array of groups to refresh
16485          * @static
16486          */
16487         refreshCache: function(groups) {
16488             for (var sGroup in groups) {
16489                 if ("string" != typeof sGroup) {
16490                     continue;
16491                 }
16492                 for (var i in this.ids[sGroup]) {
16493                     var oDD = this.ids[sGroup][i];
16494
16495                     if (this.isTypeOfDD(oDD)) {
16496                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16497                         var loc = this.getLocation(oDD);
16498                         if (loc) {
16499                             this.locationCache[oDD.id] = loc;
16500                         } else {
16501                             delete this.locationCache[oDD.id];
16502                             // this will unregister the drag and drop object if
16503                             // the element is not in a usable state
16504                             // oDD.unreg();
16505                         }
16506                     }
16507                 }
16508             }
16509         },
16510
16511         /**
16512          * This checks to make sure an element exists and is in the DOM.  The
16513          * main purpose is to handle cases where innerHTML is used to remove
16514          * drag and drop objects from the DOM.  IE provides an 'unspecified
16515          * error' when trying to access the offsetParent of such an element
16516          * @method verifyEl
16517          * @param {HTMLElement} el the element to check
16518          * @return {boolean} true if the element looks usable
16519          * @static
16520          */
16521         verifyEl: function(el) {
16522             if (el) {
16523                 var parent;
16524                 if(Roo.isIE){
16525                     try{
16526                         parent = el.offsetParent;
16527                     }catch(e){}
16528                 }else{
16529                     parent = el.offsetParent;
16530                 }
16531                 if (parent) {
16532                     return true;
16533                 }
16534             }
16535
16536             return false;
16537         },
16538
16539         /**
16540          * Returns a Region object containing the drag and drop element's position
16541          * and size, including the padding configured for it
16542          * @method getLocation
16543          * @param {DragDrop} oDD the drag and drop object to get the
16544          *                       location for
16545          * @return {Roo.lib.Region} a Region object representing the total area
16546          *                             the element occupies, including any padding
16547          *                             the instance is configured for.
16548          * @static
16549          */
16550         getLocation: function(oDD) {
16551             if (! this.isTypeOfDD(oDD)) {
16552                 return null;
16553             }
16554
16555             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16556
16557             try {
16558                 pos= Roo.lib.Dom.getXY(el);
16559             } catch (e) { }
16560
16561             if (!pos) {
16562                 return null;
16563             }
16564
16565             x1 = pos[0];
16566             x2 = x1 + el.offsetWidth;
16567             y1 = pos[1];
16568             y2 = y1 + el.offsetHeight;
16569
16570             t = y1 - oDD.padding[0];
16571             r = x2 + oDD.padding[1];
16572             b = y2 + oDD.padding[2];
16573             l = x1 - oDD.padding[3];
16574
16575             return new Roo.lib.Region( t, r, b, l );
16576         },
16577
16578         /**
16579          * Checks the cursor location to see if it over the target
16580          * @method isOverTarget
16581          * @param {Roo.lib.Point} pt The point to evaluate
16582          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16583          * @return {boolean} true if the mouse is over the target
16584          * @private
16585          * @static
16586          */
16587         isOverTarget: function(pt, oTarget, intersect) {
16588             // use cache if available
16589             var loc = this.locationCache[oTarget.id];
16590             if (!loc || !this.useCache) {
16591                 loc = this.getLocation(oTarget);
16592                 this.locationCache[oTarget.id] = loc;
16593
16594             }
16595
16596             if (!loc) {
16597                 return false;
16598             }
16599
16600             oTarget.cursorIsOver = loc.contains( pt );
16601
16602             // DragDrop is using this as a sanity check for the initial mousedown
16603             // in this case we are done.  In POINT mode, if the drag obj has no
16604             // contraints, we are also done. Otherwise we need to evaluate the
16605             // location of the target as related to the actual location of the
16606             // dragged element.
16607             var dc = this.dragCurrent;
16608             if (!dc || !dc.getTargetCoord ||
16609                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16610                 return oTarget.cursorIsOver;
16611             }
16612
16613             oTarget.overlap = null;
16614
16615             // Get the current location of the drag element, this is the
16616             // location of the mouse event less the delta that represents
16617             // where the original mousedown happened on the element.  We
16618             // need to consider constraints and ticks as well.
16619             var pos = dc.getTargetCoord(pt.x, pt.y);
16620
16621             var el = dc.getDragEl();
16622             var curRegion = new Roo.lib.Region( pos.y,
16623                                                    pos.x + el.offsetWidth,
16624                                                    pos.y + el.offsetHeight,
16625                                                    pos.x );
16626
16627             var overlap = curRegion.intersect(loc);
16628
16629             if (overlap) {
16630                 oTarget.overlap = overlap;
16631                 return (intersect) ? true : oTarget.cursorIsOver;
16632             } else {
16633                 return false;
16634             }
16635         },
16636
16637         /**
16638          * unload event handler
16639          * @method _onUnload
16640          * @private
16641          * @static
16642          */
16643         _onUnload: function(e, me) {
16644             Roo.dd.DragDropMgr.unregAll();
16645         },
16646
16647         /**
16648          * Cleans up the drag and drop events and objects.
16649          * @method unregAll
16650          * @private
16651          * @static
16652          */
16653         unregAll: function() {
16654
16655             if (this.dragCurrent) {
16656                 this.stopDrag();
16657                 this.dragCurrent = null;
16658             }
16659
16660             this._execOnAll("unreg", []);
16661
16662             for (i in this.elementCache) {
16663                 delete this.elementCache[i];
16664             }
16665
16666             this.elementCache = {};
16667             this.ids = {};
16668         },
16669
16670         /**
16671          * A cache of DOM elements
16672          * @property elementCache
16673          * @private
16674          * @static
16675          */
16676         elementCache: {},
16677
16678         /**
16679          * Get the wrapper for the DOM element specified
16680          * @method getElWrapper
16681          * @param {String} id the id of the element to get
16682          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16683          * @private
16684          * @deprecated This wrapper isn't that useful
16685          * @static
16686          */
16687         getElWrapper: function(id) {
16688             var oWrapper = this.elementCache[id];
16689             if (!oWrapper || !oWrapper.el) {
16690                 oWrapper = this.elementCache[id] =
16691                     new this.ElementWrapper(Roo.getDom(id));
16692             }
16693             return oWrapper;
16694         },
16695
16696         /**
16697          * Returns the actual DOM element
16698          * @method getElement
16699          * @param {String} id the id of the elment to get
16700          * @return {Object} The element
16701          * @deprecated use Roo.getDom instead
16702          * @static
16703          */
16704         getElement: function(id) {
16705             return Roo.getDom(id);
16706         },
16707
16708         /**
16709          * Returns the style property for the DOM element (i.e.,
16710          * document.getElById(id).style)
16711          * @method getCss
16712          * @param {String} id the id of the elment to get
16713          * @return {Object} The style property of the element
16714          * @deprecated use Roo.getDom instead
16715          * @static
16716          */
16717         getCss: function(id) {
16718             var el = Roo.getDom(id);
16719             return (el) ? el.style : null;
16720         },
16721
16722         /**
16723          * Inner class for cached elements
16724          * @class DragDropMgr.ElementWrapper
16725          * @for DragDropMgr
16726          * @private
16727          * @deprecated
16728          */
16729         ElementWrapper: function(el) {
16730                 /**
16731                  * The element
16732                  * @property el
16733                  */
16734                 this.el = el || null;
16735                 /**
16736                  * The element id
16737                  * @property id
16738                  */
16739                 this.id = this.el && el.id;
16740                 /**
16741                  * A reference to the style property
16742                  * @property css
16743                  */
16744                 this.css = this.el && el.style;
16745             },
16746
16747         /**
16748          * Returns the X position of an html element
16749          * @method getPosX
16750          * @param el the element for which to get the position
16751          * @return {int} the X coordinate
16752          * @for DragDropMgr
16753          * @deprecated use Roo.lib.Dom.getX instead
16754          * @static
16755          */
16756         getPosX: function(el) {
16757             return Roo.lib.Dom.getX(el);
16758         },
16759
16760         /**
16761          * Returns the Y position of an html element
16762          * @method getPosY
16763          * @param el the element for which to get the position
16764          * @return {int} the Y coordinate
16765          * @deprecated use Roo.lib.Dom.getY instead
16766          * @static
16767          */
16768         getPosY: function(el) {
16769             return Roo.lib.Dom.getY(el);
16770         },
16771
16772         /**
16773          * Swap two nodes.  In IE, we use the native method, for others we
16774          * emulate the IE behavior
16775          * @method swapNode
16776          * @param n1 the first node to swap
16777          * @param n2 the other node to swap
16778          * @static
16779          */
16780         swapNode: function(n1, n2) {
16781             if (n1.swapNode) {
16782                 n1.swapNode(n2);
16783             } else {
16784                 var p = n2.parentNode;
16785                 var s = n2.nextSibling;
16786
16787                 if (s == n1) {
16788                     p.insertBefore(n1, n2);
16789                 } else if (n2 == n1.nextSibling) {
16790                     p.insertBefore(n2, n1);
16791                 } else {
16792                     n1.parentNode.replaceChild(n2, n1);
16793                     p.insertBefore(n1, s);
16794                 }
16795             }
16796         },
16797
16798         /**
16799          * Returns the current scroll position
16800          * @method getScroll
16801          * @private
16802          * @static
16803          */
16804         getScroll: function () {
16805             var t, l, dde=document.documentElement, db=document.body;
16806             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16807                 t = dde.scrollTop;
16808                 l = dde.scrollLeft;
16809             } else if (db) {
16810                 t = db.scrollTop;
16811                 l = db.scrollLeft;
16812             } else {
16813
16814             }
16815             return { top: t, left: l };
16816         },
16817
16818         /**
16819          * Returns the specified element style property
16820          * @method getStyle
16821          * @param {HTMLElement} el          the element
16822          * @param {string}      styleProp   the style property
16823          * @return {string} The value of the style property
16824          * @deprecated use Roo.lib.Dom.getStyle
16825          * @static
16826          */
16827         getStyle: function(el, styleProp) {
16828             return Roo.fly(el).getStyle(styleProp);
16829         },
16830
16831         /**
16832          * Gets the scrollTop
16833          * @method getScrollTop
16834          * @return {int} the document's scrollTop
16835          * @static
16836          */
16837         getScrollTop: function () { return this.getScroll().top; },
16838
16839         /**
16840          * Gets the scrollLeft
16841          * @method getScrollLeft
16842          * @return {int} the document's scrollTop
16843          * @static
16844          */
16845         getScrollLeft: function () { return this.getScroll().left; },
16846
16847         /**
16848          * Sets the x/y position of an element to the location of the
16849          * target element.
16850          * @method moveToEl
16851          * @param {HTMLElement} moveEl      The element to move
16852          * @param {HTMLElement} targetEl    The position reference element
16853          * @static
16854          */
16855         moveToEl: function (moveEl, targetEl) {
16856             var aCoord = Roo.lib.Dom.getXY(targetEl);
16857             Roo.lib.Dom.setXY(moveEl, aCoord);
16858         },
16859
16860         /**
16861          * Numeric array sort function
16862          * @method numericSort
16863          * @static
16864          */
16865         numericSort: function(a, b) { return (a - b); },
16866
16867         /**
16868          * Internal counter
16869          * @property _timeoutCount
16870          * @private
16871          * @static
16872          */
16873         _timeoutCount: 0,
16874
16875         /**
16876          * Trying to make the load order less important.  Without this we get
16877          * an error if this file is loaded before the Event Utility.
16878          * @method _addListeners
16879          * @private
16880          * @static
16881          */
16882         _addListeners: function() {
16883             var DDM = Roo.dd.DDM;
16884             if ( Roo.lib.Event && document ) {
16885                 DDM._onLoad();
16886             } else {
16887                 if (DDM._timeoutCount > 2000) {
16888                 } else {
16889                     setTimeout(DDM._addListeners, 10);
16890                     if (document && document.body) {
16891                         DDM._timeoutCount += 1;
16892                     }
16893                 }
16894             }
16895         },
16896
16897         /**
16898          * Recursively searches the immediate parent and all child nodes for
16899          * the handle element in order to determine wheter or not it was
16900          * clicked.
16901          * @method handleWasClicked
16902          * @param node the html element to inspect
16903          * @static
16904          */
16905         handleWasClicked: function(node, id) {
16906             if (this.isHandle(id, node.id)) {
16907                 return true;
16908             } else {
16909                 // check to see if this is a text node child of the one we want
16910                 var p = node.parentNode;
16911
16912                 while (p) {
16913                     if (this.isHandle(id, p.id)) {
16914                         return true;
16915                     } else {
16916                         p = p.parentNode;
16917                     }
16918                 }
16919             }
16920
16921             return false;
16922         }
16923
16924     };
16925
16926 }();
16927
16928 // shorter alias, save a few bytes
16929 Roo.dd.DDM = Roo.dd.DragDropMgr;
16930 Roo.dd.DDM._addListeners();
16931
16932 }/*
16933  * Based on:
16934  * Ext JS Library 1.1.1
16935  * Copyright(c) 2006-2007, Ext JS, LLC.
16936  *
16937  * Originally Released Under LGPL - original licence link has changed is not relivant.
16938  *
16939  * Fork - LGPL
16940  * <script type="text/javascript">
16941  */
16942
16943 /**
16944  * @class Roo.dd.DD
16945  * A DragDrop implementation where the linked element follows the
16946  * mouse cursor during a drag.
16947  * @extends Roo.dd.DragDrop
16948  * @constructor
16949  * @param {String} id the id of the linked element
16950  * @param {String} sGroup the group of related DragDrop items
16951  * @param {object} config an object containing configurable attributes
16952  *                Valid properties for DD:
16953  *                    scroll
16954  */
16955 Roo.dd.DD = function(id, sGroup, config) {
16956     if (id) {
16957         this.init(id, sGroup, config);
16958     }
16959 };
16960
16961 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16962
16963     /**
16964      * When set to true, the utility automatically tries to scroll the browser
16965      * window wehn a drag and drop element is dragged near the viewport boundary.
16966      * Defaults to true.
16967      * @property scroll
16968      * @type boolean
16969      */
16970     scroll: true,
16971
16972     /**
16973      * Sets the pointer offset to the distance between the linked element's top
16974      * left corner and the location the element was clicked
16975      * @method autoOffset
16976      * @param {int} iPageX the X coordinate of the click
16977      * @param {int} iPageY the Y coordinate of the click
16978      */
16979     autoOffset: function(iPageX, iPageY) {
16980         var x = iPageX - this.startPageX;
16981         var y = iPageY - this.startPageY;
16982         this.setDelta(x, y);
16983     },
16984
16985     /**
16986      * Sets the pointer offset.  You can call this directly to force the
16987      * offset to be in a particular location (e.g., pass in 0,0 to set it
16988      * to the center of the object)
16989      * @method setDelta
16990      * @param {int} iDeltaX the distance from the left
16991      * @param {int} iDeltaY the distance from the top
16992      */
16993     setDelta: function(iDeltaX, iDeltaY) {
16994         this.deltaX = iDeltaX;
16995         this.deltaY = iDeltaY;
16996     },
16997
16998     /**
16999      * Sets the drag element to the location of the mousedown or click event,
17000      * maintaining the cursor location relative to the location on the element
17001      * that was clicked.  Override this if you want to place the element in a
17002      * location other than where the cursor is.
17003      * @method setDragElPos
17004      * @param {int} iPageX the X coordinate of the mousedown or drag event
17005      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17006      */
17007     setDragElPos: function(iPageX, iPageY) {
17008         // the first time we do this, we are going to check to make sure
17009         // the element has css positioning
17010
17011         var el = this.getDragEl();
17012         this.alignElWithMouse(el, iPageX, iPageY);
17013     },
17014
17015     /**
17016      * Sets the element to the location of the mousedown or click event,
17017      * maintaining the cursor location relative to the location on the element
17018      * that was clicked.  Override this if you want to place the element in a
17019      * location other than where the cursor is.
17020      * @method alignElWithMouse
17021      * @param {HTMLElement} el the element to move
17022      * @param {int} iPageX the X coordinate of the mousedown or drag event
17023      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17024      */
17025     alignElWithMouse: function(el, iPageX, iPageY) {
17026         var oCoord = this.getTargetCoord(iPageX, iPageY);
17027         var fly = el.dom ? el : Roo.fly(el);
17028         if (!this.deltaSetXY) {
17029             var aCoord = [oCoord.x, oCoord.y];
17030             fly.setXY(aCoord);
17031             var newLeft = fly.getLeft(true);
17032             var newTop  = fly.getTop(true);
17033             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17034         } else {
17035             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17036         }
17037
17038         this.cachePosition(oCoord.x, oCoord.y);
17039         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17040         return oCoord;
17041     },
17042
17043     /**
17044      * Saves the most recent position so that we can reset the constraints and
17045      * tick marks on-demand.  We need to know this so that we can calculate the
17046      * number of pixels the element is offset from its original position.
17047      * @method cachePosition
17048      * @param iPageX the current x position (optional, this just makes it so we
17049      * don't have to look it up again)
17050      * @param iPageY the current y position (optional, this just makes it so we
17051      * don't have to look it up again)
17052      */
17053     cachePosition: function(iPageX, iPageY) {
17054         if (iPageX) {
17055             this.lastPageX = iPageX;
17056             this.lastPageY = iPageY;
17057         } else {
17058             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17059             this.lastPageX = aCoord[0];
17060             this.lastPageY = aCoord[1];
17061         }
17062     },
17063
17064     /**
17065      * Auto-scroll the window if the dragged object has been moved beyond the
17066      * visible window boundary.
17067      * @method autoScroll
17068      * @param {int} x the drag element's x position
17069      * @param {int} y the drag element's y position
17070      * @param {int} h the height of the drag element
17071      * @param {int} w the width of the drag element
17072      * @private
17073      */
17074     autoScroll: function(x, y, h, w) {
17075
17076         if (this.scroll) {
17077             // The client height
17078             var clientH = Roo.lib.Dom.getViewWidth();
17079
17080             // The client width
17081             var clientW = Roo.lib.Dom.getViewHeight();
17082
17083             // The amt scrolled down
17084             var st = this.DDM.getScrollTop();
17085
17086             // The amt scrolled right
17087             var sl = this.DDM.getScrollLeft();
17088
17089             // Location of the bottom of the element
17090             var bot = h + y;
17091
17092             // Location of the right of the element
17093             var right = w + x;
17094
17095             // The distance from the cursor to the bottom of the visible area,
17096             // adjusted so that we don't scroll if the cursor is beyond the
17097             // element drag constraints
17098             var toBot = (clientH + st - y - this.deltaY);
17099
17100             // The distance from the cursor to the right of the visible area
17101             var toRight = (clientW + sl - x - this.deltaX);
17102
17103
17104             // How close to the edge the cursor must be before we scroll
17105             // var thresh = (document.all) ? 100 : 40;
17106             var thresh = 40;
17107
17108             // How many pixels to scroll per autoscroll op.  This helps to reduce
17109             // clunky scrolling. IE is more sensitive about this ... it needs this
17110             // value to be higher.
17111             var scrAmt = (document.all) ? 80 : 30;
17112
17113             // Scroll down if we are near the bottom of the visible page and the
17114             // obj extends below the crease
17115             if ( bot > clientH && toBot < thresh ) {
17116                 window.scrollTo(sl, st + scrAmt);
17117             }
17118
17119             // Scroll up if the window is scrolled down and the top of the object
17120             // goes above the top border
17121             if ( y < st && st > 0 && y - st < thresh ) {
17122                 window.scrollTo(sl, st - scrAmt);
17123             }
17124
17125             // Scroll right if the obj is beyond the right border and the cursor is
17126             // near the border.
17127             if ( right > clientW && toRight < thresh ) {
17128                 window.scrollTo(sl + scrAmt, st);
17129             }
17130
17131             // Scroll left if the window has been scrolled to the right and the obj
17132             // extends past the left border
17133             if ( x < sl && sl > 0 && x - sl < thresh ) {
17134                 window.scrollTo(sl - scrAmt, st);
17135             }
17136         }
17137     },
17138
17139     /**
17140      * Finds the location the element should be placed if we want to move
17141      * it to where the mouse location less the click offset would place us.
17142      * @method getTargetCoord
17143      * @param {int} iPageX the X coordinate of the click
17144      * @param {int} iPageY the Y coordinate of the click
17145      * @return an object that contains the coordinates (Object.x and Object.y)
17146      * @private
17147      */
17148     getTargetCoord: function(iPageX, iPageY) {
17149
17150
17151         var x = iPageX - this.deltaX;
17152         var y = iPageY - this.deltaY;
17153
17154         if (this.constrainX) {
17155             if (x < this.minX) { x = this.minX; }
17156             if (x > this.maxX) { x = this.maxX; }
17157         }
17158
17159         if (this.constrainY) {
17160             if (y < this.minY) { y = this.minY; }
17161             if (y > this.maxY) { y = this.maxY; }
17162         }
17163
17164         x = this.getTick(x, this.xTicks);
17165         y = this.getTick(y, this.yTicks);
17166
17167
17168         return {x:x, y:y};
17169     },
17170
17171     /*
17172      * Sets up config options specific to this class. Overrides
17173      * Roo.dd.DragDrop, but all versions of this method through the
17174      * inheritance chain are called
17175      */
17176     applyConfig: function() {
17177         Roo.dd.DD.superclass.applyConfig.call(this);
17178         this.scroll = (this.config.scroll !== false);
17179     },
17180
17181     /*
17182      * Event that fires prior to the onMouseDown event.  Overrides
17183      * Roo.dd.DragDrop.
17184      */
17185     b4MouseDown: function(e) {
17186         // this.resetConstraints();
17187         this.autoOffset(e.getPageX(),
17188                             e.getPageY());
17189     },
17190
17191     /*
17192      * Event that fires prior to the onDrag event.  Overrides
17193      * Roo.dd.DragDrop.
17194      */
17195     b4Drag: function(e) {
17196         this.setDragElPos(e.getPageX(),
17197                             e.getPageY());
17198     },
17199
17200     toString: function() {
17201         return ("DD " + this.id);
17202     }
17203
17204     //////////////////////////////////////////////////////////////////////////
17205     // Debugging ygDragDrop events that can be overridden
17206     //////////////////////////////////////////////////////////////////////////
17207     /*
17208     startDrag: function(x, y) {
17209     },
17210
17211     onDrag: function(e) {
17212     },
17213
17214     onDragEnter: function(e, id) {
17215     },
17216
17217     onDragOver: function(e, id) {
17218     },
17219
17220     onDragOut: function(e, id) {
17221     },
17222
17223     onDragDrop: function(e, id) {
17224     },
17225
17226     endDrag: function(e) {
17227     }
17228
17229     */
17230
17231 });/*
17232  * Based on:
17233  * Ext JS Library 1.1.1
17234  * Copyright(c) 2006-2007, Ext JS, LLC.
17235  *
17236  * Originally Released Under LGPL - original licence link has changed is not relivant.
17237  *
17238  * Fork - LGPL
17239  * <script type="text/javascript">
17240  */
17241
17242 /**
17243  * @class Roo.dd.DDProxy
17244  * A DragDrop implementation that inserts an empty, bordered div into
17245  * the document that follows the cursor during drag operations.  At the time of
17246  * the click, the frame div is resized to the dimensions of the linked html
17247  * element, and moved to the exact location of the linked element.
17248  *
17249  * References to the "frame" element refer to the single proxy element that
17250  * was created to be dragged in place of all DDProxy elements on the
17251  * page.
17252  *
17253  * @extends Roo.dd.DD
17254  * @constructor
17255  * @param {String} id the id of the linked html element
17256  * @param {String} sGroup the group of related DragDrop objects
17257  * @param {object} config an object containing configurable attributes
17258  *                Valid properties for DDProxy in addition to those in DragDrop:
17259  *                   resizeFrame, centerFrame, dragElId
17260  */
17261 Roo.dd.DDProxy = function(id, sGroup, config) {
17262     if (id) {
17263         this.init(id, sGroup, config);
17264         this.initFrame();
17265     }
17266 };
17267
17268 /**
17269  * The default drag frame div id
17270  * @property Roo.dd.DDProxy.dragElId
17271  * @type String
17272  * @static
17273  */
17274 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17275
17276 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17277
17278     /**
17279      * By default we resize the drag frame to be the same size as the element
17280      * we want to drag (this is to get the frame effect).  We can turn it off
17281      * if we want a different behavior.
17282      * @property resizeFrame
17283      * @type boolean
17284      */
17285     resizeFrame: true,
17286
17287     /**
17288      * By default the frame is positioned exactly where the drag element is, so
17289      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17290      * you do not have constraints on the obj is to have the drag frame centered
17291      * around the cursor.  Set centerFrame to true for this effect.
17292      * @property centerFrame
17293      * @type boolean
17294      */
17295     centerFrame: false,
17296
17297     /**
17298      * Creates the proxy element if it does not yet exist
17299      * @method createFrame
17300      */
17301     createFrame: function() {
17302         var self = this;
17303         var body = document.body;
17304
17305         if (!body || !body.firstChild) {
17306             setTimeout( function() { self.createFrame(); }, 50 );
17307             return;
17308         }
17309
17310         var div = this.getDragEl();
17311
17312         if (!div) {
17313             div    = document.createElement("div");
17314             div.id = this.dragElId;
17315             var s  = div.style;
17316
17317             s.position   = "absolute";
17318             s.visibility = "hidden";
17319             s.cursor     = "move";
17320             s.border     = "2px solid #aaa";
17321             s.zIndex     = 999;
17322
17323             // appendChild can blow up IE if invoked prior to the window load event
17324             // while rendering a table.  It is possible there are other scenarios
17325             // that would cause this to happen as well.
17326             body.insertBefore(div, body.firstChild);
17327         }
17328     },
17329
17330     /**
17331      * Initialization for the drag frame element.  Must be called in the
17332      * constructor of all subclasses
17333      * @method initFrame
17334      */
17335     initFrame: function() {
17336         this.createFrame();
17337     },
17338
17339     applyConfig: function() {
17340         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17341
17342         this.resizeFrame = (this.config.resizeFrame !== false);
17343         this.centerFrame = (this.config.centerFrame);
17344         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17345     },
17346
17347     /**
17348      * Resizes the drag frame to the dimensions of the clicked object, positions
17349      * it over the object, and finally displays it
17350      * @method showFrame
17351      * @param {int} iPageX X click position
17352      * @param {int} iPageY Y click position
17353      * @private
17354      */
17355     showFrame: function(iPageX, iPageY) {
17356         var el = this.getEl();
17357         var dragEl = this.getDragEl();
17358         var s = dragEl.style;
17359
17360         this._resizeProxy();
17361
17362         if (this.centerFrame) {
17363             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17364                            Math.round(parseInt(s.height, 10)/2) );
17365         }
17366
17367         this.setDragElPos(iPageX, iPageY);
17368
17369         Roo.fly(dragEl).show();
17370     },
17371
17372     /**
17373      * The proxy is automatically resized to the dimensions of the linked
17374      * element when a drag is initiated, unless resizeFrame is set to false
17375      * @method _resizeProxy
17376      * @private
17377      */
17378     _resizeProxy: function() {
17379         if (this.resizeFrame) {
17380             var el = this.getEl();
17381             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17382         }
17383     },
17384
17385     // overrides Roo.dd.DragDrop
17386     b4MouseDown: function(e) {
17387         var x = e.getPageX();
17388         var y = e.getPageY();
17389         this.autoOffset(x, y);
17390         this.setDragElPos(x, y);
17391     },
17392
17393     // overrides Roo.dd.DragDrop
17394     b4StartDrag: function(x, y) {
17395         // show the drag frame
17396         this.showFrame(x, y);
17397     },
17398
17399     // overrides Roo.dd.DragDrop
17400     b4EndDrag: function(e) {
17401         Roo.fly(this.getDragEl()).hide();
17402     },
17403
17404     // overrides Roo.dd.DragDrop
17405     // By default we try to move the element to the last location of the frame.
17406     // This is so that the default behavior mirrors that of Roo.dd.DD.
17407     endDrag: function(e) {
17408
17409         var lel = this.getEl();
17410         var del = this.getDragEl();
17411
17412         // Show the drag frame briefly so we can get its position
17413         del.style.visibility = "";
17414
17415         this.beforeMove();
17416         // Hide the linked element before the move to get around a Safari
17417         // rendering bug.
17418         lel.style.visibility = "hidden";
17419         Roo.dd.DDM.moveToEl(lel, del);
17420         del.style.visibility = "hidden";
17421         lel.style.visibility = "";
17422
17423         this.afterDrag();
17424     },
17425
17426     beforeMove : function(){
17427
17428     },
17429
17430     afterDrag : function(){
17431
17432     },
17433
17434     toString: function() {
17435         return ("DDProxy " + this.id);
17436     }
17437
17438 });
17439 /*
17440  * Based on:
17441  * Ext JS Library 1.1.1
17442  * Copyright(c) 2006-2007, Ext JS, LLC.
17443  *
17444  * Originally Released Under LGPL - original licence link has changed is not relivant.
17445  *
17446  * Fork - LGPL
17447  * <script type="text/javascript">
17448  */
17449
17450  /**
17451  * @class Roo.dd.DDTarget
17452  * A DragDrop implementation that does not move, but can be a drop
17453  * target.  You would get the same result by simply omitting implementation
17454  * for the event callbacks, but this way we reduce the processing cost of the
17455  * event listener and the callbacks.
17456  * @extends Roo.dd.DragDrop
17457  * @constructor
17458  * @param {String} id the id of the element that is a drop target
17459  * @param {String} sGroup the group of related DragDrop objects
17460  * @param {object} config an object containing configurable attributes
17461  *                 Valid properties for DDTarget in addition to those in
17462  *                 DragDrop:
17463  *                    none
17464  */
17465 Roo.dd.DDTarget = function(id, sGroup, config) {
17466     if (id) {
17467         this.initTarget(id, sGroup, config);
17468     }
17469     if (config.listeners || config.events) { 
17470        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17471             listeners : config.listeners || {}, 
17472             events : config.events || {} 
17473         });    
17474     }
17475 };
17476
17477 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17478 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17479     toString: function() {
17480         return ("DDTarget " + this.id);
17481     }
17482 });
17483 /*
17484  * Based on:
17485  * Ext JS Library 1.1.1
17486  * Copyright(c) 2006-2007, Ext JS, LLC.
17487  *
17488  * Originally Released Under LGPL - original licence link has changed is not relivant.
17489  *
17490  * Fork - LGPL
17491  * <script type="text/javascript">
17492  */
17493  
17494
17495 /**
17496  * @class Roo.dd.ScrollManager
17497  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17498  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17499  * @singleton
17500  */
17501 Roo.dd.ScrollManager = function(){
17502     var ddm = Roo.dd.DragDropMgr;
17503     var els = {};
17504     var dragEl = null;
17505     var proc = {};
17506     
17507     var onStop = function(e){
17508         dragEl = null;
17509         clearProc();
17510     };
17511     
17512     var triggerRefresh = function(){
17513         if(ddm.dragCurrent){
17514              ddm.refreshCache(ddm.dragCurrent.groups);
17515         }
17516     };
17517     
17518     var doScroll = function(){
17519         if(ddm.dragCurrent){
17520             var dds = Roo.dd.ScrollManager;
17521             if(!dds.animate){
17522                 if(proc.el.scroll(proc.dir, dds.increment)){
17523                     triggerRefresh();
17524                 }
17525             }else{
17526                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17527             }
17528         }
17529     };
17530     
17531     var clearProc = function(){
17532         if(proc.id){
17533             clearInterval(proc.id);
17534         }
17535         proc.id = 0;
17536         proc.el = null;
17537         proc.dir = "";
17538     };
17539     
17540     var startProc = function(el, dir){
17541         clearProc();
17542         proc.el = el;
17543         proc.dir = dir;
17544         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17545     };
17546     
17547     var onFire = function(e, isDrop){
17548         if(isDrop || !ddm.dragCurrent){ return; }
17549         var dds = Roo.dd.ScrollManager;
17550         if(!dragEl || dragEl != ddm.dragCurrent){
17551             dragEl = ddm.dragCurrent;
17552             // refresh regions on drag start
17553             dds.refreshCache();
17554         }
17555         
17556         var xy = Roo.lib.Event.getXY(e);
17557         var pt = new Roo.lib.Point(xy[0], xy[1]);
17558         for(var id in els){
17559             var el = els[id], r = el._region;
17560             if(r && r.contains(pt) && el.isScrollable()){
17561                 if(r.bottom - pt.y <= dds.thresh){
17562                     if(proc.el != el){
17563                         startProc(el, "down");
17564                     }
17565                     return;
17566                 }else if(r.right - pt.x <= dds.thresh){
17567                     if(proc.el != el){
17568                         startProc(el, "left");
17569                     }
17570                     return;
17571                 }else if(pt.y - r.top <= dds.thresh){
17572                     if(proc.el != el){
17573                         startProc(el, "up");
17574                     }
17575                     return;
17576                 }else if(pt.x - r.left <= dds.thresh){
17577                     if(proc.el != el){
17578                         startProc(el, "right");
17579                     }
17580                     return;
17581                 }
17582             }
17583         }
17584         clearProc();
17585     };
17586     
17587     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17588     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17589     
17590     return {
17591         /**
17592          * Registers new overflow element(s) to auto scroll
17593          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17594          */
17595         register : function(el){
17596             if(el instanceof Array){
17597                 for(var i = 0, len = el.length; i < len; i++) {
17598                         this.register(el[i]);
17599                 }
17600             }else{
17601                 el = Roo.get(el);
17602                 els[el.id] = el;
17603             }
17604         },
17605         
17606         /**
17607          * Unregisters overflow element(s) so they are no longer scrolled
17608          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17609          */
17610         unregister : function(el){
17611             if(el instanceof Array){
17612                 for(var i = 0, len = el.length; i < len; i++) {
17613                         this.unregister(el[i]);
17614                 }
17615             }else{
17616                 el = Roo.get(el);
17617                 delete els[el.id];
17618             }
17619         },
17620         
17621         /**
17622          * The number of pixels from the edge of a container the pointer needs to be to 
17623          * trigger scrolling (defaults to 25)
17624          * @type Number
17625          */
17626         thresh : 25,
17627         
17628         /**
17629          * The number of pixels to scroll in each scroll increment (defaults to 50)
17630          * @type Number
17631          */
17632         increment : 100,
17633         
17634         /**
17635          * The frequency of scrolls in milliseconds (defaults to 500)
17636          * @type Number
17637          */
17638         frequency : 500,
17639         
17640         /**
17641          * True to animate the scroll (defaults to true)
17642          * @type Boolean
17643          */
17644         animate: true,
17645         
17646         /**
17647          * The animation duration in seconds - 
17648          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17649          * @type Number
17650          */
17651         animDuration: .4,
17652         
17653         /**
17654          * Manually trigger a cache refresh.
17655          */
17656         refreshCache : function(){
17657             for(var id in els){
17658                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17659                     els[id]._region = els[id].getRegion();
17660                 }
17661             }
17662         }
17663     };
17664 }();/*
17665  * Based on:
17666  * Ext JS Library 1.1.1
17667  * Copyright(c) 2006-2007, Ext JS, LLC.
17668  *
17669  * Originally Released Under LGPL - original licence link has changed is not relivant.
17670  *
17671  * Fork - LGPL
17672  * <script type="text/javascript">
17673  */
17674  
17675
17676 /**
17677  * @class Roo.dd.Registry
17678  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17679  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17680  * @singleton
17681  */
17682 Roo.dd.Registry = function(){
17683     var elements = {}; 
17684     var handles = {}; 
17685     var autoIdSeed = 0;
17686
17687     var getId = function(el, autogen){
17688         if(typeof el == "string"){
17689             return el;
17690         }
17691         var id = el.id;
17692         if(!id && autogen !== false){
17693             id = "roodd-" + (++autoIdSeed);
17694             el.id = id;
17695         }
17696         return id;
17697     };
17698     
17699     return {
17700     /**
17701      * Register a drag drop element
17702      * @param {String|HTMLElement} element The id or DOM node to register
17703      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17704      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17705      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17706      * populated in the data object (if applicable):
17707      * <pre>
17708 Value      Description<br />
17709 ---------  ------------------------------------------<br />
17710 handles    Array of DOM nodes that trigger dragging<br />
17711            for the element being registered<br />
17712 isHandle   True if the element passed in triggers<br />
17713            dragging itself, else false
17714 </pre>
17715      */
17716         register : function(el, data){
17717             data = data || {};
17718             if(typeof el == "string"){
17719                 el = document.getElementById(el);
17720             }
17721             data.ddel = el;
17722             elements[getId(el)] = data;
17723             if(data.isHandle !== false){
17724                 handles[data.ddel.id] = data;
17725             }
17726             if(data.handles){
17727                 var hs = data.handles;
17728                 for(var i = 0, len = hs.length; i < len; i++){
17729                         handles[getId(hs[i])] = data;
17730                 }
17731             }
17732         },
17733
17734     /**
17735      * Unregister a drag drop element
17736      * @param {String|HTMLElement}  element The id or DOM node to unregister
17737      */
17738         unregister : function(el){
17739             var id = getId(el, false);
17740             var data = elements[id];
17741             if(data){
17742                 delete elements[id];
17743                 if(data.handles){
17744                     var hs = data.handles;
17745                     for(var i = 0, len = hs.length; i < len; i++){
17746                         delete handles[getId(hs[i], false)];
17747                     }
17748                 }
17749             }
17750         },
17751
17752     /**
17753      * Returns the handle registered for a DOM Node by id
17754      * @param {String|HTMLElement} id The DOM node or id to look up
17755      * @return {Object} handle The custom handle data
17756      */
17757         getHandle : function(id){
17758             if(typeof id != "string"){ // must be element?
17759                 id = id.id;
17760             }
17761             return handles[id];
17762         },
17763
17764     /**
17765      * Returns the handle that is registered for the DOM node that is the target of the event
17766      * @param {Event} e The event
17767      * @return {Object} handle The custom handle data
17768      */
17769         getHandleFromEvent : function(e){
17770             var t = Roo.lib.Event.getTarget(e);
17771             return t ? handles[t.id] : null;
17772         },
17773
17774     /**
17775      * Returns a custom data object that is registered for a DOM node by id
17776      * @param {String|HTMLElement} id The DOM node or id to look up
17777      * @return {Object} data The custom data
17778      */
17779         getTarget : function(id){
17780             if(typeof id != "string"){ // must be element?
17781                 id = id.id;
17782             }
17783             return elements[id];
17784         },
17785
17786     /**
17787      * Returns a custom data object that is registered for the DOM node that is the target of the event
17788      * @param {Event} e The event
17789      * @return {Object} data The custom data
17790      */
17791         getTargetFromEvent : function(e){
17792             var t = Roo.lib.Event.getTarget(e);
17793             return t ? elements[t.id] || handles[t.id] : null;
17794         }
17795     };
17796 }();/*
17797  * Based on:
17798  * Ext JS Library 1.1.1
17799  * Copyright(c) 2006-2007, Ext JS, LLC.
17800  *
17801  * Originally Released Under LGPL - original licence link has changed is not relivant.
17802  *
17803  * Fork - LGPL
17804  * <script type="text/javascript">
17805  */
17806  
17807
17808 /**
17809  * @class Roo.dd.StatusProxy
17810  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17811  * default drag proxy used by all Roo.dd components.
17812  * @constructor
17813  * @param {Object} config
17814  */
17815 Roo.dd.StatusProxy = function(config){
17816     Roo.apply(this, config);
17817     this.id = this.id || Roo.id();
17818     this.el = new Roo.Layer({
17819         dh: {
17820             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17821                 {tag: "div", cls: "x-dd-drop-icon"},
17822                 {tag: "div", cls: "x-dd-drag-ghost"}
17823             ]
17824         }, 
17825         shadow: !config || config.shadow !== false
17826     });
17827     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17828     this.dropStatus = this.dropNotAllowed;
17829 };
17830
17831 Roo.dd.StatusProxy.prototype = {
17832     /**
17833      * @cfg {String} dropAllowed
17834      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17835      */
17836     dropAllowed : "x-dd-drop-ok",
17837     /**
17838      * @cfg {String} dropNotAllowed
17839      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17840      */
17841     dropNotAllowed : "x-dd-drop-nodrop",
17842
17843     /**
17844      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17845      * over the current target element.
17846      * @param {String} cssClass The css class for the new drop status indicator image
17847      */
17848     setStatus : function(cssClass){
17849         cssClass = cssClass || this.dropNotAllowed;
17850         if(this.dropStatus != cssClass){
17851             this.el.replaceClass(this.dropStatus, cssClass);
17852             this.dropStatus = cssClass;
17853         }
17854     },
17855
17856     /**
17857      * Resets the status indicator to the default dropNotAllowed value
17858      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17859      */
17860     reset : function(clearGhost){
17861         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17862         this.dropStatus = this.dropNotAllowed;
17863         if(clearGhost){
17864             this.ghost.update("");
17865         }
17866     },
17867
17868     /**
17869      * Updates the contents of the ghost element
17870      * @param {String} html The html that will replace the current innerHTML of the ghost element
17871      */
17872     update : function(html){
17873         if(typeof html == "string"){
17874             this.ghost.update(html);
17875         }else{
17876             this.ghost.update("");
17877             html.style.margin = "0";
17878             this.ghost.dom.appendChild(html);
17879         }
17880         // ensure float = none set?? cant remember why though.
17881         var el = this.ghost.dom.firstChild;
17882                 if(el){
17883                         Roo.fly(el).setStyle('float', 'none');
17884                 }
17885     },
17886     
17887     /**
17888      * Returns the underlying proxy {@link Roo.Layer}
17889      * @return {Roo.Layer} el
17890     */
17891     getEl : function(){
17892         return this.el;
17893     },
17894
17895     /**
17896      * Returns the ghost element
17897      * @return {Roo.Element} el
17898      */
17899     getGhost : function(){
17900         return this.ghost;
17901     },
17902
17903     /**
17904      * Hides the proxy
17905      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17906      */
17907     hide : function(clear){
17908         this.el.hide();
17909         if(clear){
17910             this.reset(true);
17911         }
17912     },
17913
17914     /**
17915      * Stops the repair animation if it's currently running
17916      */
17917     stop : function(){
17918         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17919             this.anim.stop();
17920         }
17921     },
17922
17923     /**
17924      * Displays this proxy
17925      */
17926     show : function(){
17927         this.el.show();
17928     },
17929
17930     /**
17931      * Force the Layer to sync its shadow and shim positions to the element
17932      */
17933     sync : function(){
17934         this.el.sync();
17935     },
17936
17937     /**
17938      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17939      * invalid drop operation by the item being dragged.
17940      * @param {Array} xy The XY position of the element ([x, y])
17941      * @param {Function} callback The function to call after the repair is complete
17942      * @param {Object} scope The scope in which to execute the callback
17943      */
17944     repair : function(xy, callback, scope){
17945         this.callback = callback;
17946         this.scope = scope;
17947         if(xy && this.animRepair !== false){
17948             this.el.addClass("x-dd-drag-repair");
17949             this.el.hideUnders(true);
17950             this.anim = this.el.shift({
17951                 duration: this.repairDuration || .5,
17952                 easing: 'easeOut',
17953                 xy: xy,
17954                 stopFx: true,
17955                 callback: this.afterRepair,
17956                 scope: this
17957             });
17958         }else{
17959             this.afterRepair();
17960         }
17961     },
17962
17963     // private
17964     afterRepair : function(){
17965         this.hide(true);
17966         if(typeof this.callback == "function"){
17967             this.callback.call(this.scope || this);
17968         }
17969         this.callback = null;
17970         this.scope = null;
17971     }
17972 };/*
17973  * Based on:
17974  * Ext JS Library 1.1.1
17975  * Copyright(c) 2006-2007, Ext JS, LLC.
17976  *
17977  * Originally Released Under LGPL - original licence link has changed is not relivant.
17978  *
17979  * Fork - LGPL
17980  * <script type="text/javascript">
17981  */
17982
17983 /**
17984  * @class Roo.dd.DragSource
17985  * @extends Roo.dd.DDProxy
17986  * A simple class that provides the basic implementation needed to make any element draggable.
17987  * @constructor
17988  * @param {String/HTMLElement/Element} el The container element
17989  * @param {Object} config
17990  */
17991 Roo.dd.DragSource = function(el, config){
17992     this.el = Roo.get(el);
17993     this.dragData = {};
17994     
17995     Roo.apply(this, config);
17996     
17997     if(!this.proxy){
17998         this.proxy = new Roo.dd.StatusProxy();
17999     }
18000
18001     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18002           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18003     
18004     this.dragging = false;
18005 };
18006
18007 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18008     /**
18009      * @cfg {String} dropAllowed
18010      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18011      */
18012     dropAllowed : "x-dd-drop-ok",
18013     /**
18014      * @cfg {String} dropNotAllowed
18015      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18016      */
18017     dropNotAllowed : "x-dd-drop-nodrop",
18018
18019     /**
18020      * Returns the data object associated with this drag source
18021      * @return {Object} data An object containing arbitrary data
18022      */
18023     getDragData : function(e){
18024         return this.dragData;
18025     },
18026
18027     // private
18028     onDragEnter : function(e, id){
18029         var target = Roo.dd.DragDropMgr.getDDById(id);
18030         this.cachedTarget = target;
18031         if(this.beforeDragEnter(target, e, id) !== false){
18032             if(target.isNotifyTarget){
18033                 var status = target.notifyEnter(this, e, this.dragData);
18034                 this.proxy.setStatus(status);
18035             }else{
18036                 this.proxy.setStatus(this.dropAllowed);
18037             }
18038             
18039             if(this.afterDragEnter){
18040                 /**
18041                  * An empty function by default, but provided so that you can perform a custom action
18042                  * when the dragged item enters the drop target by providing an implementation.
18043                  * @param {Roo.dd.DragDrop} target The drop target
18044                  * @param {Event} e The event object
18045                  * @param {String} id The id of the dragged element
18046                  * @method afterDragEnter
18047                  */
18048                 this.afterDragEnter(target, e, id);
18049             }
18050         }
18051     },
18052
18053     /**
18054      * An empty function by default, but provided so that you can perform a custom action
18055      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18056      * @param {Roo.dd.DragDrop} target The drop target
18057      * @param {Event} e The event object
18058      * @param {String} id The id of the dragged element
18059      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18060      */
18061     beforeDragEnter : function(target, e, id){
18062         return true;
18063     },
18064
18065     // private
18066     alignElWithMouse: function() {
18067         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18068         this.proxy.sync();
18069     },
18070
18071     // private
18072     onDragOver : function(e, id){
18073         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18074         if(this.beforeDragOver(target, e, id) !== false){
18075             if(target.isNotifyTarget){
18076                 var status = target.notifyOver(this, e, this.dragData);
18077                 this.proxy.setStatus(status);
18078             }
18079
18080             if(this.afterDragOver){
18081                 /**
18082                  * An empty function by default, but provided so that you can perform a custom action
18083                  * while the dragged item is over the drop target by providing an implementation.
18084                  * @param {Roo.dd.DragDrop} target The drop target
18085                  * @param {Event} e The event object
18086                  * @param {String} id The id of the dragged element
18087                  * @method afterDragOver
18088                  */
18089                 this.afterDragOver(target, e, id);
18090             }
18091         }
18092     },
18093
18094     /**
18095      * An empty function by default, but provided so that you can perform a custom action
18096      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18097      * @param {Roo.dd.DragDrop} target The drop target
18098      * @param {Event} e The event object
18099      * @param {String} id The id of the dragged element
18100      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18101      */
18102     beforeDragOver : function(target, e, id){
18103         return true;
18104     },
18105
18106     // private
18107     onDragOut : function(e, id){
18108         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18109         if(this.beforeDragOut(target, e, id) !== false){
18110             if(target.isNotifyTarget){
18111                 target.notifyOut(this, e, this.dragData);
18112             }
18113             this.proxy.reset();
18114             if(this.afterDragOut){
18115                 /**
18116                  * An empty function by default, but provided so that you can perform a custom action
18117                  * after the dragged item is dragged out of the target without dropping.
18118                  * @param {Roo.dd.DragDrop} target The drop target
18119                  * @param {Event} e The event object
18120                  * @param {String} id The id of the dragged element
18121                  * @method afterDragOut
18122                  */
18123                 this.afterDragOut(target, e, id);
18124             }
18125         }
18126         this.cachedTarget = null;
18127     },
18128
18129     /**
18130      * An empty function by default, but provided so that you can perform a custom action before the dragged
18131      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18132      * @param {Roo.dd.DragDrop} target The drop target
18133      * @param {Event} e The event object
18134      * @param {String} id The id of the dragged element
18135      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18136      */
18137     beforeDragOut : function(target, e, id){
18138         return true;
18139     },
18140     
18141     // private
18142     onDragDrop : function(e, id){
18143         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18144         if(this.beforeDragDrop(target, e, id) !== false){
18145             if(target.isNotifyTarget){
18146                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18147                     this.onValidDrop(target, e, id);
18148                 }else{
18149                     this.onInvalidDrop(target, e, id);
18150                 }
18151             }else{
18152                 this.onValidDrop(target, e, id);
18153             }
18154             
18155             if(this.afterDragDrop){
18156                 /**
18157                  * An empty function by default, but provided so that you can perform a custom action
18158                  * after a valid drag drop has occurred by providing an implementation.
18159                  * @param {Roo.dd.DragDrop} target The drop target
18160                  * @param {Event} e The event object
18161                  * @param {String} id The id of the dropped element
18162                  * @method afterDragDrop
18163                  */
18164                 this.afterDragDrop(target, e, id);
18165             }
18166         }
18167         delete this.cachedTarget;
18168     },
18169
18170     /**
18171      * An empty function by default, but provided so that you can perform a custom action before the dragged
18172      * item is dropped onto the target and optionally cancel the onDragDrop.
18173      * @param {Roo.dd.DragDrop} target The drop target
18174      * @param {Event} e The event object
18175      * @param {String} id The id of the dragged element
18176      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18177      */
18178     beforeDragDrop : function(target, e, id){
18179         return true;
18180     },
18181
18182     // private
18183     onValidDrop : function(target, e, id){
18184         this.hideProxy();
18185         if(this.afterValidDrop){
18186             /**
18187              * An empty function by default, but provided so that you can perform a custom action
18188              * after a valid drop has occurred by providing an implementation.
18189              * @param {Object} target The target DD 
18190              * @param {Event} e The event object
18191              * @param {String} id The id of the dropped element
18192              * @method afterInvalidDrop
18193              */
18194             this.afterValidDrop(target, e, id);
18195         }
18196     },
18197
18198     // private
18199     getRepairXY : function(e, data){
18200         return this.el.getXY();  
18201     },
18202
18203     // private
18204     onInvalidDrop : function(target, e, id){
18205         this.beforeInvalidDrop(target, e, id);
18206         if(this.cachedTarget){
18207             if(this.cachedTarget.isNotifyTarget){
18208                 this.cachedTarget.notifyOut(this, e, this.dragData);
18209             }
18210             this.cacheTarget = null;
18211         }
18212         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18213
18214         if(this.afterInvalidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after an invalid drop has occurred by providing an implementation.
18218              * @param {Event} e The event object
18219              * @param {String} id The id of the dropped element
18220              * @method afterInvalidDrop
18221              */
18222             this.afterInvalidDrop(e, id);
18223         }
18224     },
18225
18226     // private
18227     afterRepair : function(){
18228         if(Roo.enableFx){
18229             this.el.highlight(this.hlColor || "c3daf9");
18230         }
18231         this.dragging = false;
18232     },
18233
18234     /**
18235      * An empty function by default, but provided so that you can perform a custom action after an invalid
18236      * drop has occurred.
18237      * @param {Roo.dd.DragDrop} target The drop target
18238      * @param {Event} e The event object
18239      * @param {String} id The id of the dragged element
18240      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18241      */
18242     beforeInvalidDrop : function(target, e, id){
18243         return true;
18244     },
18245
18246     // private
18247     handleMouseDown : function(e){
18248         if(this.dragging) {
18249             return;
18250         }
18251         var data = this.getDragData(e);
18252         if(data && this.onBeforeDrag(data, e) !== false){
18253             this.dragData = data;
18254             this.proxy.stop();
18255             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18256         } 
18257     },
18258
18259     /**
18260      * An empty function by default, but provided so that you can perform a custom action before the initial
18261      * drag event begins and optionally cancel it.
18262      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18263      * @param {Event} e The event object
18264      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18265      */
18266     onBeforeDrag : function(data, e){
18267         return true;
18268     },
18269
18270     /**
18271      * An empty function by default, but provided so that you can perform a custom action once the initial
18272      * drag event has begun.  The drag cannot be canceled from this function.
18273      * @param {Number} x The x position of the click on the dragged object
18274      * @param {Number} y The y position of the click on the dragged object
18275      */
18276     onStartDrag : Roo.emptyFn,
18277
18278     // private - YUI override
18279     startDrag : function(x, y){
18280         this.proxy.reset();
18281         this.dragging = true;
18282         this.proxy.update("");
18283         this.onInitDrag(x, y);
18284         this.proxy.show();
18285     },
18286
18287     // private
18288     onInitDrag : function(x, y){
18289         var clone = this.el.dom.cloneNode(true);
18290         clone.id = Roo.id(); // prevent duplicate ids
18291         this.proxy.update(clone);
18292         this.onStartDrag(x, y);
18293         return true;
18294     },
18295
18296     /**
18297      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18298      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18299      */
18300     getProxy : function(){
18301         return this.proxy;  
18302     },
18303
18304     /**
18305      * Hides the drag source's {@link Roo.dd.StatusProxy}
18306      */
18307     hideProxy : function(){
18308         this.proxy.hide();  
18309         this.proxy.reset(true);
18310         this.dragging = false;
18311     },
18312
18313     // private
18314     triggerCacheRefresh : function(){
18315         Roo.dd.DDM.refreshCache(this.groups);
18316     },
18317
18318     // private - override to prevent hiding
18319     b4EndDrag: function(e) {
18320     },
18321
18322     // private - override to prevent moving
18323     endDrag : function(e){
18324         this.onEndDrag(this.dragData, e);
18325     },
18326
18327     // private
18328     onEndDrag : function(data, e){
18329     },
18330     
18331     // private - pin to cursor
18332     autoOffset : function(x, y) {
18333         this.setDelta(-12, -20);
18334     }    
18335 });/*
18336  * Based on:
18337  * Ext JS Library 1.1.1
18338  * Copyright(c) 2006-2007, Ext JS, LLC.
18339  *
18340  * Originally Released Under LGPL - original licence link has changed is not relivant.
18341  *
18342  * Fork - LGPL
18343  * <script type="text/javascript">
18344  */
18345
18346
18347 /**
18348  * @class Roo.dd.DropTarget
18349  * @extends Roo.dd.DDTarget
18350  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18351  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18352  * @constructor
18353  * @param {String/HTMLElement/Element} el The container element
18354  * @param {Object} config
18355  */
18356 Roo.dd.DropTarget = function(el, config){
18357     this.el = Roo.get(el);
18358     
18359     var listeners = false; ;
18360     if (config && config.listeners) {
18361         listeners= config.listeners;
18362         delete config.listeners;
18363     }
18364     Roo.apply(this, config);
18365     
18366     if(this.containerScroll){
18367         Roo.dd.ScrollManager.register(this.el);
18368     }
18369     this.addEvents( {
18370          /**
18371          * @scope Roo.dd.DropTarget
18372          */
18373          
18374          /**
18375          * @event enter
18376          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18377          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18378          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18379          * 
18380          * IMPORTANT : it should set this.overClass and this.dropAllowed
18381          * 
18382          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18383          * @param {Event} e The event
18384          * @param {Object} data An object containing arbitrary data supplied by the drag source
18385          */
18386         "enter" : true,
18387         
18388          /**
18389          * @event over
18390          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18391          * This method will be called on every mouse movement while the drag source is over the drop target.
18392          * This default implementation simply returns the dropAllowed config value.
18393          * 
18394          * IMPORTANT : it should set this.dropAllowed
18395          * 
18396          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18397          * @param {Event} e The event
18398          * @param {Object} data An object containing arbitrary data supplied by the drag source
18399          
18400          */
18401         "over" : true,
18402         /**
18403          * @event out
18404          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18405          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18406          * overClass (if any) from the drop element.
18407          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18408          * @param {Event} e The event
18409          * @param {Object} data An object containing arbitrary data supplied by the drag source
18410          */
18411          "out" : true,
18412          
18413         /**
18414          * @event drop
18415          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18416          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18417          * implementation that does something to process the drop event and returns true so that the drag source's
18418          * repair action does not run.
18419          * 
18420          * IMPORTANT : it should set this.success
18421          * 
18422          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18423          * @param {Event} e The event
18424          * @param {Object} data An object containing arbitrary data supplied by the drag source
18425         */
18426          "drop" : true
18427     });
18428             
18429      
18430     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18431         this.el.dom, 
18432         this.ddGroup || this.group,
18433         {
18434             isTarget: true,
18435             listeners : listeners || {} 
18436            
18437         
18438         }
18439     );
18440
18441 };
18442
18443 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18444     /**
18445      * @cfg {String} overClass
18446      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18447      */
18448      /**
18449      * @cfg {String} ddGroup
18450      * The drag drop group to handle drop events for
18451      */
18452      
18453     /**
18454      * @cfg {String} dropAllowed
18455      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18456      */
18457     dropAllowed : "x-dd-drop-ok",
18458     /**
18459      * @cfg {String} dropNotAllowed
18460      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18461      */
18462     dropNotAllowed : "x-dd-drop-nodrop",
18463     /**
18464      * @cfg {boolean} success
18465      * set this after drop listener.. 
18466      */
18467     success : false,
18468     /**
18469      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18470      * if the drop point is valid for over/enter..
18471      */
18472     valid : false,
18473     // private
18474     isTarget : true,
18475
18476     // private
18477     isNotifyTarget : true,
18478     
18479     /**
18480      * @hide
18481      */
18482     notifyEnter : function(dd, e, data)
18483     {
18484         this.valid = true;
18485         this.fireEvent('enter', dd, e, data);
18486         if(this.overClass){
18487             this.el.addClass(this.overClass);
18488         }
18489         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18490             this.valid ? this.dropAllowed : this.dropNotAllowed
18491         );
18492     },
18493
18494     /**
18495      * @hide
18496      */
18497     notifyOver : function(dd, e, data)
18498     {
18499         this.valid = true;
18500         this.fireEvent('over', dd, e, data);
18501         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18502             this.valid ? this.dropAllowed : this.dropNotAllowed
18503         );
18504     },
18505
18506     /**
18507      * @hide
18508      */
18509     notifyOut : function(dd, e, data)
18510     {
18511         this.fireEvent('out', dd, e, data);
18512         if(this.overClass){
18513             this.el.removeClass(this.overClass);
18514         }
18515     },
18516
18517     /**
18518      * @hide
18519      */
18520     notifyDrop : function(dd, e, data)
18521     {
18522         this.success = false;
18523         this.fireEvent('drop', dd, e, data);
18524         return this.success;
18525     }
18526 });/*
18527  * Based on:
18528  * Ext JS Library 1.1.1
18529  * Copyright(c) 2006-2007, Ext JS, LLC.
18530  *
18531  * Originally Released Under LGPL - original licence link has changed is not relivant.
18532  *
18533  * Fork - LGPL
18534  * <script type="text/javascript">
18535  */
18536
18537
18538 /**
18539  * @class Roo.dd.DragZone
18540  * @extends Roo.dd.DragSource
18541  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18542  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18543  * @constructor
18544  * @param {String/HTMLElement/Element} el The container element
18545  * @param {Object} config
18546  */
18547 Roo.dd.DragZone = function(el, config){
18548     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18549     if(this.containerScroll){
18550         Roo.dd.ScrollManager.register(this.el);
18551     }
18552 };
18553
18554 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18555     /**
18556      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18557      * for auto scrolling during drag operations.
18558      */
18559     /**
18560      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18561      * method after a failed drop (defaults to "c3daf9" - light blue)
18562      */
18563
18564     /**
18565      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18566      * for a valid target to drag based on the mouse down. Override this method
18567      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18568      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18569      * @param {EventObject} e The mouse down event
18570      * @return {Object} The dragData
18571      */
18572     getDragData : function(e){
18573         return Roo.dd.Registry.getHandleFromEvent(e);
18574     },
18575     
18576     /**
18577      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18578      * this.dragData.ddel
18579      * @param {Number} x The x position of the click on the dragged object
18580      * @param {Number} y The y position of the click on the dragged object
18581      * @return {Boolean} true to continue the drag, false to cancel
18582      */
18583     onInitDrag : function(x, y){
18584         this.proxy.update(this.dragData.ddel.cloneNode(true));
18585         this.onStartDrag(x, y);
18586         return true;
18587     },
18588     
18589     /**
18590      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18591      */
18592     afterRepair : function(){
18593         if(Roo.enableFx){
18594             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18595         }
18596         this.dragging = false;
18597     },
18598
18599     /**
18600      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18601      * the XY of this.dragData.ddel
18602      * @param {EventObject} e The mouse up event
18603      * @return {Array} The xy location (e.g. [100, 200])
18604      */
18605     getRepairXY : function(e){
18606         return Roo.Element.fly(this.dragData.ddel).getXY();  
18607     }
18608 });/*
18609  * Based on:
18610  * Ext JS Library 1.1.1
18611  * Copyright(c) 2006-2007, Ext JS, LLC.
18612  *
18613  * Originally Released Under LGPL - original licence link has changed is not relivant.
18614  *
18615  * Fork - LGPL
18616  * <script type="text/javascript">
18617  */
18618 /**
18619  * @class Roo.dd.DropZone
18620  * @extends Roo.dd.DropTarget
18621  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18622  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18623  * @constructor
18624  * @param {String/HTMLElement/Element} el The container element
18625  * @param {Object} config
18626  */
18627 Roo.dd.DropZone = function(el, config){
18628     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18629 };
18630
18631 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18632     /**
18633      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18634      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18635      * provide your own custom lookup.
18636      * @param {Event} e The event
18637      * @return {Object} data The custom data
18638      */
18639     getTargetFromEvent : function(e){
18640         return Roo.dd.Registry.getTargetFromEvent(e);
18641     },
18642
18643     /**
18644      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18645      * that it has registered.  This method has no default implementation and should be overridden to provide
18646      * node-specific processing if necessary.
18647      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18648      * {@link #getTargetFromEvent} for this node)
18649      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18650      * @param {Event} e The event
18651      * @param {Object} data An object containing arbitrary data supplied by the drag source
18652      */
18653     onNodeEnter : function(n, dd, e, data){
18654         
18655     },
18656
18657     /**
18658      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18659      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18660      * overridden to provide the proper feedback.
18661      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18662      * {@link #getTargetFromEvent} for this node)
18663      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18664      * @param {Event} e The event
18665      * @param {Object} data An object containing arbitrary data supplied by the drag source
18666      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18667      * underlying {@link Roo.dd.StatusProxy} can be updated
18668      */
18669     onNodeOver : function(n, dd, e, data){
18670         return this.dropAllowed;
18671     },
18672
18673     /**
18674      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18675      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18676      * node-specific processing if necessary.
18677      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18678      * {@link #getTargetFromEvent} for this node)
18679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18680      * @param {Event} e The event
18681      * @param {Object} data An object containing arbitrary data supplied by the drag source
18682      */
18683     onNodeOut : function(n, dd, e, data){
18684         
18685     },
18686
18687     /**
18688      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18689      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18690      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18691      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18692      * {@link #getTargetFromEvent} for this node)
18693      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18694      * @param {Event} e The event
18695      * @param {Object} data An object containing arbitrary data supplied by the drag source
18696      * @return {Boolean} True if the drop was valid, else false
18697      */
18698     onNodeDrop : function(n, dd, e, data){
18699         return false;
18700     },
18701
18702     /**
18703      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18704      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18705      * it should be overridden to provide the proper feedback if necessary.
18706      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18707      * @param {Event} e The event
18708      * @param {Object} data An object containing arbitrary data supplied by the drag source
18709      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18710      * underlying {@link Roo.dd.StatusProxy} can be updated
18711      */
18712     onContainerOver : function(dd, e, data){
18713         return this.dropNotAllowed;
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18718      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18719      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18720      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18721      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18722      * @param {Event} e The event
18723      * @param {Object} data An object containing arbitrary data supplied by the drag source
18724      * @return {Boolean} True if the drop was valid, else false
18725      */
18726     onContainerDrop : function(dd, e, data){
18727         return false;
18728     },
18729
18730     /**
18731      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18732      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18733      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18734      * you should override this method and provide a custom implementation.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     notifyEnter : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18747      * This method will be called on every mouse movement while the drag source is over the drop zone.
18748      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18749      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18750      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18751      * registered node, it will call {@link #onContainerOver}.
18752      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18753      * @param {Event} e The event
18754      * @param {Object} data An object containing arbitrary data supplied by the drag source
18755      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18756      * underlying {@link Roo.dd.StatusProxy} can be updated
18757      */
18758     notifyOver : function(dd, e, data){
18759         var n = this.getTargetFromEvent(e);
18760         if(!n){ // not over valid drop target
18761             if(this.lastOverNode){
18762                 this.onNodeOut(this.lastOverNode, dd, e, data);
18763                 this.lastOverNode = null;
18764             }
18765             return this.onContainerOver(dd, e, data);
18766         }
18767         if(this.lastOverNode != n){
18768             if(this.lastOverNode){
18769                 this.onNodeOut(this.lastOverNode, dd, e, data);
18770             }
18771             this.onNodeEnter(n, dd, e, data);
18772             this.lastOverNode = n;
18773         }
18774         return this.onNodeOver(n, dd, e, data);
18775     },
18776
18777     /**
18778      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18779      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18780      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18784      */
18785     notifyOut : function(dd, e, data){
18786         if(this.lastOverNode){
18787             this.onNodeOut(this.lastOverNode, dd, e, data);
18788             this.lastOverNode = null;
18789         }
18790     },
18791
18792     /**
18793      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18794      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18795      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18796      * otherwise it will call {@link #onContainerDrop}.
18797      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18798      * @param {Event} e The event
18799      * @param {Object} data An object containing arbitrary data supplied by the drag source
18800      * @return {Boolean} True if the drop was valid, else false
18801      */
18802     notifyDrop : function(dd, e, data){
18803         if(this.lastOverNode){
18804             this.onNodeOut(this.lastOverNode, dd, e, data);
18805             this.lastOverNode = null;
18806         }
18807         var n = this.getTargetFromEvent(e);
18808         return n ?
18809             this.onNodeDrop(n, dd, e, data) :
18810             this.onContainerDrop(dd, e, data);
18811     },
18812
18813     // private
18814     triggerCacheRefresh : function(){
18815         Roo.dd.DDM.refreshCache(this.groups);
18816     }  
18817 });/*
18818  * Based on:
18819  * Ext JS Library 1.1.1
18820  * Copyright(c) 2006-2007, Ext JS, LLC.
18821  *
18822  * Originally Released Under LGPL - original licence link has changed is not relivant.
18823  *
18824  * Fork - LGPL
18825  * <script type="text/javascript">
18826  */
18827
18828
18829 /**
18830  * @class Roo.data.SortTypes
18831  * @singleton
18832  * Defines the default sorting (casting?) comparison functions used when sorting data.
18833  */
18834 Roo.data.SortTypes = {
18835     /**
18836      * Default sort that does nothing
18837      * @param {Mixed} s The value being converted
18838      * @return {Mixed} The comparison value
18839      */
18840     none : function(s){
18841         return s;
18842     },
18843     
18844     /**
18845      * The regular expression used to strip tags
18846      * @type {RegExp}
18847      * @property
18848      */
18849     stripTagsRE : /<\/?[^>]+>/gi,
18850     
18851     /**
18852      * Strips all HTML tags to sort on text only
18853      * @param {Mixed} s The value being converted
18854      * @return {String} The comparison value
18855      */
18856     asText : function(s){
18857         return String(s).replace(this.stripTagsRE, "");
18858     },
18859     
18860     /**
18861      * Strips all HTML tags to sort on text only - Case insensitive
18862      * @param {Mixed} s The value being converted
18863      * @return {String} The comparison value
18864      */
18865     asUCText : function(s){
18866         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18867     },
18868     
18869     /**
18870      * Case insensitive string
18871      * @param {Mixed} s The value being converted
18872      * @return {String} The comparison value
18873      */
18874     asUCString : function(s) {
18875         return String(s).toUpperCase();
18876     },
18877     
18878     /**
18879      * Date sorting
18880      * @param {Mixed} s The value being converted
18881      * @return {Number} The comparison value
18882      */
18883     asDate : function(s) {
18884         if(!s){
18885             return 0;
18886         }
18887         if(s instanceof Date){
18888             return s.getTime();
18889         }
18890         return Date.parse(String(s));
18891     },
18892     
18893     /**
18894      * Float sorting
18895      * @param {Mixed} s The value being converted
18896      * @return {Float} The comparison value
18897      */
18898     asFloat : function(s) {
18899         var val = parseFloat(String(s).replace(/,/g, ""));
18900         if(isNaN(val)) val = 0;
18901         return val;
18902     },
18903     
18904     /**
18905      * Integer sorting
18906      * @param {Mixed} s The value being converted
18907      * @return {Number} The comparison value
18908      */
18909     asInt : function(s) {
18910         var val = parseInt(String(s).replace(/,/g, ""));
18911         if(isNaN(val)) val = 0;
18912         return val;
18913     }
18914 };/*
18915  * Based on:
18916  * Ext JS Library 1.1.1
18917  * Copyright(c) 2006-2007, Ext JS, LLC.
18918  *
18919  * Originally Released Under LGPL - original licence link has changed is not relivant.
18920  *
18921  * Fork - LGPL
18922  * <script type="text/javascript">
18923  */
18924
18925 /**
18926 * @class Roo.data.Record
18927  * Instances of this class encapsulate both record <em>definition</em> information, and record
18928  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18929  * to access Records cached in an {@link Roo.data.Store} object.<br>
18930  * <p>
18931  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18932  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18933  * objects.<br>
18934  * <p>
18935  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18936  * @constructor
18937  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18938  * {@link #create}. The parameters are the same.
18939  * @param {Array} data An associative Array of data values keyed by the field name.
18940  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18941  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18942  * not specified an integer id is generated.
18943  */
18944 Roo.data.Record = function(data, id){
18945     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18946     this.data = data;
18947 };
18948
18949 /**
18950  * Generate a constructor for a specific record layout.
18951  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18952  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18953  * Each field definition object may contain the following properties: <ul>
18954  * <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,
18955  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18956  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18957  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18958  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18959  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18960  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18961  * this may be omitted.</p></li>
18962  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18963  * <ul><li>auto (Default, implies no conversion)</li>
18964  * <li>string</li>
18965  * <li>int</li>
18966  * <li>float</li>
18967  * <li>boolean</li>
18968  * <li>date</li></ul></p></li>
18969  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18970  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18971  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18972  * by the Reader into an object that will be stored in the Record. It is passed the
18973  * following parameters:<ul>
18974  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18975  * </ul></p></li>
18976  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18977  * </ul>
18978  * <br>usage:<br><pre><code>
18979 var TopicRecord = Roo.data.Record.create(
18980     {name: 'title', mapping: 'topic_title'},
18981     {name: 'author', mapping: 'username'},
18982     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18983     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18984     {name: 'lastPoster', mapping: 'user2'},
18985     {name: 'excerpt', mapping: 'post_text'}
18986 );
18987
18988 var myNewRecord = new TopicRecord({
18989     title: 'Do my job please',
18990     author: 'noobie',
18991     totalPosts: 1,
18992     lastPost: new Date(),
18993     lastPoster: 'Animal',
18994     excerpt: 'No way dude!'
18995 });
18996 myStore.add(myNewRecord);
18997 </code></pre>
18998  * @method create
18999  * @static
19000  */
19001 Roo.data.Record.create = function(o){
19002     var f = function(){
19003         f.superclass.constructor.apply(this, arguments);
19004     };
19005     Roo.extend(f, Roo.data.Record);
19006     var p = f.prototype;
19007     p.fields = new Roo.util.MixedCollection(false, function(field){
19008         return field.name;
19009     });
19010     for(var i = 0, len = o.length; i < len; i++){
19011         p.fields.add(new Roo.data.Field(o[i]));
19012     }
19013     f.getField = function(name){
19014         return p.fields.get(name);  
19015     };
19016     return f;
19017 };
19018
19019 Roo.data.Record.AUTO_ID = 1000;
19020 Roo.data.Record.EDIT = 'edit';
19021 Roo.data.Record.REJECT = 'reject';
19022 Roo.data.Record.COMMIT = 'commit';
19023
19024 Roo.data.Record.prototype = {
19025     /**
19026      * Readonly flag - true if this record has been modified.
19027      * @type Boolean
19028      */
19029     dirty : false,
19030     editing : false,
19031     error: null,
19032     modified: null,
19033
19034     // private
19035     join : function(store){
19036         this.store = store;
19037     },
19038
19039     /**
19040      * Set the named field to the specified value.
19041      * @param {String} name The name of the field to set.
19042      * @param {Object} value The value to set the field to.
19043      */
19044     set : function(name, value){
19045         if(this.data[name] == value){
19046             return;
19047         }
19048         this.dirty = true;
19049         if(!this.modified){
19050             this.modified = {};
19051         }
19052         if(typeof this.modified[name] == 'undefined'){
19053             this.modified[name] = this.data[name];
19054         }
19055         this.data[name] = value;
19056         if(!this.editing){
19057             this.store.afterEdit(this);
19058         }       
19059     },
19060
19061     /**
19062      * Get the value of the named field.
19063      * @param {String} name The name of the field to get the value of.
19064      * @return {Object} The value of the field.
19065      */
19066     get : function(name){
19067         return this.data[name]; 
19068     },
19069
19070     // private
19071     beginEdit : function(){
19072         this.editing = true;
19073         this.modified = {}; 
19074     },
19075
19076     // private
19077     cancelEdit : function(){
19078         this.editing = false;
19079         delete this.modified;
19080     },
19081
19082     // private
19083     endEdit : function(){
19084         this.editing = false;
19085         if(this.dirty && this.store){
19086             this.store.afterEdit(this);
19087         }
19088     },
19089
19090     /**
19091      * Usually called by the {@link Roo.data.Store} which owns the Record.
19092      * Rejects all changes made to the Record since either creation, or the last commit operation.
19093      * Modified fields are reverted to their original values.
19094      * <p>
19095      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19096      * of reject operations.
19097      */
19098     reject : function(){
19099         var m = this.modified;
19100         for(var n in m){
19101             if(typeof m[n] != "function"){
19102                 this.data[n] = m[n];
19103             }
19104         }
19105         this.dirty = false;
19106         delete this.modified;
19107         this.editing = false;
19108         if(this.store){
19109             this.store.afterReject(this);
19110         }
19111     },
19112
19113     /**
19114      * Usually called by the {@link Roo.data.Store} which owns the Record.
19115      * Commits all changes made to the Record since either creation, or the last commit operation.
19116      * <p>
19117      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19118      * of commit operations.
19119      */
19120     commit : function(){
19121         this.dirty = false;
19122         delete this.modified;
19123         this.editing = false;
19124         if(this.store){
19125             this.store.afterCommit(this);
19126         }
19127     },
19128
19129     // private
19130     hasError : function(){
19131         return this.error != null;
19132     },
19133
19134     // private
19135     clearError : function(){
19136         this.error = null;
19137     },
19138
19139     /**
19140      * Creates a copy of this record.
19141      * @param {String} id (optional) A new record id if you don't want to use this record's id
19142      * @return {Record}
19143      */
19144     copy : function(newId) {
19145         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19146     }
19147 };/*
19148  * Based on:
19149  * Ext JS Library 1.1.1
19150  * Copyright(c) 2006-2007, Ext JS, LLC.
19151  *
19152  * Originally Released Under LGPL - original licence link has changed is not relivant.
19153  *
19154  * Fork - LGPL
19155  * <script type="text/javascript">
19156  */
19157
19158
19159
19160 /**
19161  * @class Roo.data.Store
19162  * @extends Roo.util.Observable
19163  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19164  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19165  * <p>
19166  * 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
19167  * has no knowledge of the format of the data returned by the Proxy.<br>
19168  * <p>
19169  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19170  * instances from the data object. These records are cached and made available through accessor functions.
19171  * @constructor
19172  * Creates a new Store.
19173  * @param {Object} config A config object containing the objects needed for the Store to access data,
19174  * and read the data into Records.
19175  */
19176 Roo.data.Store = function(config){
19177     this.data = new Roo.util.MixedCollection(false);
19178     this.data.getKey = function(o){
19179         return o.id;
19180     };
19181     this.baseParams = {};
19182     // private
19183     this.paramNames = {
19184         "start" : "start",
19185         "limit" : "limit",
19186         "sort" : "sort",
19187         "dir" : "dir",
19188         "multisort" : "_multisort"
19189     };
19190
19191     if(config && config.data){
19192         this.inlineData = config.data;
19193         delete config.data;
19194     }
19195
19196     Roo.apply(this, config);
19197     
19198     if(this.reader){ // reader passed
19199         this.reader = Roo.factory(this.reader, Roo.data);
19200         this.reader.xmodule = this.xmodule || false;
19201         if(!this.recordType){
19202             this.recordType = this.reader.recordType;
19203         }
19204         if(this.reader.onMetaChange){
19205             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19206         }
19207     }
19208
19209     if(this.recordType){
19210         this.fields = this.recordType.prototype.fields;
19211     }
19212     this.modified = [];
19213
19214     this.addEvents({
19215         /**
19216          * @event datachanged
19217          * Fires when the data cache has changed, and a widget which is using this Store
19218          * as a Record cache should refresh its view.
19219          * @param {Store} this
19220          */
19221         datachanged : true,
19222         /**
19223          * @event metachange
19224          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19225          * @param {Store} this
19226          * @param {Object} meta The JSON metadata
19227          */
19228         metachange : true,
19229         /**
19230          * @event add
19231          * Fires when Records have been added to the Store
19232          * @param {Store} this
19233          * @param {Roo.data.Record[]} records The array of Records added
19234          * @param {Number} index The index at which the record(s) were added
19235          */
19236         add : true,
19237         /**
19238          * @event remove
19239          * Fires when a Record has been removed from the Store
19240          * @param {Store} this
19241          * @param {Roo.data.Record} record The Record that was removed
19242          * @param {Number} index The index at which the record was removed
19243          */
19244         remove : true,
19245         /**
19246          * @event update
19247          * Fires when a Record has been updated
19248          * @param {Store} this
19249          * @param {Roo.data.Record} record The Record that was updated
19250          * @param {String} operation The update operation being performed.  Value may be one of:
19251          * <pre><code>
19252  Roo.data.Record.EDIT
19253  Roo.data.Record.REJECT
19254  Roo.data.Record.COMMIT
19255          * </code></pre>
19256          */
19257         update : true,
19258         /**
19259          * @event clear
19260          * Fires when the data cache has been cleared.
19261          * @param {Store} this
19262          */
19263         clear : true,
19264         /**
19265          * @event beforeload
19266          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19267          * the load action will be canceled.
19268          * @param {Store} this
19269          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19270          */
19271         beforeload : true,
19272         /**
19273          * @event load
19274          * Fires after a new set of Records has been loaded.
19275          * @param {Store} this
19276          * @param {Roo.data.Record[]} records The Records that were loaded
19277          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19278          */
19279         load : true,
19280         /**
19281          * @event loadexception
19282          * Fires if an exception occurs in the Proxy during loading.
19283          * Called with the signature of the Proxy's "loadexception" event.
19284          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19285          * 
19286          * @param {Proxy} 
19287          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19288          * @param {Object} load options 
19289          * @param {Object} jsonData from your request (normally this contains the Exception)
19290          */
19291         loadexception : true
19292     });
19293     
19294     if(this.proxy){
19295         this.proxy = Roo.factory(this.proxy, Roo.data);
19296         this.proxy.xmodule = this.xmodule || false;
19297         this.relayEvents(this.proxy,  ["loadexception"]);
19298     }
19299     this.sortToggle = {};
19300     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19301
19302     Roo.data.Store.superclass.constructor.call(this);
19303
19304     if(this.inlineData){
19305         this.loadData(this.inlineData);
19306         delete this.inlineData;
19307     }
19308 };
19309 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19310      /**
19311     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19312     * without a remote query - used by combo/forms at present.
19313     */
19314     
19315     /**
19316     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19317     */
19318     /**
19319     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19320     */
19321     /**
19322     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19323     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19324     */
19325     /**
19326     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19327     * on any HTTP request
19328     */
19329     /**
19330     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19331     */
19332     /**
19333     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19334     */
19335     multiSort: false,
19336     /**
19337     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19338     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19339     */
19340     remoteSort : false,
19341
19342     /**
19343     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19344      * loaded or when a record is removed. (defaults to false).
19345     */
19346     pruneModifiedRecords : false,
19347
19348     // private
19349     lastOptions : null,
19350
19351     /**
19352      * Add Records to the Store and fires the add event.
19353      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19354      */
19355     add : function(records){
19356         records = [].concat(records);
19357         for(var i = 0, len = records.length; i < len; i++){
19358             records[i].join(this);
19359         }
19360         var index = this.data.length;
19361         this.data.addAll(records);
19362         this.fireEvent("add", this, records, index);
19363     },
19364
19365     /**
19366      * Remove a Record from the Store and fires the remove event.
19367      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19368      */
19369     remove : function(record){
19370         var index = this.data.indexOf(record);
19371         this.data.removeAt(index);
19372         if(this.pruneModifiedRecords){
19373             this.modified.remove(record);
19374         }
19375         this.fireEvent("remove", this, record, index);
19376     },
19377
19378     /**
19379      * Remove all Records from the Store and fires the clear event.
19380      */
19381     removeAll : function(){
19382         this.data.clear();
19383         if(this.pruneModifiedRecords){
19384             this.modified = [];
19385         }
19386         this.fireEvent("clear", this);
19387     },
19388
19389     /**
19390      * Inserts Records to the Store at the given index and fires the add event.
19391      * @param {Number} index The start index at which to insert the passed Records.
19392      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19393      */
19394     insert : function(index, records){
19395         records = [].concat(records);
19396         for(var i = 0, len = records.length; i < len; i++){
19397             this.data.insert(index, records[i]);
19398             records[i].join(this);
19399         }
19400         this.fireEvent("add", this, records, index);
19401     },
19402
19403     /**
19404      * Get the index within the cache of the passed Record.
19405      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19406      * @return {Number} The index of the passed Record. Returns -1 if not found.
19407      */
19408     indexOf : function(record){
19409         return this.data.indexOf(record);
19410     },
19411
19412     /**
19413      * Get the index within the cache of the Record with the passed id.
19414      * @param {String} id The id of the Record to find.
19415      * @return {Number} The index of the Record. Returns -1 if not found.
19416      */
19417     indexOfId : function(id){
19418         return this.data.indexOfKey(id);
19419     },
19420
19421     /**
19422      * Get the Record with the specified id.
19423      * @param {String} id The id of the Record to find.
19424      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19425      */
19426     getById : function(id){
19427         return this.data.key(id);
19428     },
19429
19430     /**
19431      * Get the Record at the specified index.
19432      * @param {Number} index The index of the Record to find.
19433      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19434      */
19435     getAt : function(index){
19436         return this.data.itemAt(index);
19437     },
19438
19439     /**
19440      * Returns a range of Records between specified indices.
19441      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19442      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19443      * @return {Roo.data.Record[]} An array of Records
19444      */
19445     getRange : function(start, end){
19446         return this.data.getRange(start, end);
19447     },
19448
19449     // private
19450     storeOptions : function(o){
19451         o = Roo.apply({}, o);
19452         delete o.callback;
19453         delete o.scope;
19454         this.lastOptions = o;
19455     },
19456
19457     /**
19458      * Loads the Record cache from the configured Proxy using the configured Reader.
19459      * <p>
19460      * If using remote paging, then the first load call must specify the <em>start</em>
19461      * and <em>limit</em> properties in the options.params property to establish the initial
19462      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19463      * <p>
19464      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19465      * and this call will return before the new data has been loaded. Perform any post-processing
19466      * in a callback function, or in a "load" event handler.</strong>
19467      * <p>
19468      * @param {Object} options An object containing properties which control loading options:<ul>
19469      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19470      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19471      * passed the following arguments:<ul>
19472      * <li>r : Roo.data.Record[]</li>
19473      * <li>options: Options object from the load call</li>
19474      * <li>success: Boolean success indicator</li></ul></li>
19475      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19476      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19477      * </ul>
19478      */
19479     load : function(options){
19480         options = options || {};
19481         if(this.fireEvent("beforeload", this, options) !== false){
19482             this.storeOptions(options);
19483             var p = Roo.apply(options.params || {}, this.baseParams);
19484             // if meta was not loaded from remote source.. try requesting it.
19485             if (!this.reader.metaFromRemote) {
19486                 p._requestMeta = 1;
19487             }
19488             if(this.sortInfo && this.remoteSort){
19489                 var pn = this.paramNames;
19490                 p[pn["sort"]] = this.sortInfo.field;
19491                 p[pn["dir"]] = this.sortInfo.direction;
19492             }
19493             if (this.multiSort) {
19494                 var pn = this.paramNames;
19495                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19496             }
19497             
19498             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19499         }
19500     },
19501
19502     /**
19503      * Reloads the Record cache from the configured Proxy using the configured Reader and
19504      * the options from the last load operation performed.
19505      * @param {Object} options (optional) An object containing properties which may override the options
19506      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19507      * the most recently used options are reused).
19508      */
19509     reload : function(options){
19510         this.load(Roo.applyIf(options||{}, this.lastOptions));
19511     },
19512
19513     // private
19514     // Called as a callback by the Reader during a load operation.
19515     loadRecords : function(o, options, success){
19516         if(!o || success === false){
19517             if(success !== false){
19518                 this.fireEvent("load", this, [], options);
19519             }
19520             if(options.callback){
19521                 options.callback.call(options.scope || this, [], options, false);
19522             }
19523             return;
19524         }
19525         // if data returned failure - throw an exception.
19526         if (o.success === false) {
19527             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19528             return;
19529         }
19530         var r = o.records, t = o.totalRecords || r.length;
19531         if(!options || options.add !== true){
19532             if(this.pruneModifiedRecords){
19533                 this.modified = [];
19534             }
19535             for(var i = 0, len = r.length; i < len; i++){
19536                 r[i].join(this);
19537             }
19538             if(this.snapshot){
19539                 this.data = this.snapshot;
19540                 delete this.snapshot;
19541             }
19542             this.data.clear();
19543             this.data.addAll(r);
19544             this.totalLength = t;
19545             this.applySort();
19546             this.fireEvent("datachanged", this);
19547         }else{
19548             this.totalLength = Math.max(t, this.data.length+r.length);
19549             this.add(r);
19550         }
19551         this.fireEvent("load", this, r, options);
19552         if(options.callback){
19553             options.callback.call(options.scope || this, r, options, true);
19554         }
19555     },
19556
19557     /**
19558      * Loads data from a passed data block. A Reader which understands the format of the data
19559      * must have been configured in the constructor.
19560      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19561      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19562      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19563      */
19564     loadData : function(o, append){
19565         var r = this.reader.readRecords(o);
19566         this.loadRecords(r, {add: append}, true);
19567     },
19568
19569     /**
19570      * Gets the number of cached records.
19571      * <p>
19572      * <em>If using paging, this may not be the total size of the dataset. If the data object
19573      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19574      * the data set size</em>
19575      */
19576     getCount : function(){
19577         return this.data.length || 0;
19578     },
19579
19580     /**
19581      * Gets the total number of records in the dataset as returned by the server.
19582      * <p>
19583      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19584      * the dataset size</em>
19585      */
19586     getTotalCount : function(){
19587         return this.totalLength || 0;
19588     },
19589
19590     /**
19591      * Returns the sort state of the Store as an object with two properties:
19592      * <pre><code>
19593  field {String} The name of the field by which the Records are sorted
19594  direction {String} The sort order, "ASC" or "DESC"
19595      * </code></pre>
19596      */
19597     getSortState : function(){
19598         return this.sortInfo;
19599     },
19600
19601     // private
19602     applySort : function(){
19603         if(this.sortInfo && !this.remoteSort){
19604             var s = this.sortInfo, f = s.field;
19605             var st = this.fields.get(f).sortType;
19606             var fn = function(r1, r2){
19607                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19608                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19609             };
19610             this.data.sort(s.direction, fn);
19611             if(this.snapshot && this.snapshot != this.data){
19612                 this.snapshot.sort(s.direction, fn);
19613             }
19614         }
19615     },
19616
19617     /**
19618      * Sets the default sort column and order to be used by the next load operation.
19619      * @param {String} fieldName The name of the field to sort by.
19620      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19621      */
19622     setDefaultSort : function(field, dir){
19623         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19624     },
19625
19626     /**
19627      * Sort the Records.
19628      * If remote sorting is used, the sort is performed on the server, and the cache is
19629      * reloaded. If local sorting is used, the cache is sorted internally.
19630      * @param {String} fieldName The name of the field to sort by.
19631      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19632      */
19633     sort : function(fieldName, dir){
19634         var f = this.fields.get(fieldName);
19635         if(!dir){
19636             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19637             
19638             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19639                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19640             }else{
19641                 dir = f.sortDir;
19642             }
19643         }
19644         this.sortToggle[f.name] = dir;
19645         this.sortInfo = {field: f.name, direction: dir};
19646         if(!this.remoteSort){
19647             this.applySort();
19648             this.fireEvent("datachanged", this);
19649         }else{
19650             this.load(this.lastOptions);
19651         }
19652     },
19653
19654     /**
19655      * Calls the specified function for each of the Records in the cache.
19656      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19657      * Returning <em>false</em> aborts and exits the iteration.
19658      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19659      */
19660     each : function(fn, scope){
19661         this.data.each(fn, scope);
19662     },
19663
19664     /**
19665      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19666      * (e.g., during paging).
19667      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19668      */
19669     getModifiedRecords : function(){
19670         return this.modified;
19671     },
19672
19673     // private
19674     createFilterFn : function(property, value, anyMatch){
19675         if(!value.exec){ // not a regex
19676             value = String(value);
19677             if(value.length == 0){
19678                 return false;
19679             }
19680             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19681         }
19682         return function(r){
19683             return value.test(r.data[property]);
19684         };
19685     },
19686
19687     /**
19688      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19689      * @param {String} property A field on your records
19690      * @param {Number} start The record index to start at (defaults to 0)
19691      * @param {Number} end The last record index to include (defaults to length - 1)
19692      * @return {Number} The sum
19693      */
19694     sum : function(property, start, end){
19695         var rs = this.data.items, v = 0;
19696         start = start || 0;
19697         end = (end || end === 0) ? end : rs.length-1;
19698
19699         for(var i = start; i <= end; i++){
19700             v += (rs[i].data[property] || 0);
19701         }
19702         return v;
19703     },
19704
19705     /**
19706      * Filter the records by a specified property.
19707      * @param {String} field A field on your records
19708      * @param {String/RegExp} value Either a string that the field
19709      * should start with or a RegExp to test against the field
19710      * @param {Boolean} anyMatch True to match any part not just the beginning
19711      */
19712     filter : function(property, value, anyMatch){
19713         var fn = this.createFilterFn(property, value, anyMatch);
19714         return fn ? this.filterBy(fn) : this.clearFilter();
19715     },
19716
19717     /**
19718      * Filter by a function. The specified function will be called with each
19719      * record in this data source. If the function returns true the record is included,
19720      * otherwise it is filtered.
19721      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19722      * @param {Object} scope (optional) The scope of the function (defaults to this)
19723      */
19724     filterBy : function(fn, scope){
19725         this.snapshot = this.snapshot || this.data;
19726         this.data = this.queryBy(fn, scope||this);
19727         this.fireEvent("datachanged", this);
19728     },
19729
19730     /**
19731      * Query the records by a specified property.
19732      * @param {String} field A field on your records
19733      * @param {String/RegExp} value Either a string that the field
19734      * should start with or a RegExp to test against the field
19735      * @param {Boolean} anyMatch True to match any part not just the beginning
19736      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19737      */
19738     query : function(property, value, anyMatch){
19739         var fn = this.createFilterFn(property, value, anyMatch);
19740         return fn ? this.queryBy(fn) : this.data.clone();
19741     },
19742
19743     /**
19744      * Query by a function. The specified function will be called with each
19745      * record in this data source. If the function returns true the record is included
19746      * in the results.
19747      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19748      * @param {Object} scope (optional) The scope of the function (defaults to this)
19749       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19750      **/
19751     queryBy : function(fn, scope){
19752         var data = this.snapshot || this.data;
19753         return data.filterBy(fn, scope||this);
19754     },
19755
19756     /**
19757      * Collects unique values for a particular dataIndex from this store.
19758      * @param {String} dataIndex The property to collect
19759      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19760      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19761      * @return {Array} An array of the unique values
19762      **/
19763     collect : function(dataIndex, allowNull, bypassFilter){
19764         var d = (bypassFilter === true && this.snapshot) ?
19765                 this.snapshot.items : this.data.items;
19766         var v, sv, r = [], l = {};
19767         for(var i = 0, len = d.length; i < len; i++){
19768             v = d[i].data[dataIndex];
19769             sv = String(v);
19770             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19771                 l[sv] = true;
19772                 r[r.length] = v;
19773             }
19774         }
19775         return r;
19776     },
19777
19778     /**
19779      * Revert to a view of the Record cache with no filtering applied.
19780      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19781      */
19782     clearFilter : function(suppressEvent){
19783         if(this.snapshot && this.snapshot != this.data){
19784             this.data = this.snapshot;
19785             delete this.snapshot;
19786             if(suppressEvent !== true){
19787                 this.fireEvent("datachanged", this);
19788             }
19789         }
19790     },
19791
19792     // private
19793     afterEdit : function(record){
19794         if(this.modified.indexOf(record) == -1){
19795             this.modified.push(record);
19796         }
19797         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19798     },
19799
19800     // private
19801     afterReject : function(record){
19802         this.modified.remove(record);
19803         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19804     },
19805
19806     // private
19807     afterCommit : function(record){
19808         this.modified.remove(record);
19809         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19810     },
19811
19812     /**
19813      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19814      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19815      */
19816     commitChanges : function(){
19817         var m = this.modified.slice(0);
19818         this.modified = [];
19819         for(var i = 0, len = m.length; i < len; i++){
19820             m[i].commit();
19821         }
19822     },
19823
19824     /**
19825      * Cancel outstanding changes on all changed records.
19826      */
19827     rejectChanges : function(){
19828         var m = this.modified.slice(0);
19829         this.modified = [];
19830         for(var i = 0, len = m.length; i < len; i++){
19831             m[i].reject();
19832         }
19833     },
19834
19835     onMetaChange : function(meta, rtype, o){
19836         this.recordType = rtype;
19837         this.fields = rtype.prototype.fields;
19838         delete this.snapshot;
19839         this.sortInfo = meta.sortInfo || this.sortInfo;
19840         this.modified = [];
19841         this.fireEvent('metachange', this, this.reader.meta);
19842     }
19843 });/*
19844  * Based on:
19845  * Ext JS Library 1.1.1
19846  * Copyright(c) 2006-2007, Ext JS, LLC.
19847  *
19848  * Originally Released Under LGPL - original licence link has changed is not relivant.
19849  *
19850  * Fork - LGPL
19851  * <script type="text/javascript">
19852  */
19853
19854 /**
19855  * @class Roo.data.SimpleStore
19856  * @extends Roo.data.Store
19857  * Small helper class to make creating Stores from Array data easier.
19858  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19859  * @cfg {Array} fields An array of field definition objects, or field name strings.
19860  * @cfg {Array} data The multi-dimensional array of data
19861  * @constructor
19862  * @param {Object} config
19863  */
19864 Roo.data.SimpleStore = function(config){
19865     Roo.data.SimpleStore.superclass.constructor.call(this, {
19866         isLocal : true,
19867         reader: new Roo.data.ArrayReader({
19868                 id: config.id
19869             },
19870             Roo.data.Record.create(config.fields)
19871         ),
19872         proxy : new Roo.data.MemoryProxy(config.data)
19873     });
19874     this.load();
19875 };
19876 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19877  * Based on:
19878  * Ext JS Library 1.1.1
19879  * Copyright(c) 2006-2007, Ext JS, LLC.
19880  *
19881  * Originally Released Under LGPL - original licence link has changed is not relivant.
19882  *
19883  * Fork - LGPL
19884  * <script type="text/javascript">
19885  */
19886
19887 /**
19888 /**
19889  * @extends Roo.data.Store
19890  * @class Roo.data.JsonStore
19891  * Small helper class to make creating Stores for JSON data easier. <br/>
19892 <pre><code>
19893 var store = new Roo.data.JsonStore({
19894     url: 'get-images.php',
19895     root: 'images',
19896     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19897 });
19898 </code></pre>
19899  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19900  * JsonReader and HttpProxy (unless inline data is provided).</b>
19901  * @cfg {Array} fields An array of field definition objects, or field name strings.
19902  * @constructor
19903  * @param {Object} config
19904  */
19905 Roo.data.JsonStore = function(c){
19906     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19907         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19908         reader: new Roo.data.JsonReader(c, c.fields)
19909     }));
19910 };
19911 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19912  * Based on:
19913  * Ext JS Library 1.1.1
19914  * Copyright(c) 2006-2007, Ext JS, LLC.
19915  *
19916  * Originally Released Under LGPL - original licence link has changed is not relivant.
19917  *
19918  * Fork - LGPL
19919  * <script type="text/javascript">
19920  */
19921
19922  
19923 Roo.data.Field = function(config){
19924     if(typeof config == "string"){
19925         config = {name: config};
19926     }
19927     Roo.apply(this, config);
19928     
19929     if(!this.type){
19930         this.type = "auto";
19931     }
19932     
19933     var st = Roo.data.SortTypes;
19934     // named sortTypes are supported, here we look them up
19935     if(typeof this.sortType == "string"){
19936         this.sortType = st[this.sortType];
19937     }
19938     
19939     // set default sortType for strings and dates
19940     if(!this.sortType){
19941         switch(this.type){
19942             case "string":
19943                 this.sortType = st.asUCString;
19944                 break;
19945             case "date":
19946                 this.sortType = st.asDate;
19947                 break;
19948             default:
19949                 this.sortType = st.none;
19950         }
19951     }
19952
19953     // define once
19954     var stripRe = /[\$,%]/g;
19955
19956     // prebuilt conversion function for this field, instead of
19957     // switching every time we're reading a value
19958     if(!this.convert){
19959         var cv, dateFormat = this.dateFormat;
19960         switch(this.type){
19961             case "":
19962             case "auto":
19963             case undefined:
19964                 cv = function(v){ return v; };
19965                 break;
19966             case "string":
19967                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19968                 break;
19969             case "int":
19970                 cv = function(v){
19971                     return v !== undefined && v !== null && v !== '' ?
19972                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19973                     };
19974                 break;
19975             case "float":
19976                 cv = function(v){
19977                     return v !== undefined && v !== null && v !== '' ?
19978                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19979                     };
19980                 break;
19981             case "bool":
19982             case "boolean":
19983                 cv = function(v){ return v === true || v === "true" || v == 1; };
19984                 break;
19985             case "date":
19986                 cv = function(v){
19987                     if(!v){
19988                         return '';
19989                     }
19990                     if(v instanceof Date){
19991                         return v;
19992                     }
19993                     if(dateFormat){
19994                         if(dateFormat == "timestamp"){
19995                             return new Date(v*1000);
19996                         }
19997                         return Date.parseDate(v, dateFormat);
19998                     }
19999                     var parsed = Date.parse(v);
20000                     return parsed ? new Date(parsed) : null;
20001                 };
20002              break;
20003             
20004         }
20005         this.convert = cv;
20006     }
20007 };
20008
20009 Roo.data.Field.prototype = {
20010     dateFormat: null,
20011     defaultValue: "",
20012     mapping: null,
20013     sortType : null,
20014     sortDir : "ASC"
20015 };/*
20016  * Based on:
20017  * Ext JS Library 1.1.1
20018  * Copyright(c) 2006-2007, Ext JS, LLC.
20019  *
20020  * Originally Released Under LGPL - original licence link has changed is not relivant.
20021  *
20022  * Fork - LGPL
20023  * <script type="text/javascript">
20024  */
20025  
20026 // Base class for reading structured data from a data source.  This class is intended to be
20027 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20028
20029 /**
20030  * @class Roo.data.DataReader
20031  * Base class for reading structured data from a data source.  This class is intended to be
20032  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20033  */
20034
20035 Roo.data.DataReader = function(meta, recordType){
20036     
20037     this.meta = meta;
20038     
20039     this.recordType = recordType instanceof Array ? 
20040         Roo.data.Record.create(recordType) : recordType;
20041 };
20042
20043 Roo.data.DataReader.prototype = {
20044      /**
20045      * Create an empty record
20046      * @param {Object} data (optional) - overlay some values
20047      * @return {Roo.data.Record} record created.
20048      */
20049     newRow :  function(d) {
20050         var da =  {};
20051         this.recordType.prototype.fields.each(function(c) {
20052             switch( c.type) {
20053                 case 'int' : da[c.name] = 0; break;
20054                 case 'date' : da[c.name] = new Date(); break;
20055                 case 'float' : da[c.name] = 0.0; break;
20056                 case 'boolean' : da[c.name] = false; break;
20057                 default : da[c.name] = ""; break;
20058             }
20059             
20060         });
20061         return new this.recordType(Roo.apply(da, d));
20062     }
20063     
20064 };/*
20065  * Based on:
20066  * Ext JS Library 1.1.1
20067  * Copyright(c) 2006-2007, Ext JS, LLC.
20068  *
20069  * Originally Released Under LGPL - original licence link has changed is not relivant.
20070  *
20071  * Fork - LGPL
20072  * <script type="text/javascript">
20073  */
20074
20075 /**
20076  * @class Roo.data.DataProxy
20077  * @extends Roo.data.Observable
20078  * This class is an abstract base class for implementations which provide retrieval of
20079  * unformatted data objects.<br>
20080  * <p>
20081  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20082  * (of the appropriate type which knows how to parse the data object) to provide a block of
20083  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20084  * <p>
20085  * Custom implementations must implement the load method as described in
20086  * {@link Roo.data.HttpProxy#load}.
20087  */
20088 Roo.data.DataProxy = function(){
20089     this.addEvents({
20090         /**
20091          * @event beforeload
20092          * Fires before a network request is made to retrieve a data object.
20093          * @param {Object} This DataProxy object.
20094          * @param {Object} params The params parameter to the load function.
20095          */
20096         beforeload : true,
20097         /**
20098          * @event load
20099          * Fires before the load method's callback is called.
20100          * @param {Object} This DataProxy object.
20101          * @param {Object} o The data object.
20102          * @param {Object} arg The callback argument object passed to the load function.
20103          */
20104         load : true,
20105         /**
20106          * @event loadexception
20107          * Fires if an Exception occurs during data retrieval.
20108          * @param {Object} This DataProxy object.
20109          * @param {Object} o The data object.
20110          * @param {Object} arg The callback argument object passed to the load function.
20111          * @param {Object} e The Exception.
20112          */
20113         loadexception : true
20114     });
20115     Roo.data.DataProxy.superclass.constructor.call(this);
20116 };
20117
20118 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20119
20120     /**
20121      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20122      */
20123 /*
20124  * Based on:
20125  * Ext JS Library 1.1.1
20126  * Copyright(c) 2006-2007, Ext JS, LLC.
20127  *
20128  * Originally Released Under LGPL - original licence link has changed is not relivant.
20129  *
20130  * Fork - LGPL
20131  * <script type="text/javascript">
20132  */
20133 /**
20134  * @class Roo.data.MemoryProxy
20135  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20136  * to the Reader when its load method is called.
20137  * @constructor
20138  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20139  */
20140 Roo.data.MemoryProxy = function(data){
20141     if (data.data) {
20142         data = data.data;
20143     }
20144     Roo.data.MemoryProxy.superclass.constructor.call(this);
20145     this.data = data;
20146 };
20147
20148 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20149     /**
20150      * Load data from the requested source (in this case an in-memory
20151      * data object passed to the constructor), read the data object into
20152      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20153      * process that block using the passed callback.
20154      * @param {Object} params This parameter is not used by the MemoryProxy class.
20155      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20156      * object into a block of Roo.data.Records.
20157      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20158      * The function must be passed <ul>
20159      * <li>The Record block object</li>
20160      * <li>The "arg" argument from the load function</li>
20161      * <li>A boolean success indicator</li>
20162      * </ul>
20163      * @param {Object} scope The scope in which to call the callback
20164      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20165      */
20166     load : function(params, reader, callback, scope, arg){
20167         params = params || {};
20168         var result;
20169         try {
20170             result = reader.readRecords(this.data);
20171         }catch(e){
20172             this.fireEvent("loadexception", this, arg, null, e);
20173             callback.call(scope, null, arg, false);
20174             return;
20175         }
20176         callback.call(scope, result, arg, true);
20177     },
20178     
20179     // private
20180     update : function(params, records){
20181         
20182     }
20183 });/*
20184  * Based on:
20185  * Ext JS Library 1.1.1
20186  * Copyright(c) 2006-2007, Ext JS, LLC.
20187  *
20188  * Originally Released Under LGPL - original licence link has changed is not relivant.
20189  *
20190  * Fork - LGPL
20191  * <script type="text/javascript">
20192  */
20193 /**
20194  * @class Roo.data.HttpProxy
20195  * @extends Roo.data.DataProxy
20196  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20197  * configured to reference a certain URL.<br><br>
20198  * <p>
20199  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20200  * from which the running page was served.<br><br>
20201  * <p>
20202  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20203  * <p>
20204  * Be aware that to enable the browser to parse an XML document, the server must set
20205  * the Content-Type header in the HTTP response to "text/xml".
20206  * @constructor
20207  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20208  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20209  * will be used to make the request.
20210  */
20211 Roo.data.HttpProxy = function(conn){
20212     Roo.data.HttpProxy.superclass.constructor.call(this);
20213     // is conn a conn config or a real conn?
20214     this.conn = conn;
20215     this.useAjax = !conn || !conn.events;
20216   
20217 };
20218
20219 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20220     // thse are take from connection...
20221     
20222     /**
20223      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20224      */
20225     /**
20226      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20227      * extra parameters to each request made by this object. (defaults to undefined)
20228      */
20229     /**
20230      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20231      *  to each request made by this object. (defaults to undefined)
20232      */
20233     /**
20234      * @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)
20235      */
20236     /**
20237      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20238      */
20239      /**
20240      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20241      * @type Boolean
20242      */
20243   
20244
20245     /**
20246      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20247      * @type Boolean
20248      */
20249     /**
20250      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20251      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20252      * a finer-grained basis than the DataProxy events.
20253      */
20254     getConnection : function(){
20255         return this.useAjax ? Roo.Ajax : this.conn;
20256     },
20257
20258     /**
20259      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20260      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20261      * process that block using the passed callback.
20262      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20263      * for the request to the remote server.
20264      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20265      * object into a block of Roo.data.Records.
20266      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20267      * The function must be passed <ul>
20268      * <li>The Record block object</li>
20269      * <li>The "arg" argument from the load function</li>
20270      * <li>A boolean success indicator</li>
20271      * </ul>
20272      * @param {Object} scope The scope in which to call the callback
20273      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20274      */
20275     load : function(params, reader, callback, scope, arg){
20276         if(this.fireEvent("beforeload", this, params) !== false){
20277             var  o = {
20278                 params : params || {},
20279                 request: {
20280                     callback : callback,
20281                     scope : scope,
20282                     arg : arg
20283                 },
20284                 reader: reader,
20285                 callback : this.loadResponse,
20286                 scope: this
20287             };
20288             if(this.useAjax){
20289                 Roo.applyIf(o, this.conn);
20290                 if(this.activeRequest){
20291                     Roo.Ajax.abort(this.activeRequest);
20292                 }
20293                 this.activeRequest = Roo.Ajax.request(o);
20294             }else{
20295                 this.conn.request(o);
20296             }
20297         }else{
20298             callback.call(scope||this, null, arg, false);
20299         }
20300     },
20301
20302     // private
20303     loadResponse : function(o, success, response){
20304         delete this.activeRequest;
20305         if(!success){
20306             this.fireEvent("loadexception", this, o, response);
20307             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20308             return;
20309         }
20310         var result;
20311         try {
20312             result = o.reader.read(response);
20313         }catch(e){
20314             this.fireEvent("loadexception", this, o, response, e);
20315             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20316             return;
20317         }
20318         
20319         this.fireEvent("load", this, o, o.request.arg);
20320         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20321     },
20322
20323     // private
20324     update : function(dataSet){
20325
20326     },
20327
20328     // private
20329     updateResponse : function(dataSet){
20330
20331     }
20332 });/*
20333  * Based on:
20334  * Ext JS Library 1.1.1
20335  * Copyright(c) 2006-2007, Ext JS, LLC.
20336  *
20337  * Originally Released Under LGPL - original licence link has changed is not relivant.
20338  *
20339  * Fork - LGPL
20340  * <script type="text/javascript">
20341  */
20342
20343 /**
20344  * @class Roo.data.ScriptTagProxy
20345  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20346  * other than the originating domain of the running page.<br><br>
20347  * <p>
20348  * <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
20349  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20350  * <p>
20351  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20352  * source code that is used as the source inside a &lt;script> tag.<br><br>
20353  * <p>
20354  * In order for the browser to process the returned data, the server must wrap the data object
20355  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20356  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20357  * depending on whether the callback name was passed:
20358  * <p>
20359  * <pre><code>
20360 boolean scriptTag = false;
20361 String cb = request.getParameter("callback");
20362 if (cb != null) {
20363     scriptTag = true;
20364     response.setContentType("text/javascript");
20365 } else {
20366     response.setContentType("application/x-json");
20367 }
20368 Writer out = response.getWriter();
20369 if (scriptTag) {
20370     out.write(cb + "(");
20371 }
20372 out.print(dataBlock.toJsonString());
20373 if (scriptTag) {
20374     out.write(");");
20375 }
20376 </pre></code>
20377  *
20378  * @constructor
20379  * @param {Object} config A configuration object.
20380  */
20381 Roo.data.ScriptTagProxy = function(config){
20382     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20383     Roo.apply(this, config);
20384     this.head = document.getElementsByTagName("head")[0];
20385 };
20386
20387 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20388
20389 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20390     /**
20391      * @cfg {String} url The URL from which to request the data object.
20392      */
20393     /**
20394      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20395      */
20396     timeout : 30000,
20397     /**
20398      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20399      * the server the name of the callback function set up by the load call to process the returned data object.
20400      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20401      * javascript output which calls this named function passing the data object as its only parameter.
20402      */
20403     callbackParam : "callback",
20404     /**
20405      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20406      * name to the request.
20407      */
20408     nocache : true,
20409
20410     /**
20411      * Load data from the configured URL, read the data object into
20412      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20413      * process that block using the passed callback.
20414      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20415      * for the request to the remote server.
20416      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20417      * object into a block of Roo.data.Records.
20418      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20419      * The function must be passed <ul>
20420      * <li>The Record block object</li>
20421      * <li>The "arg" argument from the load function</li>
20422      * <li>A boolean success indicator</li>
20423      * </ul>
20424      * @param {Object} scope The scope in which to call the callback
20425      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20426      */
20427     load : function(params, reader, callback, scope, arg){
20428         if(this.fireEvent("beforeload", this, params) !== false){
20429
20430             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20431
20432             var url = this.url;
20433             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20434             if(this.nocache){
20435                 url += "&_dc=" + (new Date().getTime());
20436             }
20437             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20438             var trans = {
20439                 id : transId,
20440                 cb : "stcCallback"+transId,
20441                 scriptId : "stcScript"+transId,
20442                 params : params,
20443                 arg : arg,
20444                 url : url,
20445                 callback : callback,
20446                 scope : scope,
20447                 reader : reader
20448             };
20449             var conn = this;
20450
20451             window[trans.cb] = function(o){
20452                 conn.handleResponse(o, trans);
20453             };
20454
20455             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20456
20457             if(this.autoAbort !== false){
20458                 this.abort();
20459             }
20460
20461             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20462
20463             var script = document.createElement("script");
20464             script.setAttribute("src", url);
20465             script.setAttribute("type", "text/javascript");
20466             script.setAttribute("id", trans.scriptId);
20467             this.head.appendChild(script);
20468
20469             this.trans = trans;
20470         }else{
20471             callback.call(scope||this, null, arg, false);
20472         }
20473     },
20474
20475     // private
20476     isLoading : function(){
20477         return this.trans ? true : false;
20478     },
20479
20480     /**
20481      * Abort the current server request.
20482      */
20483     abort : function(){
20484         if(this.isLoading()){
20485             this.destroyTrans(this.trans);
20486         }
20487     },
20488
20489     // private
20490     destroyTrans : function(trans, isLoaded){
20491         this.head.removeChild(document.getElementById(trans.scriptId));
20492         clearTimeout(trans.timeoutId);
20493         if(isLoaded){
20494             window[trans.cb] = undefined;
20495             try{
20496                 delete window[trans.cb];
20497             }catch(e){}
20498         }else{
20499             // if hasn't been loaded, wait for load to remove it to prevent script error
20500             window[trans.cb] = function(){
20501                 window[trans.cb] = undefined;
20502                 try{
20503                     delete window[trans.cb];
20504                 }catch(e){}
20505             };
20506         }
20507     },
20508
20509     // private
20510     handleResponse : function(o, trans){
20511         this.trans = false;
20512         this.destroyTrans(trans, true);
20513         var result;
20514         try {
20515             result = trans.reader.readRecords(o);
20516         }catch(e){
20517             this.fireEvent("loadexception", this, o, trans.arg, e);
20518             trans.callback.call(trans.scope||window, null, trans.arg, false);
20519             return;
20520         }
20521         this.fireEvent("load", this, o, trans.arg);
20522         trans.callback.call(trans.scope||window, result, trans.arg, true);
20523     },
20524
20525     // private
20526     handleFailure : function(trans){
20527         this.trans = false;
20528         this.destroyTrans(trans, false);
20529         this.fireEvent("loadexception", this, null, trans.arg);
20530         trans.callback.call(trans.scope||window, null, trans.arg, false);
20531     }
20532 });/*
20533  * Based on:
20534  * Ext JS Library 1.1.1
20535  * Copyright(c) 2006-2007, Ext JS, LLC.
20536  *
20537  * Originally Released Under LGPL - original licence link has changed is not relivant.
20538  *
20539  * Fork - LGPL
20540  * <script type="text/javascript">
20541  */
20542
20543 /**
20544  * @class Roo.data.JsonReader
20545  * @extends Roo.data.DataReader
20546  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20547  * based on mappings in a provided Roo.data.Record constructor.
20548  * 
20549  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20550  * in the reply previously. 
20551  * 
20552  * <p>
20553  * Example code:
20554  * <pre><code>
20555 var RecordDef = Roo.data.Record.create([
20556     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20557     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20558 ]);
20559 var myReader = new Roo.data.JsonReader({
20560     totalProperty: "results",    // The property which contains the total dataset size (optional)
20561     root: "rows",                // The property which contains an Array of row objects
20562     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20563 }, RecordDef);
20564 </code></pre>
20565  * <p>
20566  * This would consume a JSON file like this:
20567  * <pre><code>
20568 { 'results': 2, 'rows': [
20569     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20570     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20571 }
20572 </code></pre>
20573  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20574  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20575  * paged from the remote server.
20576  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20577  * @cfg {String} root name of the property which contains the Array of row objects.
20578  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20579  * @constructor
20580  * Create a new JsonReader
20581  * @param {Object} meta Metadata configuration options
20582  * @param {Object} recordType Either an Array of field definition objects,
20583  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20584  */
20585 Roo.data.JsonReader = function(meta, recordType){
20586     
20587     meta = meta || {};
20588     // set some defaults:
20589     Roo.applyIf(meta, {
20590         totalProperty: 'total',
20591         successProperty : 'success',
20592         root : 'data',
20593         id : 'id'
20594     });
20595     
20596     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20597 };
20598 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20599     
20600     /**
20601      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20602      * Used by Store query builder to append _requestMeta to params.
20603      * 
20604      */
20605     metaFromRemote : false,
20606     /**
20607      * This method is only used by a DataProxy which has retrieved data from a remote server.
20608      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20609      * @return {Object} data A data block which is used by an Roo.data.Store object as
20610      * a cache of Roo.data.Records.
20611      */
20612     read : function(response){
20613         var json = response.responseText;
20614        
20615         var o = /* eval:var:o */ eval("("+json+")");
20616         if(!o) {
20617             throw {message: "JsonReader.read: Json object not found"};
20618         }
20619         
20620         if(o.metaData){
20621             
20622             delete this.ef;
20623             this.metaFromRemote = true;
20624             this.meta = o.metaData;
20625             this.recordType = Roo.data.Record.create(o.metaData.fields);
20626             this.onMetaChange(this.meta, this.recordType, o);
20627         }
20628         return this.readRecords(o);
20629     },
20630
20631     // private function a store will implement
20632     onMetaChange : function(meta, recordType, o){
20633
20634     },
20635
20636     /**
20637          * @ignore
20638          */
20639     simpleAccess: function(obj, subsc) {
20640         return obj[subsc];
20641     },
20642
20643         /**
20644          * @ignore
20645          */
20646     getJsonAccessor: function(){
20647         var re = /[\[\.]/;
20648         return function(expr) {
20649             try {
20650                 return(re.test(expr))
20651                     ? new Function("obj", "return obj." + expr)
20652                     : function(obj){
20653                         return obj[expr];
20654                     };
20655             } catch(e){}
20656             return Roo.emptyFn;
20657         };
20658     }(),
20659
20660     /**
20661      * Create a data block containing Roo.data.Records from an XML document.
20662      * @param {Object} o An object which contains an Array of row objects in the property specified
20663      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20664      * which contains the total size of the dataset.
20665      * @return {Object} data A data block which is used by an Roo.data.Store object as
20666      * a cache of Roo.data.Records.
20667      */
20668     readRecords : function(o){
20669         /**
20670          * After any data loads, the raw JSON data is available for further custom processing.
20671          * @type Object
20672          */
20673         this.jsonData = o;
20674         var s = this.meta, Record = this.recordType,
20675             f = Record.prototype.fields, fi = f.items, fl = f.length;
20676
20677 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20678         if (!this.ef) {
20679             if(s.totalProperty) {
20680                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20681                 }
20682                 if(s.successProperty) {
20683                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20684                 }
20685                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20686                 if (s.id) {
20687                         var g = this.getJsonAccessor(s.id);
20688                         this.getId = function(rec) {
20689                                 var r = g(rec);
20690                                 return (r === undefined || r === "") ? null : r;
20691                         };
20692                 } else {
20693                         this.getId = function(){return null;};
20694                 }
20695             this.ef = [];
20696             for(var jj = 0; jj < fl; jj++){
20697                 f = fi[jj];
20698                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20699                 this.ef[jj] = this.getJsonAccessor(map);
20700             }
20701         }
20702
20703         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20704         if(s.totalProperty){
20705             var vt = parseInt(this.getTotal(o), 10);
20706             if(!isNaN(vt)){
20707                 totalRecords = vt;
20708             }
20709         }
20710         if(s.successProperty){
20711             var vs = this.getSuccess(o);
20712             if(vs === false || vs === 'false'){
20713                 success = false;
20714             }
20715         }
20716         var records = [];
20717             for(var i = 0; i < c; i++){
20718                     var n = root[i];
20719                 var values = {};
20720                 var id = this.getId(n);
20721                 for(var j = 0; j < fl; j++){
20722                     f = fi[j];
20723                 var v = this.ef[j](n);
20724                 if (!f.convert) {
20725                     Roo.log('missing convert for ' + f.name);
20726                     Roo.log(f);
20727                     continue;
20728                 }
20729                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20730                 }
20731                 var record = new Record(values, id);
20732                 record.json = n;
20733                 records[i] = record;
20734             }
20735             return {
20736                 success : success,
20737                 records : records,
20738                 totalRecords : totalRecords
20739             };
20740     }
20741 });/*
20742  * Based on:
20743  * Ext JS Library 1.1.1
20744  * Copyright(c) 2006-2007, Ext JS, LLC.
20745  *
20746  * Originally Released Under LGPL - original licence link has changed is not relivant.
20747  *
20748  * Fork - LGPL
20749  * <script type="text/javascript">
20750  */
20751
20752 /**
20753  * @class Roo.data.XmlReader
20754  * @extends Roo.data.DataReader
20755  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20756  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20757  * <p>
20758  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20759  * header in the HTTP response must be set to "text/xml".</em>
20760  * <p>
20761  * Example code:
20762  * <pre><code>
20763 var RecordDef = Roo.data.Record.create([
20764    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20765    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20766 ]);
20767 var myReader = new Roo.data.XmlReader({
20768    totalRecords: "results", // The element which contains the total dataset size (optional)
20769    record: "row",           // The repeated element which contains row information
20770    id: "id"                 // The element within the row that provides an ID for the record (optional)
20771 }, RecordDef);
20772 </code></pre>
20773  * <p>
20774  * This would consume an XML file like this:
20775  * <pre><code>
20776 &lt;?xml?>
20777 &lt;dataset>
20778  &lt;results>2&lt;/results>
20779  &lt;row>
20780    &lt;id>1&lt;/id>
20781    &lt;name>Bill&lt;/name>
20782    &lt;occupation>Gardener&lt;/occupation>
20783  &lt;/row>
20784  &lt;row>
20785    &lt;id>2&lt;/id>
20786    &lt;name>Ben&lt;/name>
20787    &lt;occupation>Horticulturalist&lt;/occupation>
20788  &lt;/row>
20789 &lt;/dataset>
20790 </code></pre>
20791  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20792  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20793  * paged from the remote server.
20794  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20795  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20796  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20797  * a record identifier value.
20798  * @constructor
20799  * Create a new XmlReader
20800  * @param {Object} meta Metadata configuration options
20801  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20802  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20803  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20804  */
20805 Roo.data.XmlReader = function(meta, recordType){
20806     meta = meta || {};
20807     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20808 };
20809 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20810     /**
20811      * This method is only used by a DataProxy which has retrieved data from a remote server.
20812          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20813          * to contain a method called 'responseXML' that returns an XML document object.
20814      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20815      * a cache of Roo.data.Records.
20816      */
20817     read : function(response){
20818         var doc = response.responseXML;
20819         if(!doc) {
20820             throw {message: "XmlReader.read: XML Document not available"};
20821         }
20822         return this.readRecords(doc);
20823     },
20824
20825     /**
20826      * Create a data block containing Roo.data.Records from an XML document.
20827          * @param {Object} doc A parsed XML document.
20828      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20829      * a cache of Roo.data.Records.
20830      */
20831     readRecords : function(doc){
20832         /**
20833          * After any data loads/reads, the raw XML Document is available for further custom processing.
20834          * @type XMLDocument
20835          */
20836         this.xmlData = doc;
20837         var root = doc.documentElement || doc;
20838         var q = Roo.DomQuery;
20839         var recordType = this.recordType, fields = recordType.prototype.fields;
20840         var sid = this.meta.id;
20841         var totalRecords = 0, success = true;
20842         if(this.meta.totalRecords){
20843             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20844         }
20845         
20846         if(this.meta.success){
20847             var sv = q.selectValue(this.meta.success, root, true);
20848             success = sv !== false && sv !== 'false';
20849         }
20850         var records = [];
20851         var ns = q.select(this.meta.record, root);
20852         for(var i = 0, len = ns.length; i < len; i++) {
20853                 var n = ns[i];
20854                 var values = {};
20855                 var id = sid ? q.selectValue(sid, n) : undefined;
20856                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20857                     var f = fields.items[j];
20858                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20859                     v = f.convert(v);
20860                     values[f.name] = v;
20861                 }
20862                 var record = new recordType(values, id);
20863                 record.node = n;
20864                 records[records.length] = record;
20865             }
20866
20867             return {
20868                 success : success,
20869                 records : records,
20870                 totalRecords : totalRecords || records.length
20871             };
20872     }
20873 });/*
20874  * Based on:
20875  * Ext JS Library 1.1.1
20876  * Copyright(c) 2006-2007, Ext JS, LLC.
20877  *
20878  * Originally Released Under LGPL - original licence link has changed is not relivant.
20879  *
20880  * Fork - LGPL
20881  * <script type="text/javascript">
20882  */
20883
20884 /**
20885  * @class Roo.data.ArrayReader
20886  * @extends Roo.data.DataReader
20887  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20888  * Each element of that Array represents a row of data fields. The
20889  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20890  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20891  * <p>
20892  * Example code:.
20893  * <pre><code>
20894 var RecordDef = Roo.data.Record.create([
20895     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20896     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20897 ]);
20898 var myReader = new Roo.data.ArrayReader({
20899     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20900 }, RecordDef);
20901 </code></pre>
20902  * <p>
20903  * This would consume an Array like this:
20904  * <pre><code>
20905 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20906   </code></pre>
20907  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20908  * @constructor
20909  * Create a new JsonReader
20910  * @param {Object} meta Metadata configuration options.
20911  * @param {Object} recordType Either an Array of field definition objects
20912  * as specified to {@link Roo.data.Record#create},
20913  * or an {@link Roo.data.Record} object
20914  * created using {@link Roo.data.Record#create}.
20915  */
20916 Roo.data.ArrayReader = function(meta, recordType){
20917     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20918 };
20919
20920 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20921     /**
20922      * Create a data block containing Roo.data.Records from an XML document.
20923      * @param {Object} o An Array of row objects which represents the dataset.
20924      * @return {Object} data A data block which is used by an Roo.data.Store object as
20925      * a cache of Roo.data.Records.
20926      */
20927     readRecords : function(o){
20928         var sid = this.meta ? this.meta.id : null;
20929         var recordType = this.recordType, fields = recordType.prototype.fields;
20930         var records = [];
20931         var root = o;
20932             for(var i = 0; i < root.length; i++){
20933                     var n = root[i];
20934                 var values = {};
20935                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20936                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20937                 var f = fields.items[j];
20938                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20939                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20940                 v = f.convert(v);
20941                 values[f.name] = v;
20942             }
20943                 var record = new recordType(values, id);
20944                 record.json = n;
20945                 records[records.length] = record;
20946             }
20947             return {
20948                 records : records,
20949                 totalRecords : records.length
20950             };
20951     }
20952 });/*
20953  * Based on:
20954  * Ext JS Library 1.1.1
20955  * Copyright(c) 2006-2007, Ext JS, LLC.
20956  *
20957  * Originally Released Under LGPL - original licence link has changed is not relivant.
20958  *
20959  * Fork - LGPL
20960  * <script type="text/javascript">
20961  */
20962
20963
20964 /**
20965  * @class Roo.data.Tree
20966  * @extends Roo.util.Observable
20967  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20968  * in the tree have most standard DOM functionality.
20969  * @constructor
20970  * @param {Node} root (optional) The root node
20971  */
20972 Roo.data.Tree = function(root){
20973    this.nodeHash = {};
20974    /**
20975     * The root node for this tree
20976     * @type Node
20977     */
20978    this.root = null;
20979    if(root){
20980        this.setRootNode(root);
20981    }
20982    this.addEvents({
20983        /**
20984         * @event append
20985         * Fires when a new child node is appended to a node in this tree.
20986         * @param {Tree} tree The owner tree
20987         * @param {Node} parent The parent node
20988         * @param {Node} node The newly appended node
20989         * @param {Number} index The index of the newly appended node
20990         */
20991        "append" : true,
20992        /**
20993         * @event remove
20994         * Fires when a child node is removed from a node in this tree.
20995         * @param {Tree} tree The owner tree
20996         * @param {Node} parent The parent node
20997         * @param {Node} node The child node removed
20998         */
20999        "remove" : true,
21000        /**
21001         * @event move
21002         * Fires when a node is moved to a new location in the tree
21003         * @param {Tree} tree The owner tree
21004         * @param {Node} node The node moved
21005         * @param {Node} oldParent The old parent of this node
21006         * @param {Node} newParent The new parent of this node
21007         * @param {Number} index The index it was moved to
21008         */
21009        "move" : true,
21010        /**
21011         * @event insert
21012         * Fires when a new child node is inserted in a node in this tree.
21013         * @param {Tree} tree The owner tree
21014         * @param {Node} parent The parent node
21015         * @param {Node} node The child node inserted
21016         * @param {Node} refNode The child node the node was inserted before
21017         */
21018        "insert" : true,
21019        /**
21020         * @event beforeappend
21021         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21022         * @param {Tree} tree The owner tree
21023         * @param {Node} parent The parent node
21024         * @param {Node} node The child node to be appended
21025         */
21026        "beforeappend" : true,
21027        /**
21028         * @event beforeremove
21029         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21030         * @param {Tree} tree The owner tree
21031         * @param {Node} parent The parent node
21032         * @param {Node} node The child node to be removed
21033         */
21034        "beforeremove" : true,
21035        /**
21036         * @event beforemove
21037         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21038         * @param {Tree} tree The owner tree
21039         * @param {Node} node The node being moved
21040         * @param {Node} oldParent The parent of the node
21041         * @param {Node} newParent The new parent the node is moving to
21042         * @param {Number} index The index it is being moved to
21043         */
21044        "beforemove" : true,
21045        /**
21046         * @event beforeinsert
21047         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21048         * @param {Tree} tree The owner tree
21049         * @param {Node} parent The parent node
21050         * @param {Node} node The child node to be inserted
21051         * @param {Node} refNode The child node the node is being inserted before
21052         */
21053        "beforeinsert" : true
21054    });
21055
21056     Roo.data.Tree.superclass.constructor.call(this);
21057 };
21058
21059 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21060     pathSeparator: "/",
21061
21062     proxyNodeEvent : function(){
21063         return this.fireEvent.apply(this, arguments);
21064     },
21065
21066     /**
21067      * Returns the root node for this tree.
21068      * @return {Node}
21069      */
21070     getRootNode : function(){
21071         return this.root;
21072     },
21073
21074     /**
21075      * Sets the root node for this tree.
21076      * @param {Node} node
21077      * @return {Node}
21078      */
21079     setRootNode : function(node){
21080         this.root = node;
21081         node.ownerTree = this;
21082         node.isRoot = true;
21083         this.registerNode(node);
21084         return node;
21085     },
21086
21087     /**
21088      * Gets a node in this tree by its id.
21089      * @param {String} id
21090      * @return {Node}
21091      */
21092     getNodeById : function(id){
21093         return this.nodeHash[id];
21094     },
21095
21096     registerNode : function(node){
21097         this.nodeHash[node.id] = node;
21098     },
21099
21100     unregisterNode : function(node){
21101         delete this.nodeHash[node.id];
21102     },
21103
21104     toString : function(){
21105         return "[Tree"+(this.id?" "+this.id:"")+"]";
21106     }
21107 });
21108
21109 /**
21110  * @class Roo.data.Node
21111  * @extends Roo.util.Observable
21112  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21113  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21114  * @constructor
21115  * @param {Object} attributes The attributes/config for the node
21116  */
21117 Roo.data.Node = function(attributes){
21118     /**
21119      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21120      * @type {Object}
21121      */
21122     this.attributes = attributes || {};
21123     this.leaf = this.attributes.leaf;
21124     /**
21125      * The node id. @type String
21126      */
21127     this.id = this.attributes.id;
21128     if(!this.id){
21129         this.id = Roo.id(null, "ynode-");
21130         this.attributes.id = this.id;
21131     }
21132     /**
21133      * All child nodes of this node. @type Array
21134      */
21135     this.childNodes = [];
21136     if(!this.childNodes.indexOf){ // indexOf is a must
21137         this.childNodes.indexOf = function(o){
21138             for(var i = 0, len = this.length; i < len; i++){
21139                 if(this[i] == o) {
21140                     return i;
21141                 }
21142             }
21143             return -1;
21144         };
21145     }
21146     /**
21147      * The parent node for this node. @type Node
21148      */
21149     this.parentNode = null;
21150     /**
21151      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21152      */
21153     this.firstChild = null;
21154     /**
21155      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21156      */
21157     this.lastChild = null;
21158     /**
21159      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21160      */
21161     this.previousSibling = null;
21162     /**
21163      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21164      */
21165     this.nextSibling = null;
21166
21167     this.addEvents({
21168        /**
21169         * @event append
21170         * Fires when a new child node is appended
21171         * @param {Tree} tree The owner tree
21172         * @param {Node} this This node
21173         * @param {Node} node The newly appended node
21174         * @param {Number} index The index of the newly appended node
21175         */
21176        "append" : true,
21177        /**
21178         * @event remove
21179         * Fires when a child node is removed
21180         * @param {Tree} tree The owner tree
21181         * @param {Node} this This node
21182         * @param {Node} node The removed node
21183         */
21184        "remove" : true,
21185        /**
21186         * @event move
21187         * Fires when this node is moved to a new location in the tree
21188         * @param {Tree} tree The owner tree
21189         * @param {Node} this This node
21190         * @param {Node} oldParent The old parent of this node
21191         * @param {Node} newParent The new parent of this node
21192         * @param {Number} index The index it was moved to
21193         */
21194        "move" : true,
21195        /**
21196         * @event insert
21197         * Fires when a new child node is inserted.
21198         * @param {Tree} tree The owner tree
21199         * @param {Node} this This node
21200         * @param {Node} node The child node inserted
21201         * @param {Node} refNode The child node the node was inserted before
21202         */
21203        "insert" : true,
21204        /**
21205         * @event beforeappend
21206         * Fires before a new child is appended, return false to cancel the append.
21207         * @param {Tree} tree The owner tree
21208         * @param {Node} this This node
21209         * @param {Node} node The child node to be appended
21210         */
21211        "beforeappend" : true,
21212        /**
21213         * @event beforeremove
21214         * Fires before a child is removed, return false to cancel the remove.
21215         * @param {Tree} tree The owner tree
21216         * @param {Node} this This node
21217         * @param {Node} node The child node to be removed
21218         */
21219        "beforeremove" : true,
21220        /**
21221         * @event beforemove
21222         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21223         * @param {Tree} tree The owner tree
21224         * @param {Node} this This node
21225         * @param {Node} oldParent The parent of this node
21226         * @param {Node} newParent The new parent this node is moving to
21227         * @param {Number} index The index it is being moved to
21228         */
21229        "beforemove" : true,
21230        /**
21231         * @event beforeinsert
21232         * Fires before a new child is inserted, return false to cancel the insert.
21233         * @param {Tree} tree The owner tree
21234         * @param {Node} this This node
21235         * @param {Node} node The child node to be inserted
21236         * @param {Node} refNode The child node the node is being inserted before
21237         */
21238        "beforeinsert" : true
21239    });
21240     this.listeners = this.attributes.listeners;
21241     Roo.data.Node.superclass.constructor.call(this);
21242 };
21243
21244 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21245     fireEvent : function(evtName){
21246         // first do standard event for this node
21247         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21248             return false;
21249         }
21250         // then bubble it up to the tree if the event wasn't cancelled
21251         var ot = this.getOwnerTree();
21252         if(ot){
21253             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21254                 return false;
21255             }
21256         }
21257         return true;
21258     },
21259
21260     /**
21261      * Returns true if this node is a leaf
21262      * @return {Boolean}
21263      */
21264     isLeaf : function(){
21265         return this.leaf === true;
21266     },
21267
21268     // private
21269     setFirstChild : function(node){
21270         this.firstChild = node;
21271     },
21272
21273     //private
21274     setLastChild : function(node){
21275         this.lastChild = node;
21276     },
21277
21278
21279     /**
21280      * Returns true if this node is the last child of its parent
21281      * @return {Boolean}
21282      */
21283     isLast : function(){
21284        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21285     },
21286
21287     /**
21288      * Returns true if this node is the first child of its parent
21289      * @return {Boolean}
21290      */
21291     isFirst : function(){
21292        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21293     },
21294
21295     hasChildNodes : function(){
21296         return !this.isLeaf() && this.childNodes.length > 0;
21297     },
21298
21299     /**
21300      * Insert node(s) as the last child node of this node.
21301      * @param {Node/Array} node The node or Array of nodes to append
21302      * @return {Node} The appended node if single append, or null if an array was passed
21303      */
21304     appendChild : function(node){
21305         var multi = false;
21306         if(node instanceof Array){
21307             multi = node;
21308         }else if(arguments.length > 1){
21309             multi = arguments;
21310         }
21311         // if passed an array or multiple args do them one by one
21312         if(multi){
21313             for(var i = 0, len = multi.length; i < len; i++) {
21314                 this.appendChild(multi[i]);
21315             }
21316         }else{
21317             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21318                 return false;
21319             }
21320             var index = this.childNodes.length;
21321             var oldParent = node.parentNode;
21322             // it's a move, make sure we move it cleanly
21323             if(oldParent){
21324                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21325                     return false;
21326                 }
21327                 oldParent.removeChild(node);
21328             }
21329             index = this.childNodes.length;
21330             if(index == 0){
21331                 this.setFirstChild(node);
21332             }
21333             this.childNodes.push(node);
21334             node.parentNode = this;
21335             var ps = this.childNodes[index-1];
21336             if(ps){
21337                 node.previousSibling = ps;
21338                 ps.nextSibling = node;
21339             }else{
21340                 node.previousSibling = null;
21341             }
21342             node.nextSibling = null;
21343             this.setLastChild(node);
21344             node.setOwnerTree(this.getOwnerTree());
21345             this.fireEvent("append", this.ownerTree, this, node, index);
21346             if(oldParent){
21347                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21348             }
21349             return node;
21350         }
21351     },
21352
21353     /**
21354      * Removes a child node from this node.
21355      * @param {Node} node The node to remove
21356      * @return {Node} The removed node
21357      */
21358     removeChild : function(node){
21359         var index = this.childNodes.indexOf(node);
21360         if(index == -1){
21361             return false;
21362         }
21363         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21364             return false;
21365         }
21366
21367         // remove it from childNodes collection
21368         this.childNodes.splice(index, 1);
21369
21370         // update siblings
21371         if(node.previousSibling){
21372             node.previousSibling.nextSibling = node.nextSibling;
21373         }
21374         if(node.nextSibling){
21375             node.nextSibling.previousSibling = node.previousSibling;
21376         }
21377
21378         // update child refs
21379         if(this.firstChild == node){
21380             this.setFirstChild(node.nextSibling);
21381         }
21382         if(this.lastChild == node){
21383             this.setLastChild(node.previousSibling);
21384         }
21385
21386         node.setOwnerTree(null);
21387         // clear any references from the node
21388         node.parentNode = null;
21389         node.previousSibling = null;
21390         node.nextSibling = null;
21391         this.fireEvent("remove", this.ownerTree, this, node);
21392         return node;
21393     },
21394
21395     /**
21396      * Inserts the first node before the second node in this nodes childNodes collection.
21397      * @param {Node} node The node to insert
21398      * @param {Node} refNode The node to insert before (if null the node is appended)
21399      * @return {Node} The inserted node
21400      */
21401     insertBefore : function(node, refNode){
21402         if(!refNode){ // like standard Dom, refNode can be null for append
21403             return this.appendChild(node);
21404         }
21405         // nothing to do
21406         if(node == refNode){
21407             return false;
21408         }
21409
21410         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21411             return false;
21412         }
21413         var index = this.childNodes.indexOf(refNode);
21414         var oldParent = node.parentNode;
21415         var refIndex = index;
21416
21417         // when moving internally, indexes will change after remove
21418         if(oldParent == this && this.childNodes.indexOf(node) < index){
21419             refIndex--;
21420         }
21421
21422         // it's a move, make sure we move it cleanly
21423         if(oldParent){
21424             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21425                 return false;
21426             }
21427             oldParent.removeChild(node);
21428         }
21429         if(refIndex == 0){
21430             this.setFirstChild(node);
21431         }
21432         this.childNodes.splice(refIndex, 0, node);
21433         node.parentNode = this;
21434         var ps = this.childNodes[refIndex-1];
21435         if(ps){
21436             node.previousSibling = ps;
21437             ps.nextSibling = node;
21438         }else{
21439             node.previousSibling = null;
21440         }
21441         node.nextSibling = refNode;
21442         refNode.previousSibling = node;
21443         node.setOwnerTree(this.getOwnerTree());
21444         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21445         if(oldParent){
21446             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21447         }
21448         return node;
21449     },
21450
21451     /**
21452      * Returns the child node at the specified index.
21453      * @param {Number} index
21454      * @return {Node}
21455      */
21456     item : function(index){
21457         return this.childNodes[index];
21458     },
21459
21460     /**
21461      * Replaces one child node in this node with another.
21462      * @param {Node} newChild The replacement node
21463      * @param {Node} oldChild The node to replace
21464      * @return {Node} The replaced node
21465      */
21466     replaceChild : function(newChild, oldChild){
21467         this.insertBefore(newChild, oldChild);
21468         this.removeChild(oldChild);
21469         return oldChild;
21470     },
21471
21472     /**
21473      * Returns the index of a child node
21474      * @param {Node} node
21475      * @return {Number} The index of the node or -1 if it was not found
21476      */
21477     indexOf : function(child){
21478         return this.childNodes.indexOf(child);
21479     },
21480
21481     /**
21482      * Returns the tree this node is in.
21483      * @return {Tree}
21484      */
21485     getOwnerTree : function(){
21486         // if it doesn't have one, look for one
21487         if(!this.ownerTree){
21488             var p = this;
21489             while(p){
21490                 if(p.ownerTree){
21491                     this.ownerTree = p.ownerTree;
21492                     break;
21493                 }
21494                 p = p.parentNode;
21495             }
21496         }
21497         return this.ownerTree;
21498     },
21499
21500     /**
21501      * Returns depth of this node (the root node has a depth of 0)
21502      * @return {Number}
21503      */
21504     getDepth : function(){
21505         var depth = 0;
21506         var p = this;
21507         while(p.parentNode){
21508             ++depth;
21509             p = p.parentNode;
21510         }
21511         return depth;
21512     },
21513
21514     // private
21515     setOwnerTree : function(tree){
21516         // if it's move, we need to update everyone
21517         if(tree != this.ownerTree){
21518             if(this.ownerTree){
21519                 this.ownerTree.unregisterNode(this);
21520             }
21521             this.ownerTree = tree;
21522             var cs = this.childNodes;
21523             for(var i = 0, len = cs.length; i < len; i++) {
21524                 cs[i].setOwnerTree(tree);
21525             }
21526             if(tree){
21527                 tree.registerNode(this);
21528             }
21529         }
21530     },
21531
21532     /**
21533      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21534      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21535      * @return {String} The path
21536      */
21537     getPath : function(attr){
21538         attr = attr || "id";
21539         var p = this.parentNode;
21540         var b = [this.attributes[attr]];
21541         while(p){
21542             b.unshift(p.attributes[attr]);
21543             p = p.parentNode;
21544         }
21545         var sep = this.getOwnerTree().pathSeparator;
21546         return sep + b.join(sep);
21547     },
21548
21549     /**
21550      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21551      * function call will be the scope provided or the current node. The arguments to the function
21552      * will be the args provided or the current node. If the function returns false at any point,
21553      * the bubble is stopped.
21554      * @param {Function} fn The function to call
21555      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21556      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21557      */
21558     bubble : function(fn, scope, args){
21559         var p = this;
21560         while(p){
21561             if(fn.call(scope || p, args || p) === false){
21562                 break;
21563             }
21564             p = p.parentNode;
21565         }
21566     },
21567
21568     /**
21569      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21570      * function call will be the scope provided or the current node. The arguments to the function
21571      * will be the args provided or the current node. If the function returns false at any point,
21572      * the cascade is stopped on that branch.
21573      * @param {Function} fn The function to call
21574      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21575      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21576      */
21577     cascade : function(fn, scope, args){
21578         if(fn.call(scope || this, args || this) !== false){
21579             var cs = this.childNodes;
21580             for(var i = 0, len = cs.length; i < len; i++) {
21581                 cs[i].cascade(fn, scope, args);
21582             }
21583         }
21584     },
21585
21586     /**
21587      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21588      * function call will be the scope provided or the current node. The arguments to the function
21589      * will be the args provided or the current node. If the function returns false at any point,
21590      * the iteration stops.
21591      * @param {Function} fn The function to call
21592      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21593      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21594      */
21595     eachChild : function(fn, scope, args){
21596         var cs = this.childNodes;
21597         for(var i = 0, len = cs.length; i < len; i++) {
21598                 if(fn.call(scope || this, args || cs[i]) === false){
21599                     break;
21600                 }
21601         }
21602     },
21603
21604     /**
21605      * Finds the first child that has the attribute with the specified value.
21606      * @param {String} attribute The attribute name
21607      * @param {Mixed} value The value to search for
21608      * @return {Node} The found child or null if none was found
21609      */
21610     findChild : function(attribute, value){
21611         var cs = this.childNodes;
21612         for(var i = 0, len = cs.length; i < len; i++) {
21613                 if(cs[i].attributes[attribute] == value){
21614                     return cs[i];
21615                 }
21616         }
21617         return null;
21618     },
21619
21620     /**
21621      * Finds the first child by a custom function. The child matches if the function passed
21622      * returns true.
21623      * @param {Function} fn
21624      * @param {Object} scope (optional)
21625      * @return {Node} The found child or null if none was found
21626      */
21627     findChildBy : function(fn, scope){
21628         var cs = this.childNodes;
21629         for(var i = 0, len = cs.length; i < len; i++) {
21630                 if(fn.call(scope||cs[i], cs[i]) === true){
21631                     return cs[i];
21632                 }
21633         }
21634         return null;
21635     },
21636
21637     /**
21638      * Sorts this nodes children using the supplied sort function
21639      * @param {Function} fn
21640      * @param {Object} scope (optional)
21641      */
21642     sort : function(fn, scope){
21643         var cs = this.childNodes;
21644         var len = cs.length;
21645         if(len > 0){
21646             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21647             cs.sort(sortFn);
21648             for(var i = 0; i < len; i++){
21649                 var n = cs[i];
21650                 n.previousSibling = cs[i-1];
21651                 n.nextSibling = cs[i+1];
21652                 if(i == 0){
21653                     this.setFirstChild(n);
21654                 }
21655                 if(i == len-1){
21656                     this.setLastChild(n);
21657                 }
21658             }
21659         }
21660     },
21661
21662     /**
21663      * Returns true if this node is an ancestor (at any point) of the passed node.
21664      * @param {Node} node
21665      * @return {Boolean}
21666      */
21667     contains : function(node){
21668         return node.isAncestor(this);
21669     },
21670
21671     /**
21672      * Returns true if the passed node is an ancestor (at any point) of this node.
21673      * @param {Node} node
21674      * @return {Boolean}
21675      */
21676     isAncestor : function(node){
21677         var p = this.parentNode;
21678         while(p){
21679             if(p == node){
21680                 return true;
21681             }
21682             p = p.parentNode;
21683         }
21684         return false;
21685     },
21686
21687     toString : function(){
21688         return "[Node"+(this.id?" "+this.id:"")+"]";
21689     }
21690 });/*
21691  * Based on:
21692  * Ext JS Library 1.1.1
21693  * Copyright(c) 2006-2007, Ext JS, LLC.
21694  *
21695  * Originally Released Under LGPL - original licence link has changed is not relivant.
21696  *
21697  * Fork - LGPL
21698  * <script type="text/javascript">
21699  */
21700  
21701
21702 /**
21703  * @class Roo.ComponentMgr
21704  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21705  * @singleton
21706  */
21707 Roo.ComponentMgr = function(){
21708     var all = new Roo.util.MixedCollection();
21709
21710     return {
21711         /**
21712          * Registers a component.
21713          * @param {Roo.Component} c The component
21714          */
21715         register : function(c){
21716             all.add(c);
21717         },
21718
21719         /**
21720          * Unregisters a component.
21721          * @param {Roo.Component} c The component
21722          */
21723         unregister : function(c){
21724             all.remove(c);
21725         },
21726
21727         /**
21728          * Returns a component by id
21729          * @param {String} id The component id
21730          */
21731         get : function(id){
21732             return all.get(id);
21733         },
21734
21735         /**
21736          * Registers a function that will be called when a specified component is added to ComponentMgr
21737          * @param {String} id The component id
21738          * @param {Funtction} fn The callback function
21739          * @param {Object} scope The scope of the callback
21740          */
21741         onAvailable : function(id, fn, scope){
21742             all.on("add", function(index, o){
21743                 if(o.id == id){
21744                     fn.call(scope || o, o);
21745                     all.un("add", fn, scope);
21746                 }
21747             });
21748         }
21749     };
21750 }();/*
21751  * Based on:
21752  * Ext JS Library 1.1.1
21753  * Copyright(c) 2006-2007, Ext JS, LLC.
21754  *
21755  * Originally Released Under LGPL - original licence link has changed is not relivant.
21756  *
21757  * Fork - LGPL
21758  * <script type="text/javascript">
21759  */
21760  
21761 /**
21762  * @class Roo.Component
21763  * @extends Roo.util.Observable
21764  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21765  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21766  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21767  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21768  * All visual components (widgets) that require rendering into a layout should subclass Component.
21769  * @constructor
21770  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21771  * 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
21772  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21773  */
21774 Roo.Component = function(config){
21775     config = config || {};
21776     if(config.tagName || config.dom || typeof config == "string"){ // element object
21777         config = {el: config, id: config.id || config};
21778     }
21779     this.initialConfig = config;
21780
21781     Roo.apply(this, config);
21782     this.addEvents({
21783         /**
21784          * @event disable
21785          * Fires after the component is disabled.
21786              * @param {Roo.Component} this
21787              */
21788         disable : true,
21789         /**
21790          * @event enable
21791          * Fires after the component is enabled.
21792              * @param {Roo.Component} this
21793              */
21794         enable : true,
21795         /**
21796          * @event beforeshow
21797          * Fires before the component is shown.  Return false to stop the show.
21798              * @param {Roo.Component} this
21799              */
21800         beforeshow : true,
21801         /**
21802          * @event show
21803          * Fires after the component is shown.
21804              * @param {Roo.Component} this
21805              */
21806         show : true,
21807         /**
21808          * @event beforehide
21809          * Fires before the component is hidden. Return false to stop the hide.
21810              * @param {Roo.Component} this
21811              */
21812         beforehide : true,
21813         /**
21814          * @event hide
21815          * Fires after the component is hidden.
21816              * @param {Roo.Component} this
21817              */
21818         hide : true,
21819         /**
21820          * @event beforerender
21821          * Fires before the component is rendered. Return false to stop the render.
21822              * @param {Roo.Component} this
21823              */
21824         beforerender : true,
21825         /**
21826          * @event render
21827          * Fires after the component is rendered.
21828              * @param {Roo.Component} this
21829              */
21830         render : true,
21831         /**
21832          * @event beforedestroy
21833          * Fires before the component is destroyed. Return false to stop the destroy.
21834              * @param {Roo.Component} this
21835              */
21836         beforedestroy : true,
21837         /**
21838          * @event destroy
21839          * Fires after the component is destroyed.
21840              * @param {Roo.Component} this
21841              */
21842         destroy : true
21843     });
21844     if(!this.id){
21845         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21846     }
21847     Roo.ComponentMgr.register(this);
21848     Roo.Component.superclass.constructor.call(this);
21849     this.initComponent();
21850     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21851         this.render(this.renderTo);
21852         delete this.renderTo;
21853     }
21854 };
21855
21856 // private
21857 Roo.Component.AUTO_ID = 1000;
21858
21859 Roo.extend(Roo.Component, Roo.util.Observable, {
21860     /**
21861      * @property {Boolean} hidden
21862      * true if this component is hidden. Read-only.
21863      */
21864     hidden : false,
21865     /**
21866      * true if this component is disabled. Read-only.
21867      */
21868     disabled : false,
21869     /**
21870      * true if this component has been rendered. Read-only.
21871      */
21872     rendered : false,
21873     
21874     /** @cfg {String} disableClass
21875      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21876      */
21877     disabledClass : "x-item-disabled",
21878         /** @cfg {Boolean} allowDomMove
21879          * Whether the component can move the Dom node when rendering (defaults to true).
21880          */
21881     allowDomMove : true,
21882     /** @cfg {String} hideMode
21883      * How this component should hidden. Supported values are
21884      * "visibility" (css visibility), "offsets" (negative offset position) and
21885      * "display" (css display) - defaults to "display".
21886      */
21887     hideMode: 'display',
21888
21889     // private
21890     ctype : "Roo.Component",
21891
21892     /** @cfg {String} actionMode 
21893      * which property holds the element that used for  hide() / show() / disable() / enable()
21894      * default is 'el' 
21895      */
21896     actionMode : "el",
21897
21898     // private
21899     getActionEl : function(){
21900         return this[this.actionMode];
21901     },
21902
21903     initComponent : Roo.emptyFn,
21904     /**
21905      * If this is a lazy rendering component, render it to its container element.
21906      * @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.
21907      */
21908     render : function(container, position){
21909         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21910             if(!container && this.el){
21911                 this.el = Roo.get(this.el);
21912                 container = this.el.dom.parentNode;
21913                 this.allowDomMove = false;
21914             }
21915             this.container = Roo.get(container);
21916             this.rendered = true;
21917             if(position !== undefined){
21918                 if(typeof position == 'number'){
21919                     position = this.container.dom.childNodes[position];
21920                 }else{
21921                     position = Roo.getDom(position);
21922                 }
21923             }
21924             this.onRender(this.container, position || null);
21925             if(this.cls){
21926                 this.el.addClass(this.cls);
21927                 delete this.cls;
21928             }
21929             if(this.style){
21930                 this.el.applyStyles(this.style);
21931                 delete this.style;
21932             }
21933             this.fireEvent("render", this);
21934             this.afterRender(this.container);
21935             if(this.hidden){
21936                 this.hide();
21937             }
21938             if(this.disabled){
21939                 this.disable();
21940             }
21941         }
21942         return this;
21943     },
21944
21945     // private
21946     // default function is not really useful
21947     onRender : function(ct, position){
21948         if(this.el){
21949             this.el = Roo.get(this.el);
21950             if(this.allowDomMove !== false){
21951                 ct.dom.insertBefore(this.el.dom, position);
21952             }
21953         }
21954     },
21955
21956     // private
21957     getAutoCreate : function(){
21958         var cfg = typeof this.autoCreate == "object" ?
21959                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21960         if(this.id && !cfg.id){
21961             cfg.id = this.id;
21962         }
21963         return cfg;
21964     },
21965
21966     // private
21967     afterRender : Roo.emptyFn,
21968
21969     /**
21970      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21971      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21972      */
21973     destroy : function(){
21974         if(this.fireEvent("beforedestroy", this) !== false){
21975             this.purgeListeners();
21976             this.beforeDestroy();
21977             if(this.rendered){
21978                 this.el.removeAllListeners();
21979                 this.el.remove();
21980                 if(this.actionMode == "container"){
21981                     this.container.remove();
21982                 }
21983             }
21984             this.onDestroy();
21985             Roo.ComponentMgr.unregister(this);
21986             this.fireEvent("destroy", this);
21987         }
21988     },
21989
21990         // private
21991     beforeDestroy : function(){
21992
21993     },
21994
21995         // private
21996         onDestroy : function(){
21997
21998     },
21999
22000     /**
22001      * Returns the underlying {@link Roo.Element}.
22002      * @return {Roo.Element} The element
22003      */
22004     getEl : function(){
22005         return this.el;
22006     },
22007
22008     /**
22009      * Returns the id of this component.
22010      * @return {String}
22011      */
22012     getId : function(){
22013         return this.id;
22014     },
22015
22016     /**
22017      * Try to focus this component.
22018      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22019      * @return {Roo.Component} this
22020      */
22021     focus : function(selectText){
22022         if(this.rendered){
22023             this.el.focus();
22024             if(selectText === true){
22025                 this.el.dom.select();
22026             }
22027         }
22028         return this;
22029     },
22030
22031     // private
22032     blur : function(){
22033         if(this.rendered){
22034             this.el.blur();
22035         }
22036         return this;
22037     },
22038
22039     /**
22040      * Disable this component.
22041      * @return {Roo.Component} this
22042      */
22043     disable : function(){
22044         if(this.rendered){
22045             this.onDisable();
22046         }
22047         this.disabled = true;
22048         this.fireEvent("disable", this);
22049         return this;
22050     },
22051
22052         // private
22053     onDisable : function(){
22054         this.getActionEl().addClass(this.disabledClass);
22055         this.el.dom.disabled = true;
22056     },
22057
22058     /**
22059      * Enable this component.
22060      * @return {Roo.Component} this
22061      */
22062     enable : function(){
22063         if(this.rendered){
22064             this.onEnable();
22065         }
22066         this.disabled = false;
22067         this.fireEvent("enable", this);
22068         return this;
22069     },
22070
22071         // private
22072     onEnable : function(){
22073         this.getActionEl().removeClass(this.disabledClass);
22074         this.el.dom.disabled = false;
22075     },
22076
22077     /**
22078      * Convenience function for setting disabled/enabled by boolean.
22079      * @param {Boolean} disabled
22080      */
22081     setDisabled : function(disabled){
22082         this[disabled ? "disable" : "enable"]();
22083     },
22084
22085     /**
22086      * Show this component.
22087      * @return {Roo.Component} this
22088      */
22089     show: function(){
22090         if(this.fireEvent("beforeshow", this) !== false){
22091             this.hidden = false;
22092             if(this.rendered){
22093                 this.onShow();
22094             }
22095             this.fireEvent("show", this);
22096         }
22097         return this;
22098     },
22099
22100     // private
22101     onShow : function(){
22102         var ae = this.getActionEl();
22103         if(this.hideMode == 'visibility'){
22104             ae.dom.style.visibility = "visible";
22105         }else if(this.hideMode == 'offsets'){
22106             ae.removeClass('x-hidden');
22107         }else{
22108             ae.dom.style.display = "";
22109         }
22110     },
22111
22112     /**
22113      * Hide this component.
22114      * @return {Roo.Component} this
22115      */
22116     hide: function(){
22117         if(this.fireEvent("beforehide", this) !== false){
22118             this.hidden = true;
22119             if(this.rendered){
22120                 this.onHide();
22121             }
22122             this.fireEvent("hide", this);
22123         }
22124         return this;
22125     },
22126
22127     // private
22128     onHide : function(){
22129         var ae = this.getActionEl();
22130         if(this.hideMode == 'visibility'){
22131             ae.dom.style.visibility = "hidden";
22132         }else if(this.hideMode == 'offsets'){
22133             ae.addClass('x-hidden');
22134         }else{
22135             ae.dom.style.display = "none";
22136         }
22137     },
22138
22139     /**
22140      * Convenience function to hide or show this component by boolean.
22141      * @param {Boolean} visible True to show, false to hide
22142      * @return {Roo.Component} this
22143      */
22144     setVisible: function(visible){
22145         if(visible) {
22146             this.show();
22147         }else{
22148             this.hide();
22149         }
22150         return this;
22151     },
22152
22153     /**
22154      * Returns true if this component is visible.
22155      */
22156     isVisible : function(){
22157         return this.getActionEl().isVisible();
22158     },
22159
22160     cloneConfig : function(overrides){
22161         overrides = overrides || {};
22162         var id = overrides.id || Roo.id();
22163         var cfg = Roo.applyIf(overrides, this.initialConfig);
22164         cfg.id = id; // prevent dup id
22165         return new this.constructor(cfg);
22166     }
22167 });/*
22168  * Based on:
22169  * Ext JS Library 1.1.1
22170  * Copyright(c) 2006-2007, Ext JS, LLC.
22171  *
22172  * Originally Released Under LGPL - original licence link has changed is not relivant.
22173  *
22174  * Fork - LGPL
22175  * <script type="text/javascript">
22176  */
22177  (function(){ 
22178 /**
22179  * @class Roo.Layer
22180  * @extends Roo.Element
22181  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22182  * automatic maintaining of shadow/shim positions.
22183  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22184  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22185  * you can pass a string with a CSS class name. False turns off the shadow.
22186  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22187  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22188  * @cfg {String} cls CSS class to add to the element
22189  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22190  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22191  * @constructor
22192  * @param {Object} config An object with config options.
22193  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22194  */
22195
22196 Roo.Layer = function(config, existingEl){
22197     config = config || {};
22198     var dh = Roo.DomHelper;
22199     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22200     if(existingEl){
22201         this.dom = Roo.getDom(existingEl);
22202     }
22203     if(!this.dom){
22204         var o = config.dh || {tag: "div", cls: "x-layer"};
22205         this.dom = dh.append(pel, o);
22206     }
22207     if(config.cls){
22208         this.addClass(config.cls);
22209     }
22210     this.constrain = config.constrain !== false;
22211     this.visibilityMode = Roo.Element.VISIBILITY;
22212     if(config.id){
22213         this.id = this.dom.id = config.id;
22214     }else{
22215         this.id = Roo.id(this.dom);
22216     }
22217     this.zindex = config.zindex || this.getZIndex();
22218     this.position("absolute", this.zindex);
22219     if(config.shadow){
22220         this.shadowOffset = config.shadowOffset || 4;
22221         this.shadow = new Roo.Shadow({
22222             offset : this.shadowOffset,
22223             mode : config.shadow
22224         });
22225     }else{
22226         this.shadowOffset = 0;
22227     }
22228     this.useShim = config.shim !== false && Roo.useShims;
22229     this.useDisplay = config.useDisplay;
22230     this.hide();
22231 };
22232
22233 var supr = Roo.Element.prototype;
22234
22235 // shims are shared among layer to keep from having 100 iframes
22236 var shims = [];
22237
22238 Roo.extend(Roo.Layer, Roo.Element, {
22239
22240     getZIndex : function(){
22241         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22242     },
22243
22244     getShim : function(){
22245         if(!this.useShim){
22246             return null;
22247         }
22248         if(this.shim){
22249             return this.shim;
22250         }
22251         var shim = shims.shift();
22252         if(!shim){
22253             shim = this.createShim();
22254             shim.enableDisplayMode('block');
22255             shim.dom.style.display = 'none';
22256             shim.dom.style.visibility = 'visible';
22257         }
22258         var pn = this.dom.parentNode;
22259         if(shim.dom.parentNode != pn){
22260             pn.insertBefore(shim.dom, this.dom);
22261         }
22262         shim.setStyle('z-index', this.getZIndex()-2);
22263         this.shim = shim;
22264         return shim;
22265     },
22266
22267     hideShim : function(){
22268         if(this.shim){
22269             this.shim.setDisplayed(false);
22270             shims.push(this.shim);
22271             delete this.shim;
22272         }
22273     },
22274
22275     disableShadow : function(){
22276         if(this.shadow){
22277             this.shadowDisabled = true;
22278             this.shadow.hide();
22279             this.lastShadowOffset = this.shadowOffset;
22280             this.shadowOffset = 0;
22281         }
22282     },
22283
22284     enableShadow : function(show){
22285         if(this.shadow){
22286             this.shadowDisabled = false;
22287             this.shadowOffset = this.lastShadowOffset;
22288             delete this.lastShadowOffset;
22289             if(show){
22290                 this.sync(true);
22291             }
22292         }
22293     },
22294
22295     // private
22296     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22297     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22298     sync : function(doShow){
22299         var sw = this.shadow;
22300         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22301             var sh = this.getShim();
22302
22303             var w = this.getWidth(),
22304                 h = this.getHeight();
22305
22306             var l = this.getLeft(true),
22307                 t = this.getTop(true);
22308
22309             if(sw && !this.shadowDisabled){
22310                 if(doShow && !sw.isVisible()){
22311                     sw.show(this);
22312                 }else{
22313                     sw.realign(l, t, w, h);
22314                 }
22315                 if(sh){
22316                     if(doShow){
22317                        sh.show();
22318                     }
22319                     // fit the shim behind the shadow, so it is shimmed too
22320                     var a = sw.adjusts, s = sh.dom.style;
22321                     s.left = (Math.min(l, l+a.l))+"px";
22322                     s.top = (Math.min(t, t+a.t))+"px";
22323                     s.width = (w+a.w)+"px";
22324                     s.height = (h+a.h)+"px";
22325                 }
22326             }else if(sh){
22327                 if(doShow){
22328                    sh.show();
22329                 }
22330                 sh.setSize(w, h);
22331                 sh.setLeftTop(l, t);
22332             }
22333             
22334         }
22335     },
22336
22337     // private
22338     destroy : function(){
22339         this.hideShim();
22340         if(this.shadow){
22341             this.shadow.hide();
22342         }
22343         this.removeAllListeners();
22344         var pn = this.dom.parentNode;
22345         if(pn){
22346             pn.removeChild(this.dom);
22347         }
22348         Roo.Element.uncache(this.id);
22349     },
22350
22351     remove : function(){
22352         this.destroy();
22353     },
22354
22355     // private
22356     beginUpdate : function(){
22357         this.updating = true;
22358     },
22359
22360     // private
22361     endUpdate : function(){
22362         this.updating = false;
22363         this.sync(true);
22364     },
22365
22366     // private
22367     hideUnders : function(negOffset){
22368         if(this.shadow){
22369             this.shadow.hide();
22370         }
22371         this.hideShim();
22372     },
22373
22374     // private
22375     constrainXY : function(){
22376         if(this.constrain){
22377             var vw = Roo.lib.Dom.getViewWidth(),
22378                 vh = Roo.lib.Dom.getViewHeight();
22379             var s = Roo.get(document).getScroll();
22380
22381             var xy = this.getXY();
22382             var x = xy[0], y = xy[1];   
22383             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22384             // only move it if it needs it
22385             var moved = false;
22386             // first validate right/bottom
22387             if((x + w) > vw+s.left){
22388                 x = vw - w - this.shadowOffset;
22389                 moved = true;
22390             }
22391             if((y + h) > vh+s.top){
22392                 y = vh - h - this.shadowOffset;
22393                 moved = true;
22394             }
22395             // then make sure top/left isn't negative
22396             if(x < s.left){
22397                 x = s.left;
22398                 moved = true;
22399             }
22400             if(y < s.top){
22401                 y = s.top;
22402                 moved = true;
22403             }
22404             if(moved){
22405                 if(this.avoidY){
22406                     var ay = this.avoidY;
22407                     if(y <= ay && (y+h) >= ay){
22408                         y = ay-h-5;   
22409                     }
22410                 }
22411                 xy = [x, y];
22412                 this.storeXY(xy);
22413                 supr.setXY.call(this, xy);
22414                 this.sync();
22415             }
22416         }
22417     },
22418
22419     isVisible : function(){
22420         return this.visible;    
22421     },
22422
22423     // private
22424     showAction : function(){
22425         this.visible = true; // track visibility to prevent getStyle calls
22426         if(this.useDisplay === true){
22427             this.setDisplayed("");
22428         }else if(this.lastXY){
22429             supr.setXY.call(this, this.lastXY);
22430         }else if(this.lastLT){
22431             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22432         }
22433     },
22434
22435     // private
22436     hideAction : function(){
22437         this.visible = false;
22438         if(this.useDisplay === true){
22439             this.setDisplayed(false);
22440         }else{
22441             this.setLeftTop(-10000,-10000);
22442         }
22443     },
22444
22445     // overridden Element method
22446     setVisible : function(v, a, d, c, e){
22447         if(v){
22448             this.showAction();
22449         }
22450         if(a && v){
22451             var cb = function(){
22452                 this.sync(true);
22453                 if(c){
22454                     c();
22455                 }
22456             }.createDelegate(this);
22457             supr.setVisible.call(this, true, true, d, cb, e);
22458         }else{
22459             if(!v){
22460                 this.hideUnders(true);
22461             }
22462             var cb = c;
22463             if(a){
22464                 cb = function(){
22465                     this.hideAction();
22466                     if(c){
22467                         c();
22468                     }
22469                 }.createDelegate(this);
22470             }
22471             supr.setVisible.call(this, v, a, d, cb, e);
22472             if(v){
22473                 this.sync(true);
22474             }else if(!a){
22475                 this.hideAction();
22476             }
22477         }
22478     },
22479
22480     storeXY : function(xy){
22481         delete this.lastLT;
22482         this.lastXY = xy;
22483     },
22484
22485     storeLeftTop : function(left, top){
22486         delete this.lastXY;
22487         this.lastLT = [left, top];
22488     },
22489
22490     // private
22491     beforeFx : function(){
22492         this.beforeAction();
22493         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22494     },
22495
22496     // private
22497     afterFx : function(){
22498         Roo.Layer.superclass.afterFx.apply(this, arguments);
22499         this.sync(this.isVisible());
22500     },
22501
22502     // private
22503     beforeAction : function(){
22504         if(!this.updating && this.shadow){
22505             this.shadow.hide();
22506         }
22507     },
22508
22509     // overridden Element method
22510     setLeft : function(left){
22511         this.storeLeftTop(left, this.getTop(true));
22512         supr.setLeft.apply(this, arguments);
22513         this.sync();
22514     },
22515
22516     setTop : function(top){
22517         this.storeLeftTop(this.getLeft(true), top);
22518         supr.setTop.apply(this, arguments);
22519         this.sync();
22520     },
22521
22522     setLeftTop : function(left, top){
22523         this.storeLeftTop(left, top);
22524         supr.setLeftTop.apply(this, arguments);
22525         this.sync();
22526     },
22527
22528     setXY : function(xy, a, d, c, e){
22529         this.fixDisplay();
22530         this.beforeAction();
22531         this.storeXY(xy);
22532         var cb = this.createCB(c);
22533         supr.setXY.call(this, xy, a, d, cb, e);
22534         if(!a){
22535             cb();
22536         }
22537     },
22538
22539     // private
22540     createCB : function(c){
22541         var el = this;
22542         return function(){
22543             el.constrainXY();
22544             el.sync(true);
22545             if(c){
22546                 c();
22547             }
22548         };
22549     },
22550
22551     // overridden Element method
22552     setX : function(x, a, d, c, e){
22553         this.setXY([x, this.getY()], a, d, c, e);
22554     },
22555
22556     // overridden Element method
22557     setY : function(y, a, d, c, e){
22558         this.setXY([this.getX(), y], a, d, c, e);
22559     },
22560
22561     // overridden Element method
22562     setSize : function(w, h, a, d, c, e){
22563         this.beforeAction();
22564         var cb = this.createCB(c);
22565         supr.setSize.call(this, w, h, a, d, cb, e);
22566         if(!a){
22567             cb();
22568         }
22569     },
22570
22571     // overridden Element method
22572     setWidth : function(w, a, d, c, e){
22573         this.beforeAction();
22574         var cb = this.createCB(c);
22575         supr.setWidth.call(this, w, a, d, cb, e);
22576         if(!a){
22577             cb();
22578         }
22579     },
22580
22581     // overridden Element method
22582     setHeight : function(h, a, d, c, e){
22583         this.beforeAction();
22584         var cb = this.createCB(c);
22585         supr.setHeight.call(this, h, a, d, cb, e);
22586         if(!a){
22587             cb();
22588         }
22589     },
22590
22591     // overridden Element method
22592     setBounds : function(x, y, w, h, a, d, c, e){
22593         this.beforeAction();
22594         var cb = this.createCB(c);
22595         if(!a){
22596             this.storeXY([x, y]);
22597             supr.setXY.call(this, [x, y]);
22598             supr.setSize.call(this, w, h, a, d, cb, e);
22599             cb();
22600         }else{
22601             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22602         }
22603         return this;
22604     },
22605     
22606     /**
22607      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22608      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22609      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22610      * @param {Number} zindex The new z-index to set
22611      * @return {this} The Layer
22612      */
22613     setZIndex : function(zindex){
22614         this.zindex = zindex;
22615         this.setStyle("z-index", zindex + 2);
22616         if(this.shadow){
22617             this.shadow.setZIndex(zindex + 1);
22618         }
22619         if(this.shim){
22620             this.shim.setStyle("z-index", zindex);
22621         }
22622     }
22623 });
22624 })();/*
22625  * Based on:
22626  * Ext JS Library 1.1.1
22627  * Copyright(c) 2006-2007, Ext JS, LLC.
22628  *
22629  * Originally Released Under LGPL - original licence link has changed is not relivant.
22630  *
22631  * Fork - LGPL
22632  * <script type="text/javascript">
22633  */
22634
22635
22636 /**
22637  * @class Roo.Shadow
22638  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22639  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22640  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22641  * @constructor
22642  * Create a new Shadow
22643  * @param {Object} config The config object
22644  */
22645 Roo.Shadow = function(config){
22646     Roo.apply(this, config);
22647     if(typeof this.mode != "string"){
22648         this.mode = this.defaultMode;
22649     }
22650     var o = this.offset, a = {h: 0};
22651     var rad = Math.floor(this.offset/2);
22652     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22653         case "drop":
22654             a.w = 0;
22655             a.l = a.t = o;
22656             a.t -= 1;
22657             if(Roo.isIE){
22658                 a.l -= this.offset + rad;
22659                 a.t -= this.offset + rad;
22660                 a.w -= rad;
22661                 a.h -= rad;
22662                 a.t += 1;
22663             }
22664         break;
22665         case "sides":
22666             a.w = (o*2);
22667             a.l = -o;
22668             a.t = o-1;
22669             if(Roo.isIE){
22670                 a.l -= (this.offset - rad);
22671                 a.t -= this.offset + rad;
22672                 a.l += 1;
22673                 a.w -= (this.offset - rad)*2;
22674                 a.w -= rad + 1;
22675                 a.h -= 1;
22676             }
22677         break;
22678         case "frame":
22679             a.w = a.h = (o*2);
22680             a.l = a.t = -o;
22681             a.t += 1;
22682             a.h -= 2;
22683             if(Roo.isIE){
22684                 a.l -= (this.offset - rad);
22685                 a.t -= (this.offset - rad);
22686                 a.l += 1;
22687                 a.w -= (this.offset + rad + 1);
22688                 a.h -= (this.offset + rad);
22689                 a.h += 1;
22690             }
22691         break;
22692     };
22693
22694     this.adjusts = a;
22695 };
22696
22697 Roo.Shadow.prototype = {
22698     /**
22699      * @cfg {String} mode
22700      * The shadow display mode.  Supports the following options:<br />
22701      * sides: Shadow displays on both sides and bottom only<br />
22702      * frame: Shadow displays equally on all four sides<br />
22703      * drop: Traditional bottom-right drop shadow (default)
22704      */
22705     /**
22706      * @cfg {String} offset
22707      * The number of pixels to offset the shadow from the element (defaults to 4)
22708      */
22709     offset: 4,
22710
22711     // private
22712     defaultMode: "drop",
22713
22714     /**
22715      * Displays the shadow under the target element
22716      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22717      */
22718     show : function(target){
22719         target = Roo.get(target);
22720         if(!this.el){
22721             this.el = Roo.Shadow.Pool.pull();
22722             if(this.el.dom.nextSibling != target.dom){
22723                 this.el.insertBefore(target);
22724             }
22725         }
22726         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22727         if(Roo.isIE){
22728             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22729         }
22730         this.realign(
22731             target.getLeft(true),
22732             target.getTop(true),
22733             target.getWidth(),
22734             target.getHeight()
22735         );
22736         this.el.dom.style.display = "block";
22737     },
22738
22739     /**
22740      * Returns true if the shadow is visible, else false
22741      */
22742     isVisible : function(){
22743         return this.el ? true : false;  
22744     },
22745
22746     /**
22747      * Direct alignment when values are already available. Show must be called at least once before
22748      * calling this method to ensure it is initialized.
22749      * @param {Number} left The target element left position
22750      * @param {Number} top The target element top position
22751      * @param {Number} width The target element width
22752      * @param {Number} height The target element height
22753      */
22754     realign : function(l, t, w, h){
22755         if(!this.el){
22756             return;
22757         }
22758         var a = this.adjusts, d = this.el.dom, s = d.style;
22759         var iea = 0;
22760         s.left = (l+a.l)+"px";
22761         s.top = (t+a.t)+"px";
22762         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22763  
22764         if(s.width != sws || s.height != shs){
22765             s.width = sws;
22766             s.height = shs;
22767             if(!Roo.isIE){
22768                 var cn = d.childNodes;
22769                 var sww = Math.max(0, (sw-12))+"px";
22770                 cn[0].childNodes[1].style.width = sww;
22771                 cn[1].childNodes[1].style.width = sww;
22772                 cn[2].childNodes[1].style.width = sww;
22773                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22774             }
22775         }
22776     },
22777
22778     /**
22779      * Hides this shadow
22780      */
22781     hide : function(){
22782         if(this.el){
22783             this.el.dom.style.display = "none";
22784             Roo.Shadow.Pool.push(this.el);
22785             delete this.el;
22786         }
22787     },
22788
22789     /**
22790      * Adjust the z-index of this shadow
22791      * @param {Number} zindex The new z-index
22792      */
22793     setZIndex : function(z){
22794         this.zIndex = z;
22795         if(this.el){
22796             this.el.setStyle("z-index", z);
22797         }
22798     }
22799 };
22800
22801 // Private utility class that manages the internal Shadow cache
22802 Roo.Shadow.Pool = function(){
22803     var p = [];
22804     var markup = Roo.isIE ?
22805                  '<div class="x-ie-shadow"></div>' :
22806                  '<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>';
22807     return {
22808         pull : function(){
22809             var sh = p.shift();
22810             if(!sh){
22811                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22812                 sh.autoBoxAdjust = false;
22813             }
22814             return sh;
22815         },
22816
22817         push : function(sh){
22818             p.push(sh);
22819         }
22820     };
22821 }();/*
22822  * Based on:
22823  * Ext JS Library 1.1.1
22824  * Copyright(c) 2006-2007, Ext JS, LLC.
22825  *
22826  * Originally Released Under LGPL - original licence link has changed is not relivant.
22827  *
22828  * Fork - LGPL
22829  * <script type="text/javascript">
22830  */
22831
22832 /**
22833  * @class Roo.BoxComponent
22834  * @extends Roo.Component
22835  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22836  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22837  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22838  * layout containers.
22839  * @constructor
22840  * @param {Roo.Element/String/Object} config The configuration options.
22841  */
22842 Roo.BoxComponent = function(config){
22843     Roo.Component.call(this, config);
22844     this.addEvents({
22845         /**
22846          * @event resize
22847          * Fires after the component is resized.
22848              * @param {Roo.Component} this
22849              * @param {Number} adjWidth The box-adjusted width that was set
22850              * @param {Number} adjHeight The box-adjusted height that was set
22851              * @param {Number} rawWidth The width that was originally specified
22852              * @param {Number} rawHeight The height that was originally specified
22853              */
22854         resize : true,
22855         /**
22856          * @event move
22857          * Fires after the component is moved.
22858              * @param {Roo.Component} this
22859              * @param {Number} x The new x position
22860              * @param {Number} y The new y position
22861              */
22862         move : true
22863     });
22864 };
22865
22866 Roo.extend(Roo.BoxComponent, Roo.Component, {
22867     // private, set in afterRender to signify that the component has been rendered
22868     boxReady : false,
22869     // private, used to defer height settings to subclasses
22870     deferHeight: false,
22871     /** @cfg {Number} width
22872      * width (optional) size of component
22873      */
22874      /** @cfg {Number} height
22875      * height (optional) size of component
22876      */
22877      
22878     /**
22879      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22880      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22881      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22882      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22883      * @return {Roo.BoxComponent} this
22884      */
22885     setSize : function(w, h){
22886         // support for standard size objects
22887         if(typeof w == 'object'){
22888             h = w.height;
22889             w = w.width;
22890         }
22891         // not rendered
22892         if(!this.boxReady){
22893             this.width = w;
22894             this.height = h;
22895             return this;
22896         }
22897
22898         // prevent recalcs when not needed
22899         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22900             return this;
22901         }
22902         this.lastSize = {width: w, height: h};
22903
22904         var adj = this.adjustSize(w, h);
22905         var aw = adj.width, ah = adj.height;
22906         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22907             var rz = this.getResizeEl();
22908             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22909                 rz.setSize(aw, ah);
22910             }else if(!this.deferHeight && ah !== undefined){
22911                 rz.setHeight(ah);
22912             }else if(aw !== undefined){
22913                 rz.setWidth(aw);
22914             }
22915             this.onResize(aw, ah, w, h);
22916             this.fireEvent('resize', this, aw, ah, w, h);
22917         }
22918         return this;
22919     },
22920
22921     /**
22922      * Gets the current size of the component's underlying element.
22923      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22924      */
22925     getSize : function(){
22926         return this.el.getSize();
22927     },
22928
22929     /**
22930      * Gets the current XY position of the component's underlying element.
22931      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22932      * @return {Array} The XY position of the element (e.g., [100, 200])
22933      */
22934     getPosition : function(local){
22935         if(local === true){
22936             return [this.el.getLeft(true), this.el.getTop(true)];
22937         }
22938         return this.xy || this.el.getXY();
22939     },
22940
22941     /**
22942      * Gets the current box measurements of the component's underlying element.
22943      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22944      * @returns {Object} box An object in the format {x, y, width, height}
22945      */
22946     getBox : function(local){
22947         var s = this.el.getSize();
22948         if(local){
22949             s.x = this.el.getLeft(true);
22950             s.y = this.el.getTop(true);
22951         }else{
22952             var xy = this.xy || this.el.getXY();
22953             s.x = xy[0];
22954             s.y = xy[1];
22955         }
22956         return s;
22957     },
22958
22959     /**
22960      * Sets the current box measurements of the component's underlying element.
22961      * @param {Object} box An object in the format {x, y, width, height}
22962      * @returns {Roo.BoxComponent} this
22963      */
22964     updateBox : function(box){
22965         this.setSize(box.width, box.height);
22966         this.setPagePosition(box.x, box.y);
22967         return this;
22968     },
22969
22970     // protected
22971     getResizeEl : function(){
22972         return this.resizeEl || this.el;
22973     },
22974
22975     // protected
22976     getPositionEl : function(){
22977         return this.positionEl || this.el;
22978     },
22979
22980     /**
22981      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22982      * This method fires the move event.
22983      * @param {Number} left The new left
22984      * @param {Number} top The new top
22985      * @returns {Roo.BoxComponent} this
22986      */
22987     setPosition : function(x, y){
22988         this.x = x;
22989         this.y = y;
22990         if(!this.boxReady){
22991             return this;
22992         }
22993         var adj = this.adjustPosition(x, y);
22994         var ax = adj.x, ay = adj.y;
22995
22996         var el = this.getPositionEl();
22997         if(ax !== undefined || ay !== undefined){
22998             if(ax !== undefined && ay !== undefined){
22999                 el.setLeftTop(ax, ay);
23000             }else if(ax !== undefined){
23001                 el.setLeft(ax);
23002             }else if(ay !== undefined){
23003                 el.setTop(ay);
23004             }
23005             this.onPosition(ax, ay);
23006             this.fireEvent('move', this, ax, ay);
23007         }
23008         return this;
23009     },
23010
23011     /**
23012      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23013      * This method fires the move event.
23014      * @param {Number} x The new x position
23015      * @param {Number} y The new y position
23016      * @returns {Roo.BoxComponent} this
23017      */
23018     setPagePosition : function(x, y){
23019         this.pageX = x;
23020         this.pageY = y;
23021         if(!this.boxReady){
23022             return;
23023         }
23024         if(x === undefined || y === undefined){ // cannot translate undefined points
23025             return;
23026         }
23027         var p = this.el.translatePoints(x, y);
23028         this.setPosition(p.left, p.top);
23029         return this;
23030     },
23031
23032     // private
23033     onRender : function(ct, position){
23034         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23035         if(this.resizeEl){
23036             this.resizeEl = Roo.get(this.resizeEl);
23037         }
23038         if(this.positionEl){
23039             this.positionEl = Roo.get(this.positionEl);
23040         }
23041     },
23042
23043     // private
23044     afterRender : function(){
23045         Roo.BoxComponent.superclass.afterRender.call(this);
23046         this.boxReady = true;
23047         this.setSize(this.width, this.height);
23048         if(this.x || this.y){
23049             this.setPosition(this.x, this.y);
23050         }
23051         if(this.pageX || this.pageY){
23052             this.setPagePosition(this.pageX, this.pageY);
23053         }
23054     },
23055
23056     /**
23057      * Force the component's size to recalculate based on the underlying element's current height and width.
23058      * @returns {Roo.BoxComponent} this
23059      */
23060     syncSize : function(){
23061         delete this.lastSize;
23062         this.setSize(this.el.getWidth(), this.el.getHeight());
23063         return this;
23064     },
23065
23066     /**
23067      * Called after the component is resized, this method is empty by default but can be implemented by any
23068      * subclass that needs to perform custom logic after a resize occurs.
23069      * @param {Number} adjWidth The box-adjusted width that was set
23070      * @param {Number} adjHeight The box-adjusted height that was set
23071      * @param {Number} rawWidth The width that was originally specified
23072      * @param {Number} rawHeight The height that was originally specified
23073      */
23074     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23075
23076     },
23077
23078     /**
23079      * Called after the component is moved, this method is empty by default but can be implemented by any
23080      * subclass that needs to perform custom logic after a move occurs.
23081      * @param {Number} x The new x position
23082      * @param {Number} y The new y position
23083      */
23084     onPosition : function(x, y){
23085
23086     },
23087
23088     // private
23089     adjustSize : function(w, h){
23090         if(this.autoWidth){
23091             w = 'auto';
23092         }
23093         if(this.autoHeight){
23094             h = 'auto';
23095         }
23096         return {width : w, height: h};
23097     },
23098
23099     // private
23100     adjustPosition : function(x, y){
23101         return {x : x, y: y};
23102     }
23103 });/*
23104  * Based on:
23105  * Ext JS Library 1.1.1
23106  * Copyright(c) 2006-2007, Ext JS, LLC.
23107  *
23108  * Originally Released Under LGPL - original licence link has changed is not relivant.
23109  *
23110  * Fork - LGPL
23111  * <script type="text/javascript">
23112  */
23113
23114
23115 /**
23116  * @class Roo.SplitBar
23117  * @extends Roo.util.Observable
23118  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23119  * <br><br>
23120  * Usage:
23121  * <pre><code>
23122 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23123                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23124 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23125 split.minSize = 100;
23126 split.maxSize = 600;
23127 split.animate = true;
23128 split.on('moved', splitterMoved);
23129 </code></pre>
23130  * @constructor
23131  * Create a new SplitBar
23132  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23133  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23134  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23135  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23136                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23137                         position of the SplitBar).
23138  */
23139 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23140     
23141     /** @private */
23142     this.el = Roo.get(dragElement, true);
23143     this.el.dom.unselectable = "on";
23144     /** @private */
23145     this.resizingEl = Roo.get(resizingElement, true);
23146
23147     /**
23148      * @private
23149      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23150      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23151      * @type Number
23152      */
23153     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23154     
23155     /**
23156      * The minimum size of the resizing element. (Defaults to 0)
23157      * @type Number
23158      */
23159     this.minSize = 0;
23160     
23161     /**
23162      * The maximum size of the resizing element. (Defaults to 2000)
23163      * @type Number
23164      */
23165     this.maxSize = 2000;
23166     
23167     /**
23168      * Whether to animate the transition to the new size
23169      * @type Boolean
23170      */
23171     this.animate = false;
23172     
23173     /**
23174      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23175      * @type Boolean
23176      */
23177     this.useShim = false;
23178     
23179     /** @private */
23180     this.shim = null;
23181     
23182     if(!existingProxy){
23183         /** @private */
23184         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23185     }else{
23186         this.proxy = Roo.get(existingProxy).dom;
23187     }
23188     /** @private */
23189     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23190     
23191     /** @private */
23192     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23193     
23194     /** @private */
23195     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23196     
23197     /** @private */
23198     this.dragSpecs = {};
23199     
23200     /**
23201      * @private The adapter to use to positon and resize elements
23202      */
23203     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23204     this.adapter.init(this);
23205     
23206     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23207         /** @private */
23208         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23209         this.el.addClass("x-splitbar-h");
23210     }else{
23211         /** @private */
23212         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23213         this.el.addClass("x-splitbar-v");
23214     }
23215     
23216     this.addEvents({
23217         /**
23218          * @event resize
23219          * Fires when the splitter is moved (alias for {@link #event-moved})
23220          * @param {Roo.SplitBar} this
23221          * @param {Number} newSize the new width or height
23222          */
23223         "resize" : true,
23224         /**
23225          * @event moved
23226          * Fires when the splitter is moved
23227          * @param {Roo.SplitBar} this
23228          * @param {Number} newSize the new width or height
23229          */
23230         "moved" : true,
23231         /**
23232          * @event beforeresize
23233          * Fires before the splitter is dragged
23234          * @param {Roo.SplitBar} this
23235          */
23236         "beforeresize" : true,
23237
23238         "beforeapply" : true
23239     });
23240
23241     Roo.util.Observable.call(this);
23242 };
23243
23244 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23245     onStartProxyDrag : function(x, y){
23246         this.fireEvent("beforeresize", this);
23247         if(!this.overlay){
23248             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23249             o.unselectable();
23250             o.enableDisplayMode("block");
23251             // all splitbars share the same overlay
23252             Roo.SplitBar.prototype.overlay = o;
23253         }
23254         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23255         this.overlay.show();
23256         Roo.get(this.proxy).setDisplayed("block");
23257         var size = this.adapter.getElementSize(this);
23258         this.activeMinSize = this.getMinimumSize();;
23259         this.activeMaxSize = this.getMaximumSize();;
23260         var c1 = size - this.activeMinSize;
23261         var c2 = Math.max(this.activeMaxSize - size, 0);
23262         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23263             this.dd.resetConstraints();
23264             this.dd.setXConstraint(
23265                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23266                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23267             );
23268             this.dd.setYConstraint(0, 0);
23269         }else{
23270             this.dd.resetConstraints();
23271             this.dd.setXConstraint(0, 0);
23272             this.dd.setYConstraint(
23273                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23274                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23275             );
23276          }
23277         this.dragSpecs.startSize = size;
23278         this.dragSpecs.startPoint = [x, y];
23279         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23280     },
23281     
23282     /** 
23283      * @private Called after the drag operation by the DDProxy
23284      */
23285     onEndProxyDrag : function(e){
23286         Roo.get(this.proxy).setDisplayed(false);
23287         var endPoint = Roo.lib.Event.getXY(e);
23288         if(this.overlay){
23289             this.overlay.hide();
23290         }
23291         var newSize;
23292         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23293             newSize = this.dragSpecs.startSize + 
23294                 (this.placement == Roo.SplitBar.LEFT ?
23295                     endPoint[0] - this.dragSpecs.startPoint[0] :
23296                     this.dragSpecs.startPoint[0] - endPoint[0]
23297                 );
23298         }else{
23299             newSize = this.dragSpecs.startSize + 
23300                 (this.placement == Roo.SplitBar.TOP ?
23301                     endPoint[1] - this.dragSpecs.startPoint[1] :
23302                     this.dragSpecs.startPoint[1] - endPoint[1]
23303                 );
23304         }
23305         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23306         if(newSize != this.dragSpecs.startSize){
23307             if(this.fireEvent('beforeapply', this, newSize) !== false){
23308                 this.adapter.setElementSize(this, newSize);
23309                 this.fireEvent("moved", this, newSize);
23310                 this.fireEvent("resize", this, newSize);
23311             }
23312         }
23313     },
23314     
23315     /**
23316      * Get the adapter this SplitBar uses
23317      * @return The adapter object
23318      */
23319     getAdapter : function(){
23320         return this.adapter;
23321     },
23322     
23323     /**
23324      * Set the adapter this SplitBar uses
23325      * @param {Object} adapter A SplitBar adapter object
23326      */
23327     setAdapter : function(adapter){
23328         this.adapter = adapter;
23329         this.adapter.init(this);
23330     },
23331     
23332     /**
23333      * Gets the minimum size for the resizing element
23334      * @return {Number} The minimum size
23335      */
23336     getMinimumSize : function(){
23337         return this.minSize;
23338     },
23339     
23340     /**
23341      * Sets the minimum size for the resizing element
23342      * @param {Number} minSize The minimum size
23343      */
23344     setMinimumSize : function(minSize){
23345         this.minSize = minSize;
23346     },
23347     
23348     /**
23349      * Gets the maximum size for the resizing element
23350      * @return {Number} The maximum size
23351      */
23352     getMaximumSize : function(){
23353         return this.maxSize;
23354     },
23355     
23356     /**
23357      * Sets the maximum size for the resizing element
23358      * @param {Number} maxSize The maximum size
23359      */
23360     setMaximumSize : function(maxSize){
23361         this.maxSize = maxSize;
23362     },
23363     
23364     /**
23365      * Sets the initialize size for the resizing element
23366      * @param {Number} size The initial size
23367      */
23368     setCurrentSize : function(size){
23369         var oldAnimate = this.animate;
23370         this.animate = false;
23371         this.adapter.setElementSize(this, size);
23372         this.animate = oldAnimate;
23373     },
23374     
23375     /**
23376      * Destroy this splitbar. 
23377      * @param {Boolean} removeEl True to remove the element
23378      */
23379     destroy : function(removeEl){
23380         if(this.shim){
23381             this.shim.remove();
23382         }
23383         this.dd.unreg();
23384         this.proxy.parentNode.removeChild(this.proxy);
23385         if(removeEl){
23386             this.el.remove();
23387         }
23388     }
23389 });
23390
23391 /**
23392  * @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.
23393  */
23394 Roo.SplitBar.createProxy = function(dir){
23395     var proxy = new Roo.Element(document.createElement("div"));
23396     proxy.unselectable();
23397     var cls = 'x-splitbar-proxy';
23398     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23399     document.body.appendChild(proxy.dom);
23400     return proxy.dom;
23401 };
23402
23403 /** 
23404  * @class Roo.SplitBar.BasicLayoutAdapter
23405  * Default Adapter. It assumes the splitter and resizing element are not positioned
23406  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23407  */
23408 Roo.SplitBar.BasicLayoutAdapter = function(){
23409 };
23410
23411 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23412     // do nothing for now
23413     init : function(s){
23414     
23415     },
23416     /**
23417      * Called before drag operations to get the current size of the resizing element. 
23418      * @param {Roo.SplitBar} s The SplitBar using this adapter
23419      */
23420      getElementSize : function(s){
23421         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23422             return s.resizingEl.getWidth();
23423         }else{
23424             return s.resizingEl.getHeight();
23425         }
23426     },
23427     
23428     /**
23429      * Called after drag operations to set the size of the resizing element.
23430      * @param {Roo.SplitBar} s The SplitBar using this adapter
23431      * @param {Number} newSize The new size to set
23432      * @param {Function} onComplete A function to be invoked when resizing is complete
23433      */
23434     setElementSize : function(s, newSize, onComplete){
23435         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23436             if(!s.animate){
23437                 s.resizingEl.setWidth(newSize);
23438                 if(onComplete){
23439                     onComplete(s, newSize);
23440                 }
23441             }else{
23442                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23443             }
23444         }else{
23445             
23446             if(!s.animate){
23447                 s.resizingEl.setHeight(newSize);
23448                 if(onComplete){
23449                     onComplete(s, newSize);
23450                 }
23451             }else{
23452                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23453             }
23454         }
23455     }
23456 };
23457
23458 /** 
23459  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23460  * @extends Roo.SplitBar.BasicLayoutAdapter
23461  * Adapter that  moves the splitter element to align with the resized sizing element. 
23462  * Used with an absolute positioned SplitBar.
23463  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23464  * document.body, make sure you assign an id to the body element.
23465  */
23466 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23467     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23468     this.container = Roo.get(container);
23469 };
23470
23471 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23472     init : function(s){
23473         this.basic.init(s);
23474     },
23475     
23476     getElementSize : function(s){
23477         return this.basic.getElementSize(s);
23478     },
23479     
23480     setElementSize : function(s, newSize, onComplete){
23481         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23482     },
23483     
23484     moveSplitter : function(s){
23485         var yes = Roo.SplitBar;
23486         switch(s.placement){
23487             case yes.LEFT:
23488                 s.el.setX(s.resizingEl.getRight());
23489                 break;
23490             case yes.RIGHT:
23491                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23492                 break;
23493             case yes.TOP:
23494                 s.el.setY(s.resizingEl.getBottom());
23495                 break;
23496             case yes.BOTTOM:
23497                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23498                 break;
23499         }
23500     }
23501 };
23502
23503 /**
23504  * Orientation constant - Create a vertical SplitBar
23505  * @static
23506  * @type Number
23507  */
23508 Roo.SplitBar.VERTICAL = 1;
23509
23510 /**
23511  * Orientation constant - Create a horizontal SplitBar
23512  * @static
23513  * @type Number
23514  */
23515 Roo.SplitBar.HORIZONTAL = 2;
23516
23517 /**
23518  * Placement constant - The resizing element is to the left of the splitter element
23519  * @static
23520  * @type Number
23521  */
23522 Roo.SplitBar.LEFT = 1;
23523
23524 /**
23525  * Placement constant - The resizing element is to the right of the splitter element
23526  * @static
23527  * @type Number
23528  */
23529 Roo.SplitBar.RIGHT = 2;
23530
23531 /**
23532  * Placement constant - The resizing element is positioned above the splitter element
23533  * @static
23534  * @type Number
23535  */
23536 Roo.SplitBar.TOP = 3;
23537
23538 /**
23539  * Placement constant - The resizing element is positioned under splitter element
23540  * @static
23541  * @type Number
23542  */
23543 Roo.SplitBar.BOTTOM = 4;
23544 /*
23545  * Based on:
23546  * Ext JS Library 1.1.1
23547  * Copyright(c) 2006-2007, Ext JS, LLC.
23548  *
23549  * Originally Released Under LGPL - original licence link has changed is not relivant.
23550  *
23551  * Fork - LGPL
23552  * <script type="text/javascript">
23553  */
23554
23555 /**
23556  * @class Roo.View
23557  * @extends Roo.util.Observable
23558  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23559  * This class also supports single and multi selection modes. <br>
23560  * Create a data model bound view:
23561  <pre><code>
23562  var store = new Roo.data.Store(...);
23563
23564  var view = new Roo.View({
23565     el : "my-element",
23566     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23567  
23568     singleSelect: true,
23569     selectedClass: "ydataview-selected",
23570     store: store
23571  });
23572
23573  // listen for node click?
23574  view.on("click", function(vw, index, node, e){
23575  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23576  });
23577
23578  // load XML data
23579  dataModel.load("foobar.xml");
23580  </code></pre>
23581  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23582  * <br><br>
23583  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23584  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23585  * 
23586  * Note: old style constructor is still suported (container, template, config)
23587  * 
23588  * @constructor
23589  * Create a new View
23590  * @param {Object} config The config object
23591  * 
23592  */
23593 Roo.View = function(config, depreciated_tpl, depreciated_config){
23594     
23595     if (typeof(depreciated_tpl) == 'undefined') {
23596         // new way.. - universal constructor.
23597         Roo.apply(this, config);
23598         this.el  = Roo.get(this.el);
23599     } else {
23600         // old format..
23601         this.el  = Roo.get(config);
23602         this.tpl = depreciated_tpl;
23603         Roo.apply(this, depreciated_config);
23604     }
23605      
23606     
23607     if(typeof(this.tpl) == "string"){
23608         this.tpl = new Roo.Template(this.tpl);
23609     } else {
23610         // support xtype ctors..
23611         this.tpl = new Roo.factory(this.tpl, Roo);
23612     }
23613     
23614     
23615     this.tpl.compile();
23616    
23617
23618      
23619     /** @private */
23620     this.addEvents({
23621     /**
23622      * @event beforeclick
23623      * Fires before a click is processed. Returns false to cancel the default action.
23624      * @param {Roo.View} this
23625      * @param {Number} index The index of the target node
23626      * @param {HTMLElement} node The target node
23627      * @param {Roo.EventObject} e The raw event object
23628      */
23629         "beforeclick" : true,
23630     /**
23631      * @event click
23632      * Fires when a template node is clicked.
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         "click" : true,
23639     /**
23640      * @event dblclick
23641      * Fires when a template node is double 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         "dblclick" : true,
23648     /**
23649      * @event contextmenu
23650      * Fires when a template node is right 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         "contextmenu" : true,
23657     /**
23658      * @event selectionchange
23659      * Fires when the selected nodes change.
23660      * @param {Roo.View} this
23661      * @param {Array} selections Array of the selected nodes
23662      */
23663         "selectionchange" : true,
23664
23665     /**
23666      * @event beforeselect
23667      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23668      * @param {Roo.View} this
23669      * @param {HTMLElement} node The node to be selected
23670      * @param {Array} selections Array of currently selected nodes
23671      */
23672         "beforeselect" : true
23673     });
23674
23675     this.el.on({
23676         "click": this.onClick,
23677         "dblclick": this.onDblClick,
23678         "contextmenu": this.onContextMenu,
23679         scope:this
23680     });
23681
23682     this.selections = [];
23683     this.nodes = [];
23684     this.cmp = new Roo.CompositeElementLite([]);
23685     if(this.store){
23686         this.store = Roo.factory(this.store, Roo.data);
23687         this.setStore(this.store, true);
23688     }
23689     Roo.View.superclass.constructor.call(this);
23690 };
23691
23692 Roo.extend(Roo.View, Roo.util.Observable, {
23693     
23694      /**
23695      * @cfg {Roo.data.Store} store Data store to load data from.
23696      */
23697     store : false,
23698     
23699     /**
23700      * @cfg {String|Roo.Element} el The container element.
23701      */
23702     el : '',
23703     
23704     /**
23705      * @cfg {String|Roo.Template} tpl The template used by this View 
23706      */
23707     tpl : false,
23708     
23709     /**
23710      * @cfg {String} selectedClass The css class to add to selected nodes
23711      */
23712     selectedClass : "x-view-selected",
23713      /**
23714      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23715      */
23716     emptyText : "",
23717     /**
23718      * @cfg {Boolean} multiSelect Allow multiple selection
23719      */
23720     
23721     multiSelect : false,
23722     /**
23723      * @cfg {Boolean} singleSelect Allow single selection
23724      */
23725     singleSelect:  false,
23726     
23727     /**
23728      * Returns the element this view is bound to.
23729      * @return {Roo.Element}
23730      */
23731     getEl : function(){
23732         return this.el;
23733     },
23734
23735     /**
23736      * Refreshes the view.
23737      */
23738     refresh : function(){
23739         var t = this.tpl;
23740         this.clearSelections();
23741         this.el.update("");
23742         var html = [];
23743         var records = this.store.getRange();
23744         if(records.length < 1){
23745             this.el.update(this.emptyText);
23746             return;
23747         }
23748         for(var i = 0, len = records.length; i < len; i++){
23749             var data = this.prepareData(records[i].data, i, records[i]);
23750             html[html.length] = t.apply(data);
23751         }
23752         this.el.update(html.join(""));
23753         this.nodes = this.el.dom.childNodes;
23754         this.updateIndexes(0);
23755     },
23756
23757     /**
23758      * Function to override to reformat the data that is sent to
23759      * the template for each node.
23760      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23761      * a JSON object for an UpdateManager bound view).
23762      */
23763     prepareData : function(data){
23764         return data;
23765     },
23766
23767     onUpdate : function(ds, record){
23768         this.clearSelections();
23769         var index = this.store.indexOf(record);
23770         var n = this.nodes[index];
23771         this.tpl.insertBefore(n, this.prepareData(record.data));
23772         n.parentNode.removeChild(n);
23773         this.updateIndexes(index, index);
23774     },
23775
23776     onAdd : function(ds, records, index){
23777         this.clearSelections();
23778         if(this.nodes.length == 0){
23779             this.refresh();
23780             return;
23781         }
23782         var n = this.nodes[index];
23783         for(var i = 0, len = records.length; i < len; i++){
23784             var d = this.prepareData(records[i].data);
23785             if(n){
23786                 this.tpl.insertBefore(n, d);
23787             }else{
23788                 this.tpl.append(this.el, d);
23789             }
23790         }
23791         this.updateIndexes(index);
23792     },
23793
23794     onRemove : function(ds, record, index){
23795         this.clearSelections();
23796         this.el.dom.removeChild(this.nodes[index]);
23797         this.updateIndexes(index);
23798     },
23799
23800     /**
23801      * Refresh an individual node.
23802      * @param {Number} index
23803      */
23804     refreshNode : function(index){
23805         this.onUpdate(this.store, this.store.getAt(index));
23806     },
23807
23808     updateIndexes : function(startIndex, endIndex){
23809         var ns = this.nodes;
23810         startIndex = startIndex || 0;
23811         endIndex = endIndex || ns.length - 1;
23812         for(var i = startIndex; i <= endIndex; i++){
23813             ns[i].nodeIndex = i;
23814         }
23815     },
23816
23817     /**
23818      * Changes the data store this view uses and refresh the view.
23819      * @param {Store} store
23820      */
23821     setStore : function(store, initial){
23822         if(!initial && this.store){
23823             this.store.un("datachanged", this.refresh);
23824             this.store.un("add", this.onAdd);
23825             this.store.un("remove", this.onRemove);
23826             this.store.un("update", this.onUpdate);
23827             this.store.un("clear", this.refresh);
23828         }
23829         if(store){
23830           
23831             store.on("datachanged", this.refresh, this);
23832             store.on("add", this.onAdd, this);
23833             store.on("remove", this.onRemove, this);
23834             store.on("update", this.onUpdate, this);
23835             store.on("clear", this.refresh, this);
23836         }
23837         
23838         if(store){
23839             this.refresh();
23840         }
23841     },
23842
23843     /**
23844      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23845      * @param {HTMLElement} node
23846      * @return {HTMLElement} The template node
23847      */
23848     findItemFromChild : function(node){
23849         var el = this.el.dom;
23850         if(!node || node.parentNode == el){
23851                     return node;
23852             }
23853             var p = node.parentNode;
23854             while(p && p != el){
23855             if(p.parentNode == el){
23856                 return p;
23857             }
23858             p = p.parentNode;
23859         }
23860             return null;
23861     },
23862
23863     /** @ignore */
23864     onClick : function(e){
23865         var item = this.findItemFromChild(e.getTarget());
23866         if(item){
23867             var index = this.indexOf(item);
23868             if(this.onItemClick(item, index, e) !== false){
23869                 this.fireEvent("click", this, index, item, e);
23870             }
23871         }else{
23872             this.clearSelections();
23873         }
23874     },
23875
23876     /** @ignore */
23877     onContextMenu : function(e){
23878         var item = this.findItemFromChild(e.getTarget());
23879         if(item){
23880             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23881         }
23882     },
23883
23884     /** @ignore */
23885     onDblClick : function(e){
23886         var item = this.findItemFromChild(e.getTarget());
23887         if(item){
23888             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23889         }
23890     },
23891
23892     onItemClick : function(item, index, e){
23893         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23894             return false;
23895         }
23896         if(this.multiSelect || this.singleSelect){
23897             if(this.multiSelect && e.shiftKey && this.lastSelection){
23898                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23899             }else{
23900                 this.select(item, this.multiSelect && e.ctrlKey);
23901                 this.lastSelection = item;
23902             }
23903             e.preventDefault();
23904         }
23905         return true;
23906     },
23907
23908     /**
23909      * Get the number of selected nodes.
23910      * @return {Number}
23911      */
23912     getSelectionCount : function(){
23913         return this.selections.length;
23914     },
23915
23916     /**
23917      * Get the currently selected nodes.
23918      * @return {Array} An array of HTMLElements
23919      */
23920     getSelectedNodes : function(){
23921         return this.selections;
23922     },
23923
23924     /**
23925      * Get the indexes of the selected nodes.
23926      * @return {Array}
23927      */
23928     getSelectedIndexes : function(){
23929         var indexes = [], s = this.selections;
23930         for(var i = 0, len = s.length; i < len; i++){
23931             indexes.push(s[i].nodeIndex);
23932         }
23933         return indexes;
23934     },
23935
23936     /**
23937      * Clear all selections
23938      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23939      */
23940     clearSelections : function(suppressEvent){
23941         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23942             this.cmp.elements = this.selections;
23943             this.cmp.removeClass(this.selectedClass);
23944             this.selections = [];
23945             if(!suppressEvent){
23946                 this.fireEvent("selectionchange", this, this.selections);
23947             }
23948         }
23949     },
23950
23951     /**
23952      * Returns true if the passed node is selected
23953      * @param {HTMLElement/Number} node The node or node index
23954      * @return {Boolean}
23955      */
23956     isSelected : function(node){
23957         var s = this.selections;
23958         if(s.length < 1){
23959             return false;
23960         }
23961         node = this.getNode(node);
23962         return s.indexOf(node) !== -1;
23963     },
23964
23965     /**
23966      * Selects nodes.
23967      * @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
23968      * @param {Boolean} keepExisting (optional) true to keep existing selections
23969      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23970      */
23971     select : function(nodeInfo, keepExisting, suppressEvent){
23972         if(nodeInfo instanceof Array){
23973             if(!keepExisting){
23974                 this.clearSelections(true);
23975             }
23976             for(var i = 0, len = nodeInfo.length; i < len; i++){
23977                 this.select(nodeInfo[i], true, true);
23978             }
23979         } else{
23980             var node = this.getNode(nodeInfo);
23981             if(node && !this.isSelected(node)){
23982                 if(!keepExisting){
23983                     this.clearSelections(true);
23984                 }
23985                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23986                     Roo.fly(node).addClass(this.selectedClass);
23987                     this.selections.push(node);
23988                     if(!suppressEvent){
23989                         this.fireEvent("selectionchange", this, this.selections);
23990                     }
23991                 }
23992             }
23993         }
23994     },
23995
23996     /**
23997      * Gets a template node.
23998      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23999      * @return {HTMLElement} The node or null if it wasn't found
24000      */
24001     getNode : function(nodeInfo){
24002         if(typeof nodeInfo == "string"){
24003             return document.getElementById(nodeInfo);
24004         }else if(typeof nodeInfo == "number"){
24005             return this.nodes[nodeInfo];
24006         }
24007         return nodeInfo;
24008     },
24009
24010     /**
24011      * Gets a range template nodes.
24012      * @param {Number} startIndex
24013      * @param {Number} endIndex
24014      * @return {Array} An array of nodes
24015      */
24016     getNodes : function(start, end){
24017         var ns = this.nodes;
24018         start = start || 0;
24019         end = typeof end == "undefined" ? ns.length - 1 : end;
24020         var nodes = [];
24021         if(start <= end){
24022             for(var i = start; i <= end; i++){
24023                 nodes.push(ns[i]);
24024             }
24025         } else{
24026             for(var i = start; i >= end; i--){
24027                 nodes.push(ns[i]);
24028             }
24029         }
24030         return nodes;
24031     },
24032
24033     /**
24034      * Finds the index of the passed node
24035      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24036      * @return {Number} The index of the node or -1
24037      */
24038     indexOf : function(node){
24039         node = this.getNode(node);
24040         if(typeof node.nodeIndex == "number"){
24041             return node.nodeIndex;
24042         }
24043         var ns = this.nodes;
24044         for(var i = 0, len = ns.length; i < len; i++){
24045             if(ns[i] == node){
24046                 return i;
24047             }
24048         }
24049         return -1;
24050     }
24051 });
24052 /*
24053  * Based on:
24054  * Ext JS Library 1.1.1
24055  * Copyright(c) 2006-2007, Ext JS, LLC.
24056  *
24057  * Originally Released Under LGPL - original licence link has changed is not relivant.
24058  *
24059  * Fork - LGPL
24060  * <script type="text/javascript">
24061  */
24062
24063 /**
24064  * @class Roo.JsonView
24065  * @extends Roo.View
24066  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24067 <pre><code>
24068 var view = new Roo.JsonView({
24069     container: "my-element",
24070     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24071     multiSelect: true, 
24072     jsonRoot: "data" 
24073 });
24074
24075 // listen for node click?
24076 view.on("click", function(vw, index, node, e){
24077     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24078 });
24079
24080 // direct load of JSON data
24081 view.load("foobar.php");
24082
24083 // Example from my blog list
24084 var tpl = new Roo.Template(
24085     '&lt;div class="entry"&gt;' +
24086     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24087     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24088     "&lt;/div&gt;&lt;hr /&gt;"
24089 );
24090
24091 var moreView = new Roo.JsonView({
24092     container :  "entry-list", 
24093     template : tpl,
24094     jsonRoot: "posts"
24095 });
24096 moreView.on("beforerender", this.sortEntries, this);
24097 moreView.load({
24098     url: "/blog/get-posts.php",
24099     params: "allposts=true",
24100     text: "Loading Blog Entries..."
24101 });
24102 </code></pre>
24103
24104 * Note: old code is supported with arguments : (container, template, config)
24105
24106
24107  * @constructor
24108  * Create a new JsonView
24109  * 
24110  * @param {Object} config The config object
24111  * 
24112  */
24113 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24114     
24115     
24116     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24117
24118     var um = this.el.getUpdateManager();
24119     um.setRenderer(this);
24120     um.on("update", this.onLoad, this);
24121     um.on("failure", this.onLoadException, this);
24122
24123     /**
24124      * @event beforerender
24125      * Fires before rendering of the downloaded JSON data.
24126      * @param {Roo.JsonView} this
24127      * @param {Object} data The JSON data loaded
24128      */
24129     /**
24130      * @event load
24131      * Fires when data is loaded.
24132      * @param {Roo.JsonView} this
24133      * @param {Object} data The JSON data loaded
24134      * @param {Object} response The raw Connect response object
24135      */
24136     /**
24137      * @event loadexception
24138      * Fires when loading fails.
24139      * @param {Roo.JsonView} this
24140      * @param {Object} response The raw Connect response object
24141      */
24142     this.addEvents({
24143         'beforerender' : true,
24144         'load' : true,
24145         'loadexception' : true
24146     });
24147 };
24148 Roo.extend(Roo.JsonView, Roo.View, {
24149     /**
24150      * @type {String} The root property in the loaded JSON object that contains the data
24151      */
24152     jsonRoot : "",
24153
24154     /**
24155      * Refreshes the view.
24156      */
24157     refresh : function(){
24158         this.clearSelections();
24159         this.el.update("");
24160         var html = [];
24161         var o = this.jsonData;
24162         if(o && o.length > 0){
24163             for(var i = 0, len = o.length; i < len; i++){
24164                 var data = this.prepareData(o[i], i, o);
24165                 html[html.length] = this.tpl.apply(data);
24166             }
24167         }else{
24168             html.push(this.emptyText);
24169         }
24170         this.el.update(html.join(""));
24171         this.nodes = this.el.dom.childNodes;
24172         this.updateIndexes(0);
24173     },
24174
24175     /**
24176      * 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.
24177      * @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:
24178      <pre><code>
24179      view.load({
24180          url: "your-url.php",
24181          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24182          callback: yourFunction,
24183          scope: yourObject, //(optional scope)
24184          discardUrl: false,
24185          nocache: false,
24186          text: "Loading...",
24187          timeout: 30,
24188          scripts: false
24189      });
24190      </code></pre>
24191      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24192      * 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.
24193      * @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}
24194      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24195      * @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.
24196      */
24197     load : function(){
24198         var um = this.el.getUpdateManager();
24199         um.update.apply(um, arguments);
24200     },
24201
24202     render : function(el, response){
24203         this.clearSelections();
24204         this.el.update("");
24205         var o;
24206         try{
24207             o = Roo.util.JSON.decode(response.responseText);
24208             if(this.jsonRoot){
24209                 
24210                 o = o[this.jsonRoot];
24211             }
24212         } catch(e){
24213         }
24214         /**
24215          * The current JSON data or null
24216          */
24217         this.jsonData = o;
24218         this.beforeRender();
24219         this.refresh();
24220     },
24221
24222 /**
24223  * Get the number of records in the current JSON dataset
24224  * @return {Number}
24225  */
24226     getCount : function(){
24227         return this.jsonData ? this.jsonData.length : 0;
24228     },
24229
24230 /**
24231  * Returns the JSON object for the specified node(s)
24232  * @param {HTMLElement/Array} node The node or an array of nodes
24233  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24234  * you get the JSON object for the node
24235  */
24236     getNodeData : function(node){
24237         if(node instanceof Array){
24238             var data = [];
24239             for(var i = 0, len = node.length; i < len; i++){
24240                 data.push(this.getNodeData(node[i]));
24241             }
24242             return data;
24243         }
24244         return this.jsonData[this.indexOf(node)] || null;
24245     },
24246
24247     beforeRender : function(){
24248         this.snapshot = this.jsonData;
24249         if(this.sortInfo){
24250             this.sort.apply(this, this.sortInfo);
24251         }
24252         this.fireEvent("beforerender", this, this.jsonData);
24253     },
24254
24255     onLoad : function(el, o){
24256         this.fireEvent("load", this, this.jsonData, o);
24257     },
24258
24259     onLoadException : function(el, o){
24260         this.fireEvent("loadexception", this, o);
24261     },
24262
24263 /**
24264  * Filter the data by a specific property.
24265  * @param {String} property A property on your JSON objects
24266  * @param {String/RegExp} value Either string that the property values
24267  * should start with, or a RegExp to test against the property
24268  */
24269     filter : function(property, value){
24270         if(this.jsonData){
24271             var data = [];
24272             var ss = this.snapshot;
24273             if(typeof value == "string"){
24274                 var vlen = value.length;
24275                 if(vlen == 0){
24276                     this.clearFilter();
24277                     return;
24278                 }
24279                 value = value.toLowerCase();
24280                 for(var i = 0, len = ss.length; i < len; i++){
24281                     var o = ss[i];
24282                     if(o[property].substr(0, vlen).toLowerCase() == value){
24283                         data.push(o);
24284                     }
24285                 }
24286             } else if(value.exec){ // regex?
24287                 for(var i = 0, len = ss.length; i < len; i++){
24288                     var o = ss[i];
24289                     if(value.test(o[property])){
24290                         data.push(o);
24291                     }
24292                 }
24293             } else{
24294                 return;
24295             }
24296             this.jsonData = data;
24297             this.refresh();
24298         }
24299     },
24300
24301 /**
24302  * Filter by a function. The passed function will be called with each
24303  * object in the current dataset. If the function returns true the value is kept,
24304  * otherwise it is filtered.
24305  * @param {Function} fn
24306  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24307  */
24308     filterBy : function(fn, scope){
24309         if(this.jsonData){
24310             var data = [];
24311             var ss = this.snapshot;
24312             for(var i = 0, len = ss.length; i < len; i++){
24313                 var o = ss[i];
24314                 if(fn.call(scope || this, o)){
24315                     data.push(o);
24316                 }
24317             }
24318             this.jsonData = data;
24319             this.refresh();
24320         }
24321     },
24322
24323 /**
24324  * Clears the current filter.
24325  */
24326     clearFilter : function(){
24327         if(this.snapshot && this.jsonData != this.snapshot){
24328             this.jsonData = this.snapshot;
24329             this.refresh();
24330         }
24331     },
24332
24333
24334 /**
24335  * Sorts the data for this view and refreshes it.
24336  * @param {String} property A property on your JSON objects to sort on
24337  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24338  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24339  */
24340     sort : function(property, dir, sortType){
24341         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24342         if(this.jsonData){
24343             var p = property;
24344             var dsc = dir && dir.toLowerCase() == "desc";
24345             var f = function(o1, o2){
24346                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24347                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24348                 ;
24349                 if(v1 < v2){
24350                     return dsc ? +1 : -1;
24351                 } else if(v1 > v2){
24352                     return dsc ? -1 : +1;
24353                 } else{
24354                     return 0;
24355                 }
24356             };
24357             this.jsonData.sort(f);
24358             this.refresh();
24359             if(this.jsonData != this.snapshot){
24360                 this.snapshot.sort(f);
24361             }
24362         }
24363     }
24364 });/*
24365  * Based on:
24366  * Ext JS Library 1.1.1
24367  * Copyright(c) 2006-2007, Ext JS, LLC.
24368  *
24369  * Originally Released Under LGPL - original licence link has changed is not relivant.
24370  *
24371  * Fork - LGPL
24372  * <script type="text/javascript">
24373  */
24374  
24375
24376 /**
24377  * @class Roo.ColorPalette
24378  * @extends Roo.Component
24379  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24380  * Here's an example of typical usage:
24381  * <pre><code>
24382 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24383 cp.render('my-div');
24384
24385 cp.on('select', function(palette, selColor){
24386     // do something with selColor
24387 });
24388 </code></pre>
24389  * @constructor
24390  * Create a new ColorPalette
24391  * @param {Object} config The config object
24392  */
24393 Roo.ColorPalette = function(config){
24394     Roo.ColorPalette.superclass.constructor.call(this, config);
24395     this.addEvents({
24396         /**
24397              * @event select
24398              * Fires when a color is selected
24399              * @param {ColorPalette} this
24400              * @param {String} color The 6-digit color hex code (without the # symbol)
24401              */
24402         select: true
24403     });
24404
24405     if(this.handler){
24406         this.on("select", this.handler, this.scope, true);
24407     }
24408 };
24409 Roo.extend(Roo.ColorPalette, Roo.Component, {
24410     /**
24411      * @cfg {String} itemCls
24412      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24413      */
24414     itemCls : "x-color-palette",
24415     /**
24416      * @cfg {String} value
24417      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24418      * the hex codes are case-sensitive.
24419      */
24420     value : null,
24421     clickEvent:'click',
24422     // private
24423     ctype: "Roo.ColorPalette",
24424
24425     /**
24426      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24427      */
24428     allowReselect : false,
24429
24430     /**
24431      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24432      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24433      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24434      * of colors with the width setting until the box is symmetrical.</p>
24435      * <p>You can override individual colors if needed:</p>
24436      * <pre><code>
24437 var cp = new Roo.ColorPalette();
24438 cp.colors[0] = "FF0000";  // change the first box to red
24439 </code></pre>
24440
24441 Or you can provide a custom array of your own for complete control:
24442 <pre><code>
24443 var cp = new Roo.ColorPalette();
24444 cp.colors = ["000000", "993300", "333300"];
24445 </code></pre>
24446      * @type Array
24447      */
24448     colors : [
24449         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24450         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24451         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24452         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24453         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24454     ],
24455
24456     // private
24457     onRender : function(container, position){
24458         var t = new Roo.MasterTemplate(
24459             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24460         );
24461         var c = this.colors;
24462         for(var i = 0, len = c.length; i < len; i++){
24463             t.add([c[i]]);
24464         }
24465         var el = document.createElement("div");
24466         el.className = this.itemCls;
24467         t.overwrite(el);
24468         container.dom.insertBefore(el, position);
24469         this.el = Roo.get(el);
24470         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24471         if(this.clickEvent != 'click'){
24472             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24473         }
24474     },
24475
24476     // private
24477     afterRender : function(){
24478         Roo.ColorPalette.superclass.afterRender.call(this);
24479         if(this.value){
24480             var s = this.value;
24481             this.value = null;
24482             this.select(s);
24483         }
24484     },
24485
24486     // private
24487     handleClick : function(e, t){
24488         e.preventDefault();
24489         if(!this.disabled){
24490             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24491             this.select(c.toUpperCase());
24492         }
24493     },
24494
24495     /**
24496      * Selects the specified color in the palette (fires the select event)
24497      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24498      */
24499     select : function(color){
24500         color = color.replace("#", "");
24501         if(color != this.value || this.allowReselect){
24502             var el = this.el;
24503             if(this.value){
24504                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24505             }
24506             el.child("a.color-"+color).addClass("x-color-palette-sel");
24507             this.value = color;
24508             this.fireEvent("select", this, color);
24509         }
24510     }
24511 });/*
24512  * Based on:
24513  * Ext JS Library 1.1.1
24514  * Copyright(c) 2006-2007, Ext JS, LLC.
24515  *
24516  * Originally Released Under LGPL - original licence link has changed is not relivant.
24517  *
24518  * Fork - LGPL
24519  * <script type="text/javascript">
24520  */
24521  
24522 /**
24523  * @class Roo.DatePicker
24524  * @extends Roo.Component
24525  * Simple date picker class.
24526  * @constructor
24527  * Create a new DatePicker
24528  * @param {Object} config The config object
24529  */
24530 Roo.DatePicker = function(config){
24531     Roo.DatePicker.superclass.constructor.call(this, config);
24532
24533     this.value = config && config.value ?
24534                  config.value.clearTime() : new Date().clearTime();
24535
24536     this.addEvents({
24537         /**
24538              * @event select
24539              * Fires when a date is selected
24540              * @param {DatePicker} this
24541              * @param {Date} date The selected date
24542              */
24543         select: true
24544     });
24545
24546     if(this.handler){
24547         this.on("select", this.handler,  this.scope || this);
24548     }
24549     // build the disabledDatesRE
24550     if(!this.disabledDatesRE && this.disabledDates){
24551         var dd = this.disabledDates;
24552         var re = "(?:";
24553         for(var i = 0; i < dd.length; i++){
24554             re += dd[i];
24555             if(i != dd.length-1) re += "|";
24556         }
24557         this.disabledDatesRE = new RegExp(re + ")");
24558     }
24559 };
24560
24561 Roo.extend(Roo.DatePicker, Roo.Component, {
24562     /**
24563      * @cfg {String} todayText
24564      * The text to display on the button that selects the current date (defaults to "Today")
24565      */
24566     todayText : "Today",
24567     /**
24568      * @cfg {String} okText
24569      * The text to display on the ok button
24570      */
24571     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24572     /**
24573      * @cfg {String} cancelText
24574      * The text to display on the cancel button
24575      */
24576     cancelText : "Cancel",
24577     /**
24578      * @cfg {String} todayTip
24579      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24580      */
24581     todayTip : "{0} (Spacebar)",
24582     /**
24583      * @cfg {Date} minDate
24584      * Minimum allowable date (JavaScript date object, defaults to null)
24585      */
24586     minDate : null,
24587     /**
24588      * @cfg {Date} maxDate
24589      * Maximum allowable date (JavaScript date object, defaults to null)
24590      */
24591     maxDate : null,
24592     /**
24593      * @cfg {String} minText
24594      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24595      */
24596     minText : "This date is before the minimum date",
24597     /**
24598      * @cfg {String} maxText
24599      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24600      */
24601     maxText : "This date is after the maximum date",
24602     /**
24603      * @cfg {String} format
24604      * The default date format string which can be overriden for localization support.  The format must be
24605      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24606      */
24607     format : "m/d/y",
24608     /**
24609      * @cfg {Array} disabledDays
24610      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24611      */
24612     disabledDays : null,
24613     /**
24614      * @cfg {String} disabledDaysText
24615      * The tooltip to display when the date falls on a disabled day (defaults to "")
24616      */
24617     disabledDaysText : "",
24618     /**
24619      * @cfg {RegExp} disabledDatesRE
24620      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24621      */
24622     disabledDatesRE : null,
24623     /**
24624      * @cfg {String} disabledDatesText
24625      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24626      */
24627     disabledDatesText : "",
24628     /**
24629      * @cfg {Boolean} constrainToViewport
24630      * True to constrain the date picker to the viewport (defaults to true)
24631      */
24632     constrainToViewport : true,
24633     /**
24634      * @cfg {Array} monthNames
24635      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24636      */
24637     monthNames : Date.monthNames,
24638     /**
24639      * @cfg {Array} dayNames
24640      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24641      */
24642     dayNames : Date.dayNames,
24643     /**
24644      * @cfg {String} nextText
24645      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24646      */
24647     nextText: 'Next Month (Control+Right)',
24648     /**
24649      * @cfg {String} prevText
24650      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24651      */
24652     prevText: 'Previous Month (Control+Left)',
24653     /**
24654      * @cfg {String} monthYearText
24655      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24656      */
24657     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24658     /**
24659      * @cfg {Number} startDay
24660      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24661      */
24662     startDay : 0,
24663     /**
24664      * @cfg {Bool} showClear
24665      * Show a clear button (usefull for date form elements that can be blank.)
24666      */
24667     
24668     showClear: false,
24669     
24670     /**
24671      * Sets the value of the date field
24672      * @param {Date} value The date to set
24673      */
24674     setValue : function(value){
24675         var old = this.value;
24676         this.value = value.clearTime(true);
24677         if(this.el){
24678             this.update(this.value);
24679         }
24680     },
24681
24682     /**
24683      * Gets the current selected value of the date field
24684      * @return {Date} The selected date
24685      */
24686     getValue : function(){
24687         return this.value;
24688     },
24689
24690     // private
24691     focus : function(){
24692         if(this.el){
24693             this.update(this.activeDate);
24694         }
24695     },
24696
24697     // private
24698     onRender : function(container, position){
24699         var m = [
24700              '<table cellspacing="0">',
24701                 '<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>',
24702                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24703         var dn = this.dayNames;
24704         for(var i = 0; i < 7; i++){
24705             var d = this.startDay+i;
24706             if(d > 6){
24707                 d = d-7;
24708             }
24709             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24710         }
24711         m[m.length] = "</tr></thead><tbody><tr>";
24712         for(var i = 0; i < 42; i++) {
24713             if(i % 7 == 0 && i != 0){
24714                 m[m.length] = "</tr><tr>";
24715             }
24716             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24717         }
24718         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24719             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24720
24721         var el = document.createElement("div");
24722         el.className = "x-date-picker";
24723         el.innerHTML = m.join("");
24724
24725         container.dom.insertBefore(el, position);
24726
24727         this.el = Roo.get(el);
24728         this.eventEl = Roo.get(el.firstChild);
24729
24730         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24731             handler: this.showPrevMonth,
24732             scope: this,
24733             preventDefault:true,
24734             stopDefault:true
24735         });
24736
24737         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24738             handler: this.showNextMonth,
24739             scope: this,
24740             preventDefault:true,
24741             stopDefault:true
24742         });
24743
24744         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24745
24746         this.monthPicker = this.el.down('div.x-date-mp');
24747         this.monthPicker.enableDisplayMode('block');
24748         
24749         var kn = new Roo.KeyNav(this.eventEl, {
24750             "left" : function(e){
24751                 e.ctrlKey ?
24752                     this.showPrevMonth() :
24753                     this.update(this.activeDate.add("d", -1));
24754             },
24755
24756             "right" : function(e){
24757                 e.ctrlKey ?
24758                     this.showNextMonth() :
24759                     this.update(this.activeDate.add("d", 1));
24760             },
24761
24762             "up" : function(e){
24763                 e.ctrlKey ?
24764                     this.showNextYear() :
24765                     this.update(this.activeDate.add("d", -7));
24766             },
24767
24768             "down" : function(e){
24769                 e.ctrlKey ?
24770                     this.showPrevYear() :
24771                     this.update(this.activeDate.add("d", 7));
24772             },
24773
24774             "pageUp" : function(e){
24775                 this.showNextMonth();
24776             },
24777
24778             "pageDown" : function(e){
24779                 this.showPrevMonth();
24780             },
24781
24782             "enter" : function(e){
24783                 e.stopPropagation();
24784                 return true;
24785             },
24786
24787             scope : this
24788         });
24789
24790         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24791
24792         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24793
24794         this.el.unselectable();
24795         
24796         this.cells = this.el.select("table.x-date-inner tbody td");
24797         this.textNodes = this.el.query("table.x-date-inner tbody span");
24798
24799         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24800             text: "&#160;",
24801             tooltip: this.monthYearText
24802         });
24803
24804         this.mbtn.on('click', this.showMonthPicker, this);
24805         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24806
24807
24808         var today = (new Date()).dateFormat(this.format);
24809         
24810         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24811         if (this.showClear) {
24812             baseTb.add( new Roo.Toolbar.Fill());
24813         }
24814         baseTb.add({
24815             text: String.format(this.todayText, today),
24816             tooltip: String.format(this.todayTip, today),
24817             handler: this.selectToday,
24818             scope: this
24819         });
24820         
24821         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24822             
24823         //});
24824         if (this.showClear) {
24825             
24826             baseTb.add( new Roo.Toolbar.Fill());
24827             baseTb.add({
24828                 text: '&#160;',
24829                 cls: 'x-btn-icon x-btn-clear',
24830                 handler: function() {
24831                     //this.value = '';
24832                     this.fireEvent("select", this, '');
24833                 },
24834                 scope: this
24835             });
24836         }
24837         
24838         
24839         if(Roo.isIE){
24840             this.el.repaint();
24841         }
24842         this.update(this.value);
24843     },
24844
24845     createMonthPicker : function(){
24846         if(!this.monthPicker.dom.firstChild){
24847             var buf = ['<table border="0" cellspacing="0">'];
24848             for(var i = 0; i < 6; i++){
24849                 buf.push(
24850                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24851                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24852                     i == 0 ?
24853                     '<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>' :
24854                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24855                 );
24856             }
24857             buf.push(
24858                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24859                     this.okText,
24860                     '</button><button type="button" class="x-date-mp-cancel">',
24861                     this.cancelText,
24862                     '</button></td></tr>',
24863                 '</table>'
24864             );
24865             this.monthPicker.update(buf.join(''));
24866             this.monthPicker.on('click', this.onMonthClick, this);
24867             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24868
24869             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24870             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24871
24872             this.mpMonths.each(function(m, a, i){
24873                 i += 1;
24874                 if((i%2) == 0){
24875                     m.dom.xmonth = 5 + Math.round(i * .5);
24876                 }else{
24877                     m.dom.xmonth = Math.round((i-1) * .5);
24878                 }
24879             });
24880         }
24881     },
24882
24883     showMonthPicker : function(){
24884         this.createMonthPicker();
24885         var size = this.el.getSize();
24886         this.monthPicker.setSize(size);
24887         this.monthPicker.child('table').setSize(size);
24888
24889         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24890         this.updateMPMonth(this.mpSelMonth);
24891         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24892         this.updateMPYear(this.mpSelYear);
24893
24894         this.monthPicker.slideIn('t', {duration:.2});
24895     },
24896
24897     updateMPYear : function(y){
24898         this.mpyear = y;
24899         var ys = this.mpYears.elements;
24900         for(var i = 1; i <= 10; i++){
24901             var td = ys[i-1], y2;
24902             if((i%2) == 0){
24903                 y2 = y + Math.round(i * .5);
24904                 td.firstChild.innerHTML = y2;
24905                 td.xyear = y2;
24906             }else{
24907                 y2 = y - (5-Math.round(i * .5));
24908                 td.firstChild.innerHTML = y2;
24909                 td.xyear = y2;
24910             }
24911             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24912         }
24913     },
24914
24915     updateMPMonth : function(sm){
24916         this.mpMonths.each(function(m, a, i){
24917             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24918         });
24919     },
24920
24921     selectMPMonth: function(m){
24922         
24923     },
24924
24925     onMonthClick : function(e, t){
24926         e.stopEvent();
24927         var el = new Roo.Element(t), pn;
24928         if(el.is('button.x-date-mp-cancel')){
24929             this.hideMonthPicker();
24930         }
24931         else if(el.is('button.x-date-mp-ok')){
24932             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24933             this.hideMonthPicker();
24934         }
24935         else if(pn = el.up('td.x-date-mp-month', 2)){
24936             this.mpMonths.removeClass('x-date-mp-sel');
24937             pn.addClass('x-date-mp-sel');
24938             this.mpSelMonth = pn.dom.xmonth;
24939         }
24940         else if(pn = el.up('td.x-date-mp-year', 2)){
24941             this.mpYears.removeClass('x-date-mp-sel');
24942             pn.addClass('x-date-mp-sel');
24943             this.mpSelYear = pn.dom.xyear;
24944         }
24945         else if(el.is('a.x-date-mp-prev')){
24946             this.updateMPYear(this.mpyear-10);
24947         }
24948         else if(el.is('a.x-date-mp-next')){
24949             this.updateMPYear(this.mpyear+10);
24950         }
24951     },
24952
24953     onMonthDblClick : function(e, t){
24954         e.stopEvent();
24955         var el = new Roo.Element(t), pn;
24956         if(pn = el.up('td.x-date-mp-month', 2)){
24957             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24958             this.hideMonthPicker();
24959         }
24960         else if(pn = el.up('td.x-date-mp-year', 2)){
24961             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24962             this.hideMonthPicker();
24963         }
24964     },
24965
24966     hideMonthPicker : function(disableAnim){
24967         if(this.monthPicker){
24968             if(disableAnim === true){
24969                 this.monthPicker.hide();
24970             }else{
24971                 this.monthPicker.slideOut('t', {duration:.2});
24972             }
24973         }
24974     },
24975
24976     // private
24977     showPrevMonth : function(e){
24978         this.update(this.activeDate.add("mo", -1));
24979     },
24980
24981     // private
24982     showNextMonth : function(e){
24983         this.update(this.activeDate.add("mo", 1));
24984     },
24985
24986     // private
24987     showPrevYear : function(){
24988         this.update(this.activeDate.add("y", -1));
24989     },
24990
24991     // private
24992     showNextYear : function(){
24993         this.update(this.activeDate.add("y", 1));
24994     },
24995
24996     // private
24997     handleMouseWheel : function(e){
24998         var delta = e.getWheelDelta();
24999         if(delta > 0){
25000             this.showPrevMonth();
25001             e.stopEvent();
25002         } else if(delta < 0){
25003             this.showNextMonth();
25004             e.stopEvent();
25005         }
25006     },
25007
25008     // private
25009     handleDateClick : function(e, t){
25010         e.stopEvent();
25011         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25012             this.setValue(new Date(t.dateValue));
25013             this.fireEvent("select", this, this.value);
25014         }
25015     },
25016
25017     // private
25018     selectToday : function(){
25019         this.setValue(new Date().clearTime());
25020         this.fireEvent("select", this, this.value);
25021     },
25022
25023     // private
25024     update : function(date){
25025         var vd = this.activeDate;
25026         this.activeDate = date;
25027         if(vd && this.el){
25028             var t = date.getTime();
25029             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25030                 this.cells.removeClass("x-date-selected");
25031                 this.cells.each(function(c){
25032                    if(c.dom.firstChild.dateValue == t){
25033                        c.addClass("x-date-selected");
25034                        setTimeout(function(){
25035                             try{c.dom.firstChild.focus();}catch(e){}
25036                        }, 50);
25037                        return false;
25038                    }
25039                 });
25040                 return;
25041             }
25042         }
25043         var days = date.getDaysInMonth();
25044         var firstOfMonth = date.getFirstDateOfMonth();
25045         var startingPos = firstOfMonth.getDay()-this.startDay;
25046
25047         if(startingPos <= this.startDay){
25048             startingPos += 7;
25049         }
25050
25051         var pm = date.add("mo", -1);
25052         var prevStart = pm.getDaysInMonth()-startingPos;
25053
25054         var cells = this.cells.elements;
25055         var textEls = this.textNodes;
25056         days += startingPos;
25057
25058         // convert everything to numbers so it's fast
25059         var day = 86400000;
25060         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25061         var today = new Date().clearTime().getTime();
25062         var sel = date.clearTime().getTime();
25063         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25064         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25065         var ddMatch = this.disabledDatesRE;
25066         var ddText = this.disabledDatesText;
25067         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25068         var ddaysText = this.disabledDaysText;
25069         var format = this.format;
25070
25071         var setCellClass = function(cal, cell){
25072             cell.title = "";
25073             var t = d.getTime();
25074             cell.firstChild.dateValue = t;
25075             if(t == today){
25076                 cell.className += " x-date-today";
25077                 cell.title = cal.todayText;
25078             }
25079             if(t == sel){
25080                 cell.className += " x-date-selected";
25081                 setTimeout(function(){
25082                     try{cell.firstChild.focus();}catch(e){}
25083                 }, 50);
25084             }
25085             // disabling
25086             if(t < min) {
25087                 cell.className = " x-date-disabled";
25088                 cell.title = cal.minText;
25089                 return;
25090             }
25091             if(t > max) {
25092                 cell.className = " x-date-disabled";
25093                 cell.title = cal.maxText;
25094                 return;
25095             }
25096             if(ddays){
25097                 if(ddays.indexOf(d.getDay()) != -1){
25098                     cell.title = ddaysText;
25099                     cell.className = " x-date-disabled";
25100                 }
25101             }
25102             if(ddMatch && format){
25103                 var fvalue = d.dateFormat(format);
25104                 if(ddMatch.test(fvalue)){
25105                     cell.title = ddText.replace("%0", fvalue);
25106                     cell.className = " x-date-disabled";
25107                 }
25108             }
25109         };
25110
25111         var i = 0;
25112         for(; i < startingPos; i++) {
25113             textEls[i].innerHTML = (++prevStart);
25114             d.setDate(d.getDate()+1);
25115             cells[i].className = "x-date-prevday";
25116             setCellClass(this, cells[i]);
25117         }
25118         for(; i < days; i++){
25119             intDay = i - startingPos + 1;
25120             textEls[i].innerHTML = (intDay);
25121             d.setDate(d.getDate()+1);
25122             cells[i].className = "x-date-active";
25123             setCellClass(this, cells[i]);
25124         }
25125         var extraDays = 0;
25126         for(; i < 42; i++) {
25127              textEls[i].innerHTML = (++extraDays);
25128              d.setDate(d.getDate()+1);
25129              cells[i].className = "x-date-nextday";
25130              setCellClass(this, cells[i]);
25131         }
25132
25133         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25134
25135         if(!this.internalRender){
25136             var main = this.el.dom.firstChild;
25137             var w = main.offsetWidth;
25138             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25139             Roo.fly(main).setWidth(w);
25140             this.internalRender = true;
25141             // opera does not respect the auto grow header center column
25142             // then, after it gets a width opera refuses to recalculate
25143             // without a second pass
25144             if(Roo.isOpera && !this.secondPass){
25145                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25146                 this.secondPass = true;
25147                 this.update.defer(10, this, [date]);
25148             }
25149         }
25150     }
25151 });/*
25152  * Based on:
25153  * Ext JS Library 1.1.1
25154  * Copyright(c) 2006-2007, Ext JS, LLC.
25155  *
25156  * Originally Released Under LGPL - original licence link has changed is not relivant.
25157  *
25158  * Fork - LGPL
25159  * <script type="text/javascript">
25160  */
25161 /**
25162  * @class Roo.TabPanel
25163  * @extends Roo.util.Observable
25164  * A lightweight tab container.
25165  * <br><br>
25166  * Usage:
25167  * <pre><code>
25168 // basic tabs 1, built from existing content
25169 var tabs = new Roo.TabPanel("tabs1");
25170 tabs.addTab("script", "View Script");
25171 tabs.addTab("markup", "View Markup");
25172 tabs.activate("script");
25173
25174 // more advanced tabs, built from javascript
25175 var jtabs = new Roo.TabPanel("jtabs");
25176 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25177
25178 // set up the UpdateManager
25179 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25180 var updater = tab2.getUpdateManager();
25181 updater.setDefaultUrl("ajax1.htm");
25182 tab2.on('activate', updater.refresh, updater, true);
25183
25184 // Use setUrl for Ajax loading
25185 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25186 tab3.setUrl("ajax2.htm", null, true);
25187
25188 // Disabled tab
25189 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25190 tab4.disable();
25191
25192 jtabs.activate("jtabs-1");
25193  * </code></pre>
25194  * @constructor
25195  * Create a new TabPanel.
25196  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25197  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25198  */
25199 Roo.TabPanel = function(container, config){
25200     /**
25201     * The container element for this TabPanel.
25202     * @type Roo.Element
25203     */
25204     this.el = Roo.get(container, true);
25205     if(config){
25206         if(typeof config == "boolean"){
25207             this.tabPosition = config ? "bottom" : "top";
25208         }else{
25209             Roo.apply(this, config);
25210         }
25211     }
25212     if(this.tabPosition == "bottom"){
25213         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25214         this.el.addClass("x-tabs-bottom");
25215     }
25216     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25217     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25218     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25219     if(Roo.isIE){
25220         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25221     }
25222     if(this.tabPosition != "bottom"){
25223     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25224      * @type Roo.Element
25225      */
25226       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25227       this.el.addClass("x-tabs-top");
25228     }
25229     this.items = [];
25230
25231     this.bodyEl.setStyle("position", "relative");
25232
25233     this.active = null;
25234     this.activateDelegate = this.activate.createDelegate(this);
25235
25236     this.addEvents({
25237         /**
25238          * @event tabchange
25239          * Fires when the active tab changes
25240          * @param {Roo.TabPanel} this
25241          * @param {Roo.TabPanelItem} activePanel The new active tab
25242          */
25243         "tabchange": true,
25244         /**
25245          * @event beforetabchange
25246          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25247          * @param {Roo.TabPanel} this
25248          * @param {Object} e Set cancel to true on this object to cancel the tab change
25249          * @param {Roo.TabPanelItem} tab The tab being changed to
25250          */
25251         "beforetabchange" : true
25252     });
25253
25254     Roo.EventManager.onWindowResize(this.onResize, this);
25255     this.cpad = this.el.getPadding("lr");
25256     this.hiddenCount = 0;
25257
25258     Roo.TabPanel.superclass.constructor.call(this);
25259 };
25260
25261 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25262         /*
25263          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25264          */
25265     tabPosition : "top",
25266         /*
25267          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25268          */
25269     currentTabWidth : 0,
25270         /*
25271          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25272          */
25273     minTabWidth : 40,
25274         /*
25275          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25276          */
25277     maxTabWidth : 250,
25278         /*
25279          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25280          */
25281     preferredTabWidth : 175,
25282         /*
25283          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25284          */
25285     resizeTabs : false,
25286         /*
25287          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25288          */
25289     monitorResize : true,
25290
25291     /**
25292      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25293      * @param {String} id The id of the div to use <b>or create</b>
25294      * @param {String} text The text for the tab
25295      * @param {String} content (optional) Content to put in the TabPanelItem body
25296      * @param {Boolean} closable (optional) True to create a close icon on the tab
25297      * @return {Roo.TabPanelItem} The created TabPanelItem
25298      */
25299     addTab : function(id, text, content, closable){
25300         var item = new Roo.TabPanelItem(this, id, text, closable);
25301         this.addTabItem(item);
25302         if(content){
25303             item.setContent(content);
25304         }
25305         return item;
25306     },
25307
25308     /**
25309      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25310      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25311      * @return {Roo.TabPanelItem}
25312      */
25313     getTab : function(id){
25314         return this.items[id];
25315     },
25316
25317     /**
25318      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25319      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25320      */
25321     hideTab : function(id){
25322         var t = this.items[id];
25323         if(!t.isHidden()){
25324            t.setHidden(true);
25325            this.hiddenCount++;
25326            this.autoSizeTabs();
25327         }
25328     },
25329
25330     /**
25331      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25332      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25333      */
25334     unhideTab : function(id){
25335         var t = this.items[id];
25336         if(t.isHidden()){
25337            t.setHidden(false);
25338            this.hiddenCount--;
25339            this.autoSizeTabs();
25340         }
25341     },
25342
25343     /**
25344      * Adds an existing {@link Roo.TabPanelItem}.
25345      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25346      */
25347     addTabItem : function(item){
25348         this.items[item.id] = item;
25349         this.items.push(item);
25350         if(this.resizeTabs){
25351            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25352            this.autoSizeTabs();
25353         }else{
25354             item.autoSize();
25355         }
25356     },
25357
25358     /**
25359      * Removes a {@link Roo.TabPanelItem}.
25360      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25361      */
25362     removeTab : function(id){
25363         var items = this.items;
25364         var tab = items[id];
25365         if(!tab) { return; }
25366         var index = items.indexOf(tab);
25367         if(this.active == tab && items.length > 1){
25368             var newTab = this.getNextAvailable(index);
25369             if(newTab) {
25370                 newTab.activate();
25371             }
25372         }
25373         this.stripEl.dom.removeChild(tab.pnode.dom);
25374         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25375             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25376         }
25377         items.splice(index, 1);
25378         delete this.items[tab.id];
25379         tab.fireEvent("close", tab);
25380         tab.purgeListeners();
25381         this.autoSizeTabs();
25382     },
25383
25384     getNextAvailable : function(start){
25385         var items = this.items;
25386         var index = start;
25387         // look for a next tab that will slide over to
25388         // replace the one being removed
25389         while(index < items.length){
25390             var item = items[++index];
25391             if(item && !item.isHidden()){
25392                 return item;
25393             }
25394         }
25395         // if one isn't found select the previous tab (on the left)
25396         index = start;
25397         while(index >= 0){
25398             var item = items[--index];
25399             if(item && !item.isHidden()){
25400                 return item;
25401             }
25402         }
25403         return null;
25404     },
25405
25406     /**
25407      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25408      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25409      */
25410     disableTab : function(id){
25411         var tab = this.items[id];
25412         if(tab && this.active != tab){
25413             tab.disable();
25414         }
25415     },
25416
25417     /**
25418      * Enables a {@link Roo.TabPanelItem} that is disabled.
25419      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25420      */
25421     enableTab : function(id){
25422         var tab = this.items[id];
25423         tab.enable();
25424     },
25425
25426     /**
25427      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25428      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25429      * @return {Roo.TabPanelItem} The TabPanelItem.
25430      */
25431     activate : function(id){
25432         var tab = this.items[id];
25433         if(!tab){
25434             return null;
25435         }
25436         if(tab == this.active || tab.disabled){
25437             return tab;
25438         }
25439         var e = {};
25440         this.fireEvent("beforetabchange", this, e, tab);
25441         if(e.cancel !== true && !tab.disabled){
25442             if(this.active){
25443                 this.active.hide();
25444             }
25445             this.active = this.items[id];
25446             this.active.show();
25447             this.fireEvent("tabchange", this, this.active);
25448         }
25449         return tab;
25450     },
25451
25452     /**
25453      * Gets the active {@link Roo.TabPanelItem}.
25454      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25455      */
25456     getActiveTab : function(){
25457         return this.active;
25458     },
25459
25460     /**
25461      * Updates the tab body element to fit the height of the container element
25462      * for overflow scrolling
25463      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25464      */
25465     syncHeight : function(targetHeight){
25466         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25467         var bm = this.bodyEl.getMargins();
25468         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25469         this.bodyEl.setHeight(newHeight);
25470         return newHeight;
25471     },
25472
25473     onResize : function(){
25474         if(this.monitorResize){
25475             this.autoSizeTabs();
25476         }
25477     },
25478
25479     /**
25480      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25481      */
25482     beginUpdate : function(){
25483         this.updating = true;
25484     },
25485
25486     /**
25487      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25488      */
25489     endUpdate : function(){
25490         this.updating = false;
25491         this.autoSizeTabs();
25492     },
25493
25494     /**
25495      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25496      */
25497     autoSizeTabs : function(){
25498         var count = this.items.length;
25499         var vcount = count - this.hiddenCount;
25500         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25501         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25502         var availWidth = Math.floor(w / vcount);
25503         var b = this.stripBody;
25504         if(b.getWidth() > w){
25505             var tabs = this.items;
25506             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25507             if(availWidth < this.minTabWidth){
25508                 /*if(!this.sleft){    // incomplete scrolling code
25509                     this.createScrollButtons();
25510                 }
25511                 this.showScroll();
25512                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25513             }
25514         }else{
25515             if(this.currentTabWidth < this.preferredTabWidth){
25516                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25517             }
25518         }
25519     },
25520
25521     /**
25522      * Returns the number of tabs in this TabPanel.
25523      * @return {Number}
25524      */
25525      getCount : function(){
25526          return this.items.length;
25527      },
25528
25529     /**
25530      * Resizes all the tabs to the passed width
25531      * @param {Number} The new width
25532      */
25533     setTabWidth : function(width){
25534         this.currentTabWidth = width;
25535         for(var i = 0, len = this.items.length; i < len; i++) {
25536                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25537         }
25538     },
25539
25540     /**
25541      * Destroys this TabPanel
25542      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25543      */
25544     destroy : function(removeEl){
25545         Roo.EventManager.removeResizeListener(this.onResize, this);
25546         for(var i = 0, len = this.items.length; i < len; i++){
25547             this.items[i].purgeListeners();
25548         }
25549         if(removeEl === true){
25550             this.el.update("");
25551             this.el.remove();
25552         }
25553     }
25554 });
25555
25556 /**
25557  * @class Roo.TabPanelItem
25558  * @extends Roo.util.Observable
25559  * Represents an individual item (tab plus body) in a TabPanel.
25560  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25561  * @param {String} id The id of this TabPanelItem
25562  * @param {String} text The text for the tab of this TabPanelItem
25563  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25564  */
25565 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25566     /**
25567      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25568      * @type Roo.TabPanel
25569      */
25570     this.tabPanel = tabPanel;
25571     /**
25572      * The id for this TabPanelItem
25573      * @type String
25574      */
25575     this.id = id;
25576     /** @private */
25577     this.disabled = false;
25578     /** @private */
25579     this.text = text;
25580     /** @private */
25581     this.loaded = false;
25582     this.closable = closable;
25583
25584     /**
25585      * The body element for this TabPanelItem.
25586      * @type Roo.Element
25587      */
25588     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25589     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25590     this.bodyEl.setStyle("display", "block");
25591     this.bodyEl.setStyle("zoom", "1");
25592     this.hideAction();
25593
25594     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25595     /** @private */
25596     this.el = Roo.get(els.el, true);
25597     this.inner = Roo.get(els.inner, true);
25598     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25599     this.pnode = Roo.get(els.el.parentNode, true);
25600     this.el.on("mousedown", this.onTabMouseDown, this);
25601     this.el.on("click", this.onTabClick, this);
25602     /** @private */
25603     if(closable){
25604         var c = Roo.get(els.close, true);
25605         c.dom.title = this.closeText;
25606         c.addClassOnOver("close-over");
25607         c.on("click", this.closeClick, this);
25608      }
25609
25610     this.addEvents({
25611          /**
25612          * @event activate
25613          * Fires when this tab becomes the active tab.
25614          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25615          * @param {Roo.TabPanelItem} this
25616          */
25617         "activate": true,
25618         /**
25619          * @event beforeclose
25620          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25621          * @param {Roo.TabPanelItem} this
25622          * @param {Object} e Set cancel to true on this object to cancel the close.
25623          */
25624         "beforeclose": true,
25625         /**
25626          * @event close
25627          * Fires when this tab is closed.
25628          * @param {Roo.TabPanelItem} this
25629          */
25630          "close": true,
25631         /**
25632          * @event deactivate
25633          * Fires when this tab is no longer the active tab.
25634          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25635          * @param {Roo.TabPanelItem} this
25636          */
25637          "deactivate" : true
25638     });
25639     this.hidden = false;
25640
25641     Roo.TabPanelItem.superclass.constructor.call(this);
25642 };
25643
25644 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25645     purgeListeners : function(){
25646        Roo.util.Observable.prototype.purgeListeners.call(this);
25647        this.el.removeAllListeners();
25648     },
25649     /**
25650      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25651      */
25652     show : function(){
25653         this.pnode.addClass("on");
25654         this.showAction();
25655         if(Roo.isOpera){
25656             this.tabPanel.stripWrap.repaint();
25657         }
25658         this.fireEvent("activate", this.tabPanel, this);
25659     },
25660
25661     /**
25662      * Returns true if this tab is the active tab.
25663      * @return {Boolean}
25664      */
25665     isActive : function(){
25666         return this.tabPanel.getActiveTab() == this;
25667     },
25668
25669     /**
25670      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25671      */
25672     hide : function(){
25673         this.pnode.removeClass("on");
25674         this.hideAction();
25675         this.fireEvent("deactivate", this.tabPanel, this);
25676     },
25677
25678     hideAction : function(){
25679         this.bodyEl.hide();
25680         this.bodyEl.setStyle("position", "absolute");
25681         this.bodyEl.setLeft("-20000px");
25682         this.bodyEl.setTop("-20000px");
25683     },
25684
25685     showAction : function(){
25686         this.bodyEl.setStyle("position", "relative");
25687         this.bodyEl.setTop("");
25688         this.bodyEl.setLeft("");
25689         this.bodyEl.show();
25690     },
25691
25692     /**
25693      * Set the tooltip for the tab.
25694      * @param {String} tooltip The tab's tooltip
25695      */
25696     setTooltip : function(text){
25697         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25698             this.textEl.dom.qtip = text;
25699             this.textEl.dom.removeAttribute('title');
25700         }else{
25701             this.textEl.dom.title = text;
25702         }
25703     },
25704
25705     onTabClick : function(e){
25706         e.preventDefault();
25707         this.tabPanel.activate(this.id);
25708     },
25709
25710     onTabMouseDown : function(e){
25711         e.preventDefault();
25712         this.tabPanel.activate(this.id);
25713     },
25714
25715     getWidth : function(){
25716         return this.inner.getWidth();
25717     },
25718
25719     setWidth : function(width){
25720         var iwidth = width - this.pnode.getPadding("lr");
25721         this.inner.setWidth(iwidth);
25722         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25723         this.pnode.setWidth(width);
25724     },
25725
25726     /**
25727      * Show or hide the tab
25728      * @param {Boolean} hidden True to hide or false to show.
25729      */
25730     setHidden : function(hidden){
25731         this.hidden = hidden;
25732         this.pnode.setStyle("display", hidden ? "none" : "");
25733     },
25734
25735     /**
25736      * Returns true if this tab is "hidden"
25737      * @return {Boolean}
25738      */
25739     isHidden : function(){
25740         return this.hidden;
25741     },
25742
25743     /**
25744      * Returns the text for this tab
25745      * @return {String}
25746      */
25747     getText : function(){
25748         return this.text;
25749     },
25750
25751     autoSize : function(){
25752         //this.el.beginMeasure();
25753         this.textEl.setWidth(1);
25754         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25755         //this.el.endMeasure();
25756     },
25757
25758     /**
25759      * Sets the text for the tab (Note: this also sets the tooltip text)
25760      * @param {String} text The tab's text and tooltip
25761      */
25762     setText : function(text){
25763         this.text = text;
25764         this.textEl.update(text);
25765         this.setTooltip(text);
25766         if(!this.tabPanel.resizeTabs){
25767             this.autoSize();
25768         }
25769     },
25770     /**
25771      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25772      */
25773     activate : function(){
25774         this.tabPanel.activate(this.id);
25775     },
25776
25777     /**
25778      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25779      */
25780     disable : function(){
25781         if(this.tabPanel.active != this){
25782             this.disabled = true;
25783             this.pnode.addClass("disabled");
25784         }
25785     },
25786
25787     /**
25788      * Enables this TabPanelItem if it was previously disabled.
25789      */
25790     enable : function(){
25791         this.disabled = false;
25792         this.pnode.removeClass("disabled");
25793     },
25794
25795     /**
25796      * Sets the content for this TabPanelItem.
25797      * @param {String} content The content
25798      * @param {Boolean} loadScripts true to look for and load scripts
25799      */
25800     setContent : function(content, loadScripts){
25801         this.bodyEl.update(content, loadScripts);
25802     },
25803
25804     /**
25805      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25806      * @return {Roo.UpdateManager} The UpdateManager
25807      */
25808     getUpdateManager : function(){
25809         return this.bodyEl.getUpdateManager();
25810     },
25811
25812     /**
25813      * Set a URL to be used to load the content for this TabPanelItem.
25814      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25815      * @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)
25816      * @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)
25817      * @return {Roo.UpdateManager} The UpdateManager
25818      */
25819     setUrl : function(url, params, loadOnce){
25820         if(this.refreshDelegate){
25821             this.un('activate', this.refreshDelegate);
25822         }
25823         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25824         this.on("activate", this.refreshDelegate);
25825         return this.bodyEl.getUpdateManager();
25826     },
25827
25828     /** @private */
25829     _handleRefresh : function(url, params, loadOnce){
25830         if(!loadOnce || !this.loaded){
25831             var updater = this.bodyEl.getUpdateManager();
25832             updater.update(url, params, this._setLoaded.createDelegate(this));
25833         }
25834     },
25835
25836     /**
25837      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25838      *   Will fail silently if the setUrl method has not been called.
25839      *   This does not activate the panel, just updates its content.
25840      */
25841     refresh : function(){
25842         if(this.refreshDelegate){
25843            this.loaded = false;
25844            this.refreshDelegate();
25845         }
25846     },
25847
25848     /** @private */
25849     _setLoaded : function(){
25850         this.loaded = true;
25851     },
25852
25853     /** @private */
25854     closeClick : function(e){
25855         var o = {};
25856         e.stopEvent();
25857         this.fireEvent("beforeclose", this, o);
25858         if(o.cancel !== true){
25859             this.tabPanel.removeTab(this.id);
25860         }
25861     },
25862     /**
25863      * The text displayed in the tooltip for the close icon.
25864      * @type String
25865      */
25866     closeText : "Close this tab"
25867 });
25868
25869 /** @private */
25870 Roo.TabPanel.prototype.createStrip = function(container){
25871     var strip = document.createElement("div");
25872     strip.className = "x-tabs-wrap";
25873     container.appendChild(strip);
25874     return strip;
25875 };
25876 /** @private */
25877 Roo.TabPanel.prototype.createStripList = function(strip){
25878     // div wrapper for retard IE
25879     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25880     return strip.firstChild.firstChild.firstChild.firstChild;
25881 };
25882 /** @private */
25883 Roo.TabPanel.prototype.createBody = function(container){
25884     var body = document.createElement("div");
25885     Roo.id(body, "tab-body");
25886     Roo.fly(body).addClass("x-tabs-body");
25887     container.appendChild(body);
25888     return body;
25889 };
25890 /** @private */
25891 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25892     var body = Roo.getDom(id);
25893     if(!body){
25894         body = document.createElement("div");
25895         body.id = id;
25896     }
25897     Roo.fly(body).addClass("x-tabs-item-body");
25898     bodyEl.insertBefore(body, bodyEl.firstChild);
25899     return body;
25900 };
25901 /** @private */
25902 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25903     var td = document.createElement("td");
25904     stripEl.appendChild(td);
25905     if(closable){
25906         td.className = "x-tabs-closable";
25907         if(!this.closeTpl){
25908             this.closeTpl = new Roo.Template(
25909                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25910                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25911                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25912             );
25913         }
25914         var el = this.closeTpl.overwrite(td, {"text": text});
25915         var close = el.getElementsByTagName("div")[0];
25916         var inner = el.getElementsByTagName("em")[0];
25917         return {"el": el, "close": close, "inner": inner};
25918     } else {
25919         if(!this.tabTpl){
25920             this.tabTpl = new Roo.Template(
25921                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25922                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25923             );
25924         }
25925         var el = this.tabTpl.overwrite(td, {"text": text});
25926         var inner = el.getElementsByTagName("em")[0];
25927         return {"el": el, "inner": inner};
25928     }
25929 };/*
25930  * Based on:
25931  * Ext JS Library 1.1.1
25932  * Copyright(c) 2006-2007, Ext JS, LLC.
25933  *
25934  * Originally Released Under LGPL - original licence link has changed is not relivant.
25935  *
25936  * Fork - LGPL
25937  * <script type="text/javascript">
25938  */
25939
25940 /**
25941  * @class Roo.Button
25942  * @extends Roo.util.Observable
25943  * Simple Button class
25944  * @cfg {String} text The button text
25945  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25946  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25947  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25948  * @cfg {Object} scope The scope of the handler
25949  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25950  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25951  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25952  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25953  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25954  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25955    applies if enableToggle = true)
25956  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25957  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25958   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25959  * @constructor
25960  * Create a new button
25961  * @param {Object} config The config object
25962  */
25963 Roo.Button = function(renderTo, config)
25964 {
25965     if (!config) {
25966         config = renderTo;
25967         renderTo = config.renderTo || false;
25968     }
25969     
25970     Roo.apply(this, config);
25971     this.addEvents({
25972         /**
25973              * @event click
25974              * Fires when this button is clicked
25975              * @param {Button} this
25976              * @param {EventObject} e The click event
25977              */
25978             "click" : true,
25979         /**
25980              * @event toggle
25981              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25982              * @param {Button} this
25983              * @param {Boolean} pressed
25984              */
25985             "toggle" : true,
25986         /**
25987              * @event mouseover
25988              * Fires when the mouse hovers over the button
25989              * @param {Button} this
25990              * @param {Event} e The event object
25991              */
25992         'mouseover' : true,
25993         /**
25994              * @event mouseout
25995              * Fires when the mouse exits the button
25996              * @param {Button} this
25997              * @param {Event} e The event object
25998              */
25999         'mouseout': true,
26000          /**
26001              * @event render
26002              * Fires when the button is rendered
26003              * @param {Button} this
26004              */
26005         'render': true
26006     });
26007     if(this.menu){
26008         this.menu = Roo.menu.MenuMgr.get(this.menu);
26009     }
26010     // register listeners first!!  - so render can be captured..
26011     Roo.util.Observable.call(this);
26012     if(renderTo){
26013         this.render(renderTo);
26014     }
26015     
26016   
26017 };
26018
26019 Roo.extend(Roo.Button, Roo.util.Observable, {
26020     /**
26021      * 
26022      */
26023     
26024     /**
26025      * Read-only. True if this button is hidden
26026      * @type Boolean
26027      */
26028     hidden : false,
26029     /**
26030      * Read-only. True if this button is disabled
26031      * @type Boolean
26032      */
26033     disabled : false,
26034     /**
26035      * Read-only. True if this button is pressed (only if enableToggle = true)
26036      * @type Boolean
26037      */
26038     pressed : false,
26039
26040     /**
26041      * @cfg {Number} tabIndex 
26042      * The DOM tabIndex for this button (defaults to undefined)
26043      */
26044     tabIndex : undefined,
26045
26046     /**
26047      * @cfg {Boolean} enableToggle
26048      * True to enable pressed/not pressed toggling (defaults to false)
26049      */
26050     enableToggle: false,
26051     /**
26052      * @cfg {Mixed} menu
26053      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26054      */
26055     menu : undefined,
26056     /**
26057      * @cfg {String} menuAlign
26058      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26059      */
26060     menuAlign : "tl-bl?",
26061
26062     /**
26063      * @cfg {String} iconCls
26064      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26065      */
26066     iconCls : undefined,
26067     /**
26068      * @cfg {String} type
26069      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26070      */
26071     type : 'button',
26072
26073     // private
26074     menuClassTarget: 'tr',
26075
26076     /**
26077      * @cfg {String} clickEvent
26078      * The type of event to map to the button's event handler (defaults to 'click')
26079      */
26080     clickEvent : 'click',
26081
26082     /**
26083      * @cfg {Boolean} handleMouseEvents
26084      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26085      */
26086     handleMouseEvents : true,
26087
26088     /**
26089      * @cfg {String} tooltipType
26090      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26091      */
26092     tooltipType : 'qtip',
26093
26094     /**
26095      * @cfg {String} cls
26096      * A CSS class to apply to the button's main element.
26097      */
26098     
26099     /**
26100      * @cfg {Roo.Template} template (Optional)
26101      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26102      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26103      * require code modifications if required elements (e.g. a button) aren't present.
26104      */
26105
26106     // private
26107     render : function(renderTo){
26108         var btn;
26109         if(this.hideParent){
26110             this.parentEl = Roo.get(renderTo);
26111         }
26112         if(!this.dhconfig){
26113             if(!this.template){
26114                 if(!Roo.Button.buttonTemplate){
26115                     // hideous table template
26116                     Roo.Button.buttonTemplate = new Roo.Template(
26117                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26118                         '<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>',
26119                         "</tr></tbody></table>");
26120                 }
26121                 this.template = Roo.Button.buttonTemplate;
26122             }
26123             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26124             var btnEl = btn.child("button:first");
26125             btnEl.on('focus', this.onFocus, this);
26126             btnEl.on('blur', this.onBlur, this);
26127             if(this.cls){
26128                 btn.addClass(this.cls);
26129             }
26130             if(this.icon){
26131                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26132             }
26133             if(this.iconCls){
26134                 btnEl.addClass(this.iconCls);
26135                 if(!this.cls){
26136                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26137                 }
26138             }
26139             if(this.tabIndex !== undefined){
26140                 btnEl.dom.tabIndex = this.tabIndex;
26141             }
26142             if(this.tooltip){
26143                 if(typeof this.tooltip == 'object'){
26144                     Roo.QuickTips.tips(Roo.apply({
26145                           target: btnEl.id
26146                     }, this.tooltip));
26147                 } else {
26148                     btnEl.dom[this.tooltipType] = this.tooltip;
26149                 }
26150             }
26151         }else{
26152             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26153         }
26154         this.el = btn;
26155         if(this.id){
26156             this.el.dom.id = this.el.id = this.id;
26157         }
26158         if(this.menu){
26159             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26160             this.menu.on("show", this.onMenuShow, this);
26161             this.menu.on("hide", this.onMenuHide, this);
26162         }
26163         btn.addClass("x-btn");
26164         if(Roo.isIE && !Roo.isIE7){
26165             this.autoWidth.defer(1, this);
26166         }else{
26167             this.autoWidth();
26168         }
26169         if(this.handleMouseEvents){
26170             btn.on("mouseover", this.onMouseOver, this);
26171             btn.on("mouseout", this.onMouseOut, this);
26172             btn.on("mousedown", this.onMouseDown, this);
26173         }
26174         btn.on(this.clickEvent, this.onClick, this);
26175         //btn.on("mouseup", this.onMouseUp, this);
26176         if(this.hidden){
26177             this.hide();
26178         }
26179         if(this.disabled){
26180             this.disable();
26181         }
26182         Roo.ButtonToggleMgr.register(this);
26183         if(this.pressed){
26184             this.el.addClass("x-btn-pressed");
26185         }
26186         if(this.repeat){
26187             var repeater = new Roo.util.ClickRepeater(btn,
26188                 typeof this.repeat == "object" ? this.repeat : {}
26189             );
26190             repeater.on("click", this.onClick,  this);
26191         }
26192         
26193         this.fireEvent('render', this);
26194         
26195     },
26196     /**
26197      * Returns the button's underlying element
26198      * @return {Roo.Element} The element
26199      */
26200     getEl : function(){
26201         return this.el;  
26202     },
26203     
26204     /**
26205      * Destroys this Button and removes any listeners.
26206      */
26207     destroy : function(){
26208         Roo.ButtonToggleMgr.unregister(this);
26209         this.el.removeAllListeners();
26210         this.purgeListeners();
26211         this.el.remove();
26212     },
26213
26214     // private
26215     autoWidth : function(){
26216         if(this.el){
26217             this.el.setWidth("auto");
26218             if(Roo.isIE7 && Roo.isStrict){
26219                 var ib = this.el.child('button');
26220                 if(ib && ib.getWidth() > 20){
26221                     ib.clip();
26222                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26223                 }
26224             }
26225             if(this.minWidth){
26226                 if(this.hidden){
26227                     this.el.beginMeasure();
26228                 }
26229                 if(this.el.getWidth() < this.minWidth){
26230                     this.el.setWidth(this.minWidth);
26231                 }
26232                 if(this.hidden){
26233                     this.el.endMeasure();
26234                 }
26235             }
26236         }
26237     },
26238
26239     /**
26240      * Assigns this button's click handler
26241      * @param {Function} handler The function to call when the button is clicked
26242      * @param {Object} scope (optional) Scope for the function passed in
26243      */
26244     setHandler : function(handler, scope){
26245         this.handler = handler;
26246         this.scope = scope;  
26247     },
26248     
26249     /**
26250      * Sets this button's text
26251      * @param {String} text The button text
26252      */
26253     setText : function(text){
26254         this.text = text;
26255         if(this.el){
26256             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26257         }
26258         this.autoWidth();
26259     },
26260     
26261     /**
26262      * Gets the text for this button
26263      * @return {String} The button text
26264      */
26265     getText : function(){
26266         return this.text;  
26267     },
26268     
26269     /**
26270      * Show this button
26271      */
26272     show: function(){
26273         this.hidden = false;
26274         if(this.el){
26275             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26276         }
26277     },
26278     
26279     /**
26280      * Hide this button
26281      */
26282     hide: function(){
26283         this.hidden = true;
26284         if(this.el){
26285             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26286         }
26287     },
26288     
26289     /**
26290      * Convenience function for boolean show/hide
26291      * @param {Boolean} visible True to show, false to hide
26292      */
26293     setVisible: function(visible){
26294         if(visible) {
26295             this.show();
26296         }else{
26297             this.hide();
26298         }
26299     },
26300     
26301     /**
26302      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26303      * @param {Boolean} state (optional) Force a particular state
26304      */
26305     toggle : function(state){
26306         state = state === undefined ? !this.pressed : state;
26307         if(state != this.pressed){
26308             if(state){
26309                 this.el.addClass("x-btn-pressed");
26310                 this.pressed = true;
26311                 this.fireEvent("toggle", this, true);
26312             }else{
26313                 this.el.removeClass("x-btn-pressed");
26314                 this.pressed = false;
26315                 this.fireEvent("toggle", this, false);
26316             }
26317             if(this.toggleHandler){
26318                 this.toggleHandler.call(this.scope || this, this, state);
26319             }
26320         }
26321     },
26322     
26323     /**
26324      * Focus the button
26325      */
26326     focus : function(){
26327         this.el.child('button:first').focus();
26328     },
26329     
26330     /**
26331      * Disable this button
26332      */
26333     disable : function(){
26334         if(this.el){
26335             this.el.addClass("x-btn-disabled");
26336         }
26337         this.disabled = true;
26338     },
26339     
26340     /**
26341      * Enable this button
26342      */
26343     enable : function(){
26344         if(this.el){
26345             this.el.removeClass("x-btn-disabled");
26346         }
26347         this.disabled = false;
26348     },
26349
26350     /**
26351      * Convenience function for boolean enable/disable
26352      * @param {Boolean} enabled True to enable, false to disable
26353      */
26354     setDisabled : function(v){
26355         this[v !== true ? "enable" : "disable"]();
26356     },
26357
26358     // private
26359     onClick : function(e){
26360         if(e){
26361             e.preventDefault();
26362         }
26363         if(e.button != 0){
26364             return;
26365         }
26366         if(!this.disabled){
26367             if(this.enableToggle){
26368                 this.toggle();
26369             }
26370             if(this.menu && !this.menu.isVisible()){
26371                 this.menu.show(this.el, this.menuAlign);
26372             }
26373             this.fireEvent("click", this, e);
26374             if(this.handler){
26375                 this.el.removeClass("x-btn-over");
26376                 this.handler.call(this.scope || this, this, e);
26377             }
26378         }
26379     },
26380     // private
26381     onMouseOver : function(e){
26382         if(!this.disabled){
26383             this.el.addClass("x-btn-over");
26384             this.fireEvent('mouseover', this, e);
26385         }
26386     },
26387     // private
26388     onMouseOut : function(e){
26389         if(!e.within(this.el,  true)){
26390             this.el.removeClass("x-btn-over");
26391             this.fireEvent('mouseout', this, e);
26392         }
26393     },
26394     // private
26395     onFocus : function(e){
26396         if(!this.disabled){
26397             this.el.addClass("x-btn-focus");
26398         }
26399     },
26400     // private
26401     onBlur : function(e){
26402         this.el.removeClass("x-btn-focus");
26403     },
26404     // private
26405     onMouseDown : function(e){
26406         if(!this.disabled && e.button == 0){
26407             this.el.addClass("x-btn-click");
26408             Roo.get(document).on('mouseup', this.onMouseUp, this);
26409         }
26410     },
26411     // private
26412     onMouseUp : function(e){
26413         if(e.button == 0){
26414             this.el.removeClass("x-btn-click");
26415             Roo.get(document).un('mouseup', this.onMouseUp, this);
26416         }
26417     },
26418     // private
26419     onMenuShow : function(e){
26420         this.el.addClass("x-btn-menu-active");
26421     },
26422     // private
26423     onMenuHide : function(e){
26424         this.el.removeClass("x-btn-menu-active");
26425     }   
26426 });
26427
26428 // Private utility class used by Button
26429 Roo.ButtonToggleMgr = function(){
26430    var groups = {};
26431    
26432    function toggleGroup(btn, state){
26433        if(state){
26434            var g = groups[btn.toggleGroup];
26435            for(var i = 0, l = g.length; i < l; i++){
26436                if(g[i] != btn){
26437                    g[i].toggle(false);
26438                }
26439            }
26440        }
26441    }
26442    
26443    return {
26444        register : function(btn){
26445            if(!btn.toggleGroup){
26446                return;
26447            }
26448            var g = groups[btn.toggleGroup];
26449            if(!g){
26450                g = groups[btn.toggleGroup] = [];
26451            }
26452            g.push(btn);
26453            btn.on("toggle", toggleGroup);
26454        },
26455        
26456        unregister : function(btn){
26457            if(!btn.toggleGroup){
26458                return;
26459            }
26460            var g = groups[btn.toggleGroup];
26461            if(g){
26462                g.remove(btn);
26463                btn.un("toggle", toggleGroup);
26464            }
26465        }
26466    };
26467 }();/*
26468  * Based on:
26469  * Ext JS Library 1.1.1
26470  * Copyright(c) 2006-2007, Ext JS, LLC.
26471  *
26472  * Originally Released Under LGPL - original licence link has changed is not relivant.
26473  *
26474  * Fork - LGPL
26475  * <script type="text/javascript">
26476  */
26477  
26478 /**
26479  * @class Roo.SplitButton
26480  * @extends Roo.Button
26481  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26482  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26483  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26484  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26485  * @cfg {String} arrowTooltip The title attribute of the arrow
26486  * @constructor
26487  * Create a new menu button
26488  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26489  * @param {Object} config The config object
26490  */
26491 Roo.SplitButton = function(renderTo, config){
26492     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26493     /**
26494      * @event arrowclick
26495      * Fires when this button's arrow is clicked
26496      * @param {SplitButton} this
26497      * @param {EventObject} e The click event
26498      */
26499     this.addEvents({"arrowclick":true});
26500 };
26501
26502 Roo.extend(Roo.SplitButton, Roo.Button, {
26503     render : function(renderTo){
26504         // this is one sweet looking template!
26505         var tpl = new Roo.Template(
26506             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26507             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26508             '<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>',
26509             "</tbody></table></td><td>",
26510             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26511             '<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>',
26512             "</tbody></table></td></tr></table>"
26513         );
26514         var btn = tpl.append(renderTo, [this.text, this.type], true);
26515         var btnEl = btn.child("button");
26516         if(this.cls){
26517             btn.addClass(this.cls);
26518         }
26519         if(this.icon){
26520             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26521         }
26522         if(this.iconCls){
26523             btnEl.addClass(this.iconCls);
26524             if(!this.cls){
26525                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26526             }
26527         }
26528         this.el = btn;
26529         if(this.handleMouseEvents){
26530             btn.on("mouseover", this.onMouseOver, this);
26531             btn.on("mouseout", this.onMouseOut, this);
26532             btn.on("mousedown", this.onMouseDown, this);
26533             btn.on("mouseup", this.onMouseUp, this);
26534         }
26535         btn.on(this.clickEvent, this.onClick, this);
26536         if(this.tooltip){
26537             if(typeof this.tooltip == 'object'){
26538                 Roo.QuickTips.tips(Roo.apply({
26539                       target: btnEl.id
26540                 }, this.tooltip));
26541             } else {
26542                 btnEl.dom[this.tooltipType] = this.tooltip;
26543             }
26544         }
26545         if(this.arrowTooltip){
26546             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26547         }
26548         if(this.hidden){
26549             this.hide();
26550         }
26551         if(this.disabled){
26552             this.disable();
26553         }
26554         if(this.pressed){
26555             this.el.addClass("x-btn-pressed");
26556         }
26557         if(Roo.isIE && !Roo.isIE7){
26558             this.autoWidth.defer(1, this);
26559         }else{
26560             this.autoWidth();
26561         }
26562         if(this.menu){
26563             this.menu.on("show", this.onMenuShow, this);
26564             this.menu.on("hide", this.onMenuHide, this);
26565         }
26566         this.fireEvent('render', this);
26567     },
26568
26569     // private
26570     autoWidth : function(){
26571         if(this.el){
26572             var tbl = this.el.child("table:first");
26573             var tbl2 = this.el.child("table:last");
26574             this.el.setWidth("auto");
26575             tbl.setWidth("auto");
26576             if(Roo.isIE7 && Roo.isStrict){
26577                 var ib = this.el.child('button:first');
26578                 if(ib && ib.getWidth() > 20){
26579                     ib.clip();
26580                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26581                 }
26582             }
26583             if(this.minWidth){
26584                 if(this.hidden){
26585                     this.el.beginMeasure();
26586                 }
26587                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26588                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26589                 }
26590                 if(this.hidden){
26591                     this.el.endMeasure();
26592                 }
26593             }
26594             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26595         } 
26596     },
26597     /**
26598      * Sets this button's click handler
26599      * @param {Function} handler The function to call when the button is clicked
26600      * @param {Object} scope (optional) Scope for the function passed above
26601      */
26602     setHandler : function(handler, scope){
26603         this.handler = handler;
26604         this.scope = scope;  
26605     },
26606     
26607     /**
26608      * Sets this button's arrow click handler
26609      * @param {Function} handler The function to call when the arrow is clicked
26610      * @param {Object} scope (optional) Scope for the function passed above
26611      */
26612     setArrowHandler : function(handler, scope){
26613         this.arrowHandler = handler;
26614         this.scope = scope;  
26615     },
26616     
26617     /**
26618      * Focus the button
26619      */
26620     focus : function(){
26621         if(this.el){
26622             this.el.child("button:first").focus();
26623         }
26624     },
26625
26626     // private
26627     onClick : function(e){
26628         e.preventDefault();
26629         if(!this.disabled){
26630             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26631                 if(this.menu && !this.menu.isVisible()){
26632                     this.menu.show(this.el, this.menuAlign);
26633                 }
26634                 this.fireEvent("arrowclick", this, e);
26635                 if(this.arrowHandler){
26636                     this.arrowHandler.call(this.scope || this, this, e);
26637                 }
26638             }else{
26639                 this.fireEvent("click", this, e);
26640                 if(this.handler){
26641                     this.handler.call(this.scope || this, this, e);
26642                 }
26643             }
26644         }
26645     },
26646     // private
26647     onMouseDown : function(e){
26648         if(!this.disabled){
26649             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26650         }
26651     },
26652     // private
26653     onMouseUp : function(e){
26654         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26655     }   
26656 });
26657
26658
26659 // backwards compat
26660 Roo.MenuButton = Roo.SplitButton;/*
26661  * Based on:
26662  * Ext JS Library 1.1.1
26663  * Copyright(c) 2006-2007, Ext JS, LLC.
26664  *
26665  * Originally Released Under LGPL - original licence link has changed is not relivant.
26666  *
26667  * Fork - LGPL
26668  * <script type="text/javascript">
26669  */
26670
26671 /**
26672  * @class Roo.Toolbar
26673  * Basic Toolbar class.
26674  * @constructor
26675  * Creates a new Toolbar
26676  * @param {Object} config The config object
26677  */ 
26678 Roo.Toolbar = function(container, buttons, config)
26679 {
26680     /// old consturctor format still supported..
26681     if(container instanceof Array){ // omit the container for later rendering
26682         buttons = container;
26683         config = buttons;
26684         container = null;
26685     }
26686     if (typeof(container) == 'object' && container.xtype) {
26687         config = container;
26688         container = config.container;
26689         buttons = config.buttons; // not really - use items!!
26690     }
26691     var xitems = [];
26692     if (config && config.items) {
26693         xitems = config.items;
26694         delete config.items;
26695     }
26696     Roo.apply(this, config);
26697     this.buttons = buttons;
26698     
26699     if(container){
26700         this.render(container);
26701     }
26702     Roo.each(xitems, function(b) {
26703         this.add(b);
26704     }, this);
26705     
26706 };
26707
26708 Roo.Toolbar.prototype = {
26709     /**
26710      * @cfg {Roo.data.Store} items
26711      * array of button configs or elements to add
26712      */
26713     
26714     /**
26715      * @cfg {String/HTMLElement/Element} container
26716      * The id or element that will contain the toolbar
26717      */
26718     // private
26719     render : function(ct){
26720         this.el = Roo.get(ct);
26721         if(this.cls){
26722             this.el.addClass(this.cls);
26723         }
26724         // using a table allows for vertical alignment
26725         // 100% width is needed by Safari...
26726         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26727         this.tr = this.el.child("tr", true);
26728         var autoId = 0;
26729         this.items = new Roo.util.MixedCollection(false, function(o){
26730             return o.id || ("item" + (++autoId));
26731         });
26732         if(this.buttons){
26733             this.add.apply(this, this.buttons);
26734             delete this.buttons;
26735         }
26736     },
26737
26738     /**
26739      * Adds element(s) to the toolbar -- this function takes a variable number of 
26740      * arguments of mixed type and adds them to the toolbar.
26741      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26742      * <ul>
26743      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26744      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26745      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26746      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26747      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26748      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26749      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26750      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26751      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26752      * </ul>
26753      * @param {Mixed} arg2
26754      * @param {Mixed} etc.
26755      */
26756     add : function(){
26757         var a = arguments, l = a.length;
26758         for(var i = 0; i < l; i++){
26759             this._add(a[i]);
26760         }
26761     },
26762     // private..
26763     _add : function(el) {
26764         
26765         if (el.xtype) {
26766             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26767         }
26768         
26769         if (el.applyTo){ // some kind of form field
26770             return this.addField(el);
26771         } 
26772         if (el.render){ // some kind of Toolbar.Item
26773             return this.addItem(el);
26774         }
26775         if (typeof el == "string"){ // string
26776             if(el == "separator" || el == "-"){
26777                 return this.addSeparator();
26778             }
26779             if (el == " "){
26780                 return this.addSpacer();
26781             }
26782             if(el == "->"){
26783                 return this.addFill();
26784             }
26785             return this.addText(el);
26786             
26787         }
26788         if(el.tagName){ // element
26789             return this.addElement(el);
26790         }
26791         if(typeof el == "object"){ // must be button config?
26792             return this.addButton(el);
26793         }
26794         // and now what?!?!
26795         return false;
26796         
26797     },
26798     
26799     /**
26800      * Add an Xtype element
26801      * @param {Object} xtype Xtype Object
26802      * @return {Object} created Object
26803      */
26804     addxtype : function(e){
26805         return this.add(e);  
26806     },
26807     
26808     /**
26809      * Returns the Element for this toolbar.
26810      * @return {Roo.Element}
26811      */
26812     getEl : function(){
26813         return this.el;  
26814     },
26815     
26816     /**
26817      * Adds a separator
26818      * @return {Roo.Toolbar.Item} The separator item
26819      */
26820     addSeparator : function(){
26821         return this.addItem(new Roo.Toolbar.Separator());
26822     },
26823
26824     /**
26825      * Adds a spacer element
26826      * @return {Roo.Toolbar.Spacer} The spacer item
26827      */
26828     addSpacer : function(){
26829         return this.addItem(new Roo.Toolbar.Spacer());
26830     },
26831
26832     /**
26833      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26834      * @return {Roo.Toolbar.Fill} The fill item
26835      */
26836     addFill : function(){
26837         return this.addItem(new Roo.Toolbar.Fill());
26838     },
26839
26840     /**
26841      * Adds any standard HTML element to the toolbar
26842      * @param {String/HTMLElement/Element} el The element or id of the element to add
26843      * @return {Roo.Toolbar.Item} The element's item
26844      */
26845     addElement : function(el){
26846         return this.addItem(new Roo.Toolbar.Item(el));
26847     },
26848     /**
26849      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26850      * @type Roo.util.MixedCollection  
26851      */
26852     items : false,
26853      
26854     /**
26855      * Adds any Toolbar.Item or subclass
26856      * @param {Roo.Toolbar.Item} item
26857      * @return {Roo.Toolbar.Item} The item
26858      */
26859     addItem : function(item){
26860         var td = this.nextBlock();
26861         item.render(td);
26862         this.items.add(item);
26863         return item;
26864     },
26865     
26866     /**
26867      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26868      * @param {Object/Array} config A button config or array of configs
26869      * @return {Roo.Toolbar.Button/Array}
26870      */
26871     addButton : function(config){
26872         if(config instanceof Array){
26873             var buttons = [];
26874             for(var i = 0, len = config.length; i < len; i++) {
26875                 buttons.push(this.addButton(config[i]));
26876             }
26877             return buttons;
26878         }
26879         var b = config;
26880         if(!(config instanceof Roo.Toolbar.Button)){
26881             b = config.split ?
26882                 new Roo.Toolbar.SplitButton(config) :
26883                 new Roo.Toolbar.Button(config);
26884         }
26885         var td = this.nextBlock();
26886         b.render(td);
26887         this.items.add(b);
26888         return b;
26889     },
26890     
26891     /**
26892      * Adds text to the toolbar
26893      * @param {String} text The text to add
26894      * @return {Roo.Toolbar.Item} The element's item
26895      */
26896     addText : function(text){
26897         return this.addItem(new Roo.Toolbar.TextItem(text));
26898     },
26899     
26900     /**
26901      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26902      * @param {Number} index The index where the item is to be inserted
26903      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26904      * @return {Roo.Toolbar.Button/Item}
26905      */
26906     insertButton : function(index, item){
26907         if(item instanceof Array){
26908             var buttons = [];
26909             for(var i = 0, len = item.length; i < len; i++) {
26910                buttons.push(this.insertButton(index + i, item[i]));
26911             }
26912             return buttons;
26913         }
26914         if (!(item instanceof Roo.Toolbar.Button)){
26915            item = new Roo.Toolbar.Button(item);
26916         }
26917         var td = document.createElement("td");
26918         this.tr.insertBefore(td, this.tr.childNodes[index]);
26919         item.render(td);
26920         this.items.insert(index, item);
26921         return item;
26922     },
26923     
26924     /**
26925      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26926      * @param {Object} config
26927      * @return {Roo.Toolbar.Item} The element's item
26928      */
26929     addDom : function(config, returnEl){
26930         var td = this.nextBlock();
26931         Roo.DomHelper.overwrite(td, config);
26932         var ti = new Roo.Toolbar.Item(td.firstChild);
26933         ti.render(td);
26934         this.items.add(ti);
26935         return ti;
26936     },
26937
26938     /**
26939      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26940      * @type Roo.util.MixedCollection  
26941      */
26942     fields : false,
26943     
26944     /**
26945      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26946      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26947      * @param {Roo.form.Field} field
26948      * @return {Roo.ToolbarItem}
26949      */
26950      
26951       
26952     addField : function(field) {
26953         if (!this.fields) {
26954             var autoId = 0;
26955             this.fields = new Roo.util.MixedCollection(false, function(o){
26956                 return o.id || ("item" + (++autoId));
26957             });
26958
26959         }
26960         
26961         var td = this.nextBlock();
26962         field.render(td);
26963         var ti = new Roo.Toolbar.Item(td.firstChild);
26964         ti.render(td);
26965         this.items.add(ti);
26966         this.fields.add(field);
26967         return ti;
26968     },
26969     /**
26970      * Hide the toolbar
26971      * @method hide
26972      */
26973      
26974       
26975     hide : function()
26976     {
26977         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26978         this.el.child('div').hide();
26979     },
26980     /**
26981      * Show the toolbar
26982      * @method show
26983      */
26984     show : function()
26985     {
26986         this.el.child('div').show();
26987     },
26988       
26989     // private
26990     nextBlock : function(){
26991         var td = document.createElement("td");
26992         this.tr.appendChild(td);
26993         return td;
26994     },
26995
26996     // private
26997     destroy : function(){
26998         if(this.items){ // rendered?
26999             Roo.destroy.apply(Roo, this.items.items);
27000         }
27001         if(this.fields){ // rendered?
27002             Roo.destroy.apply(Roo, this.fields.items);
27003         }
27004         Roo.Element.uncache(this.el, this.tr);
27005     }
27006 };
27007
27008 /**
27009  * @class Roo.Toolbar.Item
27010  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27011  * @constructor
27012  * Creates a new Item
27013  * @param {HTMLElement} el 
27014  */
27015 Roo.Toolbar.Item = function(el){
27016     this.el = Roo.getDom(el);
27017     this.id = Roo.id(this.el);
27018     this.hidden = false;
27019 };
27020
27021 Roo.Toolbar.Item.prototype = {
27022     
27023     /**
27024      * Get this item's HTML Element
27025      * @return {HTMLElement}
27026      */
27027     getEl : function(){
27028        return this.el;  
27029     },
27030
27031     // private
27032     render : function(td){
27033         this.td = td;
27034         td.appendChild(this.el);
27035     },
27036     
27037     /**
27038      * Removes and destroys this item.
27039      */
27040     destroy : function(){
27041         this.td.parentNode.removeChild(this.td);
27042     },
27043     
27044     /**
27045      * Shows this item.
27046      */
27047     show: function(){
27048         this.hidden = false;
27049         this.td.style.display = "";
27050     },
27051     
27052     /**
27053      * Hides this item.
27054      */
27055     hide: function(){
27056         this.hidden = true;
27057         this.td.style.display = "none";
27058     },
27059     
27060     /**
27061      * Convenience function for boolean show/hide.
27062      * @param {Boolean} visible true to show/false to hide
27063      */
27064     setVisible: function(visible){
27065         if(visible) {
27066             this.show();
27067         }else{
27068             this.hide();
27069         }
27070     },
27071     
27072     /**
27073      * Try to focus this item.
27074      */
27075     focus : function(){
27076         Roo.fly(this.el).focus();
27077     },
27078     
27079     /**
27080      * Disables this item.
27081      */
27082     disable : function(){
27083         Roo.fly(this.td).addClass("x-item-disabled");
27084         this.disabled = true;
27085         this.el.disabled = true;
27086     },
27087     
27088     /**
27089      * Enables this item.
27090      */
27091     enable : function(){
27092         Roo.fly(this.td).removeClass("x-item-disabled");
27093         this.disabled = false;
27094         this.el.disabled = false;
27095     }
27096 };
27097
27098
27099 /**
27100  * @class Roo.Toolbar.Separator
27101  * @extends Roo.Toolbar.Item
27102  * A simple toolbar separator class
27103  * @constructor
27104  * Creates a new Separator
27105  */
27106 Roo.Toolbar.Separator = function(){
27107     var s = document.createElement("span");
27108     s.className = "ytb-sep";
27109     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27110 };
27111 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27112     enable:Roo.emptyFn,
27113     disable:Roo.emptyFn,
27114     focus:Roo.emptyFn
27115 });
27116
27117 /**
27118  * @class Roo.Toolbar.Spacer
27119  * @extends Roo.Toolbar.Item
27120  * A simple element that adds extra horizontal space to a toolbar.
27121  * @constructor
27122  * Creates a new Spacer
27123  */
27124 Roo.Toolbar.Spacer = function(){
27125     var s = document.createElement("div");
27126     s.className = "ytb-spacer";
27127     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27128 };
27129 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27130     enable:Roo.emptyFn,
27131     disable:Roo.emptyFn,
27132     focus:Roo.emptyFn
27133 });
27134
27135 /**
27136  * @class Roo.Toolbar.Fill
27137  * @extends Roo.Toolbar.Spacer
27138  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27139  * @constructor
27140  * Creates a new Spacer
27141  */
27142 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27143     // private
27144     render : function(td){
27145         td.style.width = '100%';
27146         Roo.Toolbar.Fill.superclass.render.call(this, td);
27147     }
27148 });
27149
27150 /**
27151  * @class Roo.Toolbar.TextItem
27152  * @extends Roo.Toolbar.Item
27153  * A simple class that renders text directly into a toolbar.
27154  * @constructor
27155  * Creates a new TextItem
27156  * @param {String} text
27157  */
27158 Roo.Toolbar.TextItem = function(text){
27159     if (typeof(text) == 'object') {
27160         text = text.text;
27161     }
27162     var s = document.createElement("span");
27163     s.className = "ytb-text";
27164     s.innerHTML = text;
27165     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27166 };
27167 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27168     enable:Roo.emptyFn,
27169     disable:Roo.emptyFn,
27170     focus:Roo.emptyFn
27171 });
27172
27173 /**
27174  * @class Roo.Toolbar.Button
27175  * @extends Roo.Button
27176  * A button that renders into a toolbar.
27177  * @constructor
27178  * Creates a new Button
27179  * @param {Object} config A standard {@link Roo.Button} config object
27180  */
27181 Roo.Toolbar.Button = function(config){
27182     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27183 };
27184 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27185     render : function(td){
27186         this.td = td;
27187         Roo.Toolbar.Button.superclass.render.call(this, td);
27188     },
27189     
27190     /**
27191      * Removes and destroys this button
27192      */
27193     destroy : function(){
27194         Roo.Toolbar.Button.superclass.destroy.call(this);
27195         this.td.parentNode.removeChild(this.td);
27196     },
27197     
27198     /**
27199      * Shows this button
27200      */
27201     show: function(){
27202         this.hidden = false;
27203         this.td.style.display = "";
27204     },
27205     
27206     /**
27207      * Hides this button
27208      */
27209     hide: function(){
27210         this.hidden = true;
27211         this.td.style.display = "none";
27212     },
27213
27214     /**
27215      * Disables this item
27216      */
27217     disable : function(){
27218         Roo.fly(this.td).addClass("x-item-disabled");
27219         this.disabled = true;
27220     },
27221
27222     /**
27223      * Enables this item
27224      */
27225     enable : function(){
27226         Roo.fly(this.td).removeClass("x-item-disabled");
27227         this.disabled = false;
27228     }
27229 });
27230 // backwards compat
27231 Roo.ToolbarButton = Roo.Toolbar.Button;
27232
27233 /**
27234  * @class Roo.Toolbar.SplitButton
27235  * @extends Roo.SplitButton
27236  * A menu button that renders into a toolbar.
27237  * @constructor
27238  * Creates a new SplitButton
27239  * @param {Object} config A standard {@link Roo.SplitButton} config object
27240  */
27241 Roo.Toolbar.SplitButton = function(config){
27242     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27243 };
27244 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27245     render : function(td){
27246         this.td = td;
27247         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27248     },
27249     
27250     /**
27251      * Removes and destroys this button
27252      */
27253     destroy : function(){
27254         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27255         this.td.parentNode.removeChild(this.td);
27256     },
27257     
27258     /**
27259      * Shows this button
27260      */
27261     show: function(){
27262         this.hidden = false;
27263         this.td.style.display = "";
27264     },
27265     
27266     /**
27267      * Hides this button
27268      */
27269     hide: function(){
27270         this.hidden = true;
27271         this.td.style.display = "none";
27272     }
27273 });
27274
27275 // backwards compat
27276 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27277  * Based on:
27278  * Ext JS Library 1.1.1
27279  * Copyright(c) 2006-2007, Ext JS, LLC.
27280  *
27281  * Originally Released Under LGPL - original licence link has changed is not relivant.
27282  *
27283  * Fork - LGPL
27284  * <script type="text/javascript">
27285  */
27286  
27287 /**
27288  * @class Roo.PagingToolbar
27289  * @extends Roo.Toolbar
27290  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27291  * @constructor
27292  * Create a new PagingToolbar
27293  * @param {Object} config The config object
27294  */
27295 Roo.PagingToolbar = function(el, ds, config)
27296 {
27297     // old args format still supported... - xtype is prefered..
27298     if (typeof(el) == 'object' && el.xtype) {
27299         // created from xtype...
27300         config = el;
27301         ds = el.dataSource;
27302         el = config.container;
27303     }
27304     var items = [];
27305     if (config.items) {
27306         items = config.items;
27307         config.items = [];
27308     }
27309     
27310     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27311     this.ds = ds;
27312     this.cursor = 0;
27313     this.renderButtons(this.el);
27314     this.bind(ds);
27315     
27316     // supprot items array.
27317    
27318     Roo.each(items, function(e) {
27319         this.add(Roo.factory(e));
27320     },this);
27321     
27322 };
27323
27324 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27325     /**
27326      * @cfg {Roo.data.Store} dataSource
27327      * The underlying data store providing the paged data
27328      */
27329     /**
27330      * @cfg {String/HTMLElement/Element} container
27331      * container The id or element that will contain the toolbar
27332      */
27333     /**
27334      * @cfg {Boolean} displayInfo
27335      * True to display the displayMsg (defaults to false)
27336      */
27337     /**
27338      * @cfg {Number} pageSize
27339      * The number of records to display per page (defaults to 20)
27340      */
27341     pageSize: 20,
27342     /**
27343      * @cfg {String} displayMsg
27344      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27345      */
27346     displayMsg : 'Displaying {0} - {1} of {2}',
27347     /**
27348      * @cfg {String} emptyMsg
27349      * The message to display when no records are found (defaults to "No data to display")
27350      */
27351     emptyMsg : 'No data to display',
27352     /**
27353      * Customizable piece of the default paging text (defaults to "Page")
27354      * @type String
27355      */
27356     beforePageText : "Page",
27357     /**
27358      * Customizable piece of the default paging text (defaults to "of %0")
27359      * @type String
27360      */
27361     afterPageText : "of {0}",
27362     /**
27363      * Customizable piece of the default paging text (defaults to "First Page")
27364      * @type String
27365      */
27366     firstText : "First Page",
27367     /**
27368      * Customizable piece of the default paging text (defaults to "Previous Page")
27369      * @type String
27370      */
27371     prevText : "Previous Page",
27372     /**
27373      * Customizable piece of the default paging text (defaults to "Next Page")
27374      * @type String
27375      */
27376     nextText : "Next Page",
27377     /**
27378      * Customizable piece of the default paging text (defaults to "Last Page")
27379      * @type String
27380      */
27381     lastText : "Last Page",
27382     /**
27383      * Customizable piece of the default paging text (defaults to "Refresh")
27384      * @type String
27385      */
27386     refreshText : "Refresh",
27387
27388     // private
27389     renderButtons : function(el){
27390         Roo.PagingToolbar.superclass.render.call(this, el);
27391         this.first = this.addButton({
27392             tooltip: this.firstText,
27393             cls: "x-btn-icon x-grid-page-first",
27394             disabled: true,
27395             handler: this.onClick.createDelegate(this, ["first"])
27396         });
27397         this.prev = this.addButton({
27398             tooltip: this.prevText,
27399             cls: "x-btn-icon x-grid-page-prev",
27400             disabled: true,
27401             handler: this.onClick.createDelegate(this, ["prev"])
27402         });
27403         //this.addSeparator();
27404         this.add(this.beforePageText);
27405         this.field = Roo.get(this.addDom({
27406            tag: "input",
27407            type: "text",
27408            size: "3",
27409            value: "1",
27410            cls: "x-grid-page-number"
27411         }).el);
27412         this.field.on("keydown", this.onPagingKeydown, this);
27413         this.field.on("focus", function(){this.dom.select();});
27414         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27415         this.field.setHeight(18);
27416         //this.addSeparator();
27417         this.next = this.addButton({
27418             tooltip: this.nextText,
27419             cls: "x-btn-icon x-grid-page-next",
27420             disabled: true,
27421             handler: this.onClick.createDelegate(this, ["next"])
27422         });
27423         this.last = this.addButton({
27424             tooltip: this.lastText,
27425             cls: "x-btn-icon x-grid-page-last",
27426             disabled: true,
27427             handler: this.onClick.createDelegate(this, ["last"])
27428         });
27429         //this.addSeparator();
27430         this.loading = this.addButton({
27431             tooltip: this.refreshText,
27432             cls: "x-btn-icon x-grid-loading",
27433             handler: this.onClick.createDelegate(this, ["refresh"])
27434         });
27435
27436         if(this.displayInfo){
27437             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27438         }
27439     },
27440
27441     // private
27442     updateInfo : function(){
27443         if(this.displayEl){
27444             var count = this.ds.getCount();
27445             var msg = count == 0 ?
27446                 this.emptyMsg :
27447                 String.format(
27448                     this.displayMsg,
27449                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27450                 );
27451             this.displayEl.update(msg);
27452         }
27453     },
27454
27455     // private
27456     onLoad : function(ds, r, o){
27457        this.cursor = o.params ? o.params.start : 0;
27458        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27459
27460        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27461        this.field.dom.value = ap;
27462        this.first.setDisabled(ap == 1);
27463        this.prev.setDisabled(ap == 1);
27464        this.next.setDisabled(ap == ps);
27465        this.last.setDisabled(ap == ps);
27466        this.loading.enable();
27467        this.updateInfo();
27468     },
27469
27470     // private
27471     getPageData : function(){
27472         var total = this.ds.getTotalCount();
27473         return {
27474             total : total,
27475             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27476             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27477         };
27478     },
27479
27480     // private
27481     onLoadError : function(){
27482         this.loading.enable();
27483     },
27484
27485     // private
27486     onPagingKeydown : function(e){
27487         var k = e.getKey();
27488         var d = this.getPageData();
27489         if(k == e.RETURN){
27490             var v = this.field.dom.value, pageNum;
27491             if(!v || isNaN(pageNum = parseInt(v, 10))){
27492                 this.field.dom.value = d.activePage;
27493                 return;
27494             }
27495             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27496             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27497             e.stopEvent();
27498         }
27499         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))
27500         {
27501           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27502           this.field.dom.value = pageNum;
27503           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27504           e.stopEvent();
27505         }
27506         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27507         {
27508           var v = this.field.dom.value, pageNum; 
27509           var increment = (e.shiftKey) ? 10 : 1;
27510           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27511             increment *= -1;
27512           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27513             this.field.dom.value = d.activePage;
27514             return;
27515           }
27516           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27517           {
27518             this.field.dom.value = parseInt(v, 10) + increment;
27519             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27520             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27521           }
27522           e.stopEvent();
27523         }
27524     },
27525
27526     // private
27527     beforeLoad : function(){
27528         if(this.loading){
27529             this.loading.disable();
27530         }
27531     },
27532
27533     // private
27534     onClick : function(which){
27535         var ds = this.ds;
27536         switch(which){
27537             case "first":
27538                 ds.load({params:{start: 0, limit: this.pageSize}});
27539             break;
27540             case "prev":
27541                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27542             break;
27543             case "next":
27544                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27545             break;
27546             case "last":
27547                 var total = ds.getTotalCount();
27548                 var extra = total % this.pageSize;
27549                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27550                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27551             break;
27552             case "refresh":
27553                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27554             break;
27555         }
27556     },
27557
27558     /**
27559      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27560      * @param {Roo.data.Store} store The data store to unbind
27561      */
27562     unbind : function(ds){
27563         ds.un("beforeload", this.beforeLoad, this);
27564         ds.un("load", this.onLoad, this);
27565         ds.un("loadexception", this.onLoadError, this);
27566         ds.un("remove", this.updateInfo, this);
27567         ds.un("add", this.updateInfo, this);
27568         this.ds = undefined;
27569     },
27570
27571     /**
27572      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27573      * @param {Roo.data.Store} store The data store to bind
27574      */
27575     bind : function(ds){
27576         ds.on("beforeload", this.beforeLoad, this);
27577         ds.on("load", this.onLoad, this);
27578         ds.on("loadexception", this.onLoadError, this);
27579         ds.on("remove", this.updateInfo, this);
27580         ds.on("add", this.updateInfo, this);
27581         this.ds = ds;
27582     }
27583 });/*
27584  * Based on:
27585  * Ext JS Library 1.1.1
27586  * Copyright(c) 2006-2007, Ext JS, LLC.
27587  *
27588  * Originally Released Under LGPL - original licence link has changed is not relivant.
27589  *
27590  * Fork - LGPL
27591  * <script type="text/javascript">
27592  */
27593
27594 /**
27595  * @class Roo.Resizable
27596  * @extends Roo.util.Observable
27597  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27598  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27599  * 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
27600  * the element will be wrapped for you automatically.</p>
27601  * <p>Here is the list of valid resize handles:</p>
27602  * <pre>
27603 Value   Description
27604 ------  -------------------
27605  'n'     north
27606  's'     south
27607  'e'     east
27608  'w'     west
27609  'nw'    northwest
27610  'sw'    southwest
27611  'se'    southeast
27612  'ne'    northeast
27613  'hd'    horizontal drag
27614  'all'   all
27615 </pre>
27616  * <p>Here's an example showing the creation of a typical Resizable:</p>
27617  * <pre><code>
27618 var resizer = new Roo.Resizable("element-id", {
27619     handles: 'all',
27620     minWidth: 200,
27621     minHeight: 100,
27622     maxWidth: 500,
27623     maxHeight: 400,
27624     pinned: true
27625 });
27626 resizer.on("resize", myHandler);
27627 </code></pre>
27628  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27629  * resizer.east.setDisplayed(false);</p>
27630  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27631  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27632  * resize operation's new size (defaults to [0, 0])
27633  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27634  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27635  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27636  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27637  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27638  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27639  * @cfg {Number} width The width of the element in pixels (defaults to null)
27640  * @cfg {Number} height The height of the element in pixels (defaults to null)
27641  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27642  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27643  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27644  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27645  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27646  * in favor of the handles config option (defaults to false)
27647  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27648  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27649  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27650  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27651  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27652  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27653  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27654  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27655  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27656  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27657  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27658  * @constructor
27659  * Create a new resizable component
27660  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27661  * @param {Object} config configuration options
27662   */
27663 Roo.Resizable = function(el, config)
27664 {
27665     this.el = Roo.get(el);
27666
27667     if(config && config.wrap){
27668         config.resizeChild = this.el;
27669         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27670         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27671         this.el.setStyle("overflow", "hidden");
27672         this.el.setPositioning(config.resizeChild.getPositioning());
27673         config.resizeChild.clearPositioning();
27674         if(!config.width || !config.height){
27675             var csize = config.resizeChild.getSize();
27676             this.el.setSize(csize.width, csize.height);
27677         }
27678         if(config.pinned && !config.adjustments){
27679             config.adjustments = "auto";
27680         }
27681     }
27682
27683     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27684     this.proxy.unselectable();
27685     this.proxy.enableDisplayMode('block');
27686
27687     Roo.apply(this, config);
27688
27689     if(this.pinned){
27690         this.disableTrackOver = true;
27691         this.el.addClass("x-resizable-pinned");
27692     }
27693     // if the element isn't positioned, make it relative
27694     var position = this.el.getStyle("position");
27695     if(position != "absolute" && position != "fixed"){
27696         this.el.setStyle("position", "relative");
27697     }
27698     if(!this.handles){ // no handles passed, must be legacy style
27699         this.handles = 's,e,se';
27700         if(this.multiDirectional){
27701             this.handles += ',n,w';
27702         }
27703     }
27704     if(this.handles == "all"){
27705         this.handles = "n s e w ne nw se sw";
27706     }
27707     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27708     var ps = Roo.Resizable.positions;
27709     for(var i = 0, len = hs.length; i < len; i++){
27710         if(hs[i] && ps[hs[i]]){
27711             var pos = ps[hs[i]];
27712             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27713         }
27714     }
27715     // legacy
27716     this.corner = this.southeast;
27717     
27718     // updateBox = the box can move..
27719     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27720         this.updateBox = true;
27721     }
27722
27723     this.activeHandle = null;
27724
27725     if(this.resizeChild){
27726         if(typeof this.resizeChild == "boolean"){
27727             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27728         }else{
27729             this.resizeChild = Roo.get(this.resizeChild, true);
27730         }
27731     }
27732     
27733     if(this.adjustments == "auto"){
27734         var rc = this.resizeChild;
27735         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27736         if(rc && (hw || hn)){
27737             rc.position("relative");
27738             rc.setLeft(hw ? hw.el.getWidth() : 0);
27739             rc.setTop(hn ? hn.el.getHeight() : 0);
27740         }
27741         this.adjustments = [
27742             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27743             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27744         ];
27745     }
27746
27747     if(this.draggable){
27748         this.dd = this.dynamic ?
27749             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27750         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27751     }
27752
27753     // public events
27754     this.addEvents({
27755         /**
27756          * @event beforeresize
27757          * Fired before resize is allowed. Set enabled to false to cancel resize.
27758          * @param {Roo.Resizable} this
27759          * @param {Roo.EventObject} e The mousedown event
27760          */
27761         "beforeresize" : true,
27762         /**
27763          * @event resize
27764          * Fired after a resize.
27765          * @param {Roo.Resizable} this
27766          * @param {Number} width The new width
27767          * @param {Number} height The new height
27768          * @param {Roo.EventObject} e The mouseup event
27769          */
27770         "resize" : true
27771     });
27772
27773     if(this.width !== null && this.height !== null){
27774         this.resizeTo(this.width, this.height);
27775     }else{
27776         this.updateChildSize();
27777     }
27778     if(Roo.isIE){
27779         this.el.dom.style.zoom = 1;
27780     }
27781     Roo.Resizable.superclass.constructor.call(this);
27782 };
27783
27784 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27785         resizeChild : false,
27786         adjustments : [0, 0],
27787         minWidth : 5,
27788         minHeight : 5,
27789         maxWidth : 10000,
27790         maxHeight : 10000,
27791         enabled : true,
27792         animate : false,
27793         duration : .35,
27794         dynamic : false,
27795         handles : false,
27796         multiDirectional : false,
27797         disableTrackOver : false,
27798         easing : 'easeOutStrong',
27799         widthIncrement : 0,
27800         heightIncrement : 0,
27801         pinned : false,
27802         width : null,
27803         height : null,
27804         preserveRatio : false,
27805         transparent: false,
27806         minX: 0,
27807         minY: 0,
27808         draggable: false,
27809
27810         /**
27811          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27812          */
27813         constrainTo: undefined,
27814         /**
27815          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27816          */
27817         resizeRegion: undefined,
27818
27819
27820     /**
27821      * Perform a manual resize
27822      * @param {Number} width
27823      * @param {Number} height
27824      */
27825     resizeTo : function(width, height){
27826         this.el.setSize(width, height);
27827         this.updateChildSize();
27828         this.fireEvent("resize", this, width, height, null);
27829     },
27830
27831     // private
27832     startSizing : function(e, handle){
27833         this.fireEvent("beforeresize", this, e);
27834         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27835
27836             if(!this.overlay){
27837                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27838                 this.overlay.unselectable();
27839                 this.overlay.enableDisplayMode("block");
27840                 this.overlay.on("mousemove", this.onMouseMove, this);
27841                 this.overlay.on("mouseup", this.onMouseUp, this);
27842             }
27843             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27844
27845             this.resizing = true;
27846             this.startBox = this.el.getBox();
27847             this.startPoint = e.getXY();
27848             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27849                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27850
27851             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27852             this.overlay.show();
27853
27854             if(this.constrainTo) {
27855                 var ct = Roo.get(this.constrainTo);
27856                 this.resizeRegion = ct.getRegion().adjust(
27857                     ct.getFrameWidth('t'),
27858                     ct.getFrameWidth('l'),
27859                     -ct.getFrameWidth('b'),
27860                     -ct.getFrameWidth('r')
27861                 );
27862             }
27863
27864             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27865             this.proxy.show();
27866             this.proxy.setBox(this.startBox);
27867             if(!this.dynamic){
27868                 this.proxy.setStyle('visibility', 'visible');
27869             }
27870         }
27871     },
27872
27873     // private
27874     onMouseDown : function(handle, e){
27875         if(this.enabled){
27876             e.stopEvent();
27877             this.activeHandle = handle;
27878             this.startSizing(e, handle);
27879         }
27880     },
27881
27882     // private
27883     onMouseUp : function(e){
27884         var size = this.resizeElement();
27885         this.resizing = false;
27886         this.handleOut();
27887         this.overlay.hide();
27888         this.proxy.hide();
27889         this.fireEvent("resize", this, size.width, size.height, e);
27890     },
27891
27892     // private
27893     updateChildSize : function(){
27894         if(this.resizeChild){
27895             var el = this.el;
27896             var child = this.resizeChild;
27897             var adj = this.adjustments;
27898             if(el.dom.offsetWidth){
27899                 var b = el.getSize(true);
27900                 child.setSize(b.width+adj[0], b.height+adj[1]);
27901             }
27902             // Second call here for IE
27903             // The first call enables instant resizing and
27904             // the second call corrects scroll bars if they
27905             // exist
27906             if(Roo.isIE){
27907                 setTimeout(function(){
27908                     if(el.dom.offsetWidth){
27909                         var b = el.getSize(true);
27910                         child.setSize(b.width+adj[0], b.height+adj[1]);
27911                     }
27912                 }, 10);
27913             }
27914         }
27915     },
27916
27917     // private
27918     snap : function(value, inc, min){
27919         if(!inc || !value) return value;
27920         var newValue = value;
27921         var m = value % inc;
27922         if(m > 0){
27923             if(m > (inc/2)){
27924                 newValue = value + (inc-m);
27925             }else{
27926                 newValue = value - m;
27927             }
27928         }
27929         return Math.max(min, newValue);
27930     },
27931
27932     // private
27933     resizeElement : function(){
27934         var box = this.proxy.getBox();
27935         if(this.updateBox){
27936             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27937         }else{
27938             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27939         }
27940         this.updateChildSize();
27941         if(!this.dynamic){
27942             this.proxy.hide();
27943         }
27944         return box;
27945     },
27946
27947     // private
27948     constrain : function(v, diff, m, mx){
27949         if(v - diff < m){
27950             diff = v - m;
27951         }else if(v - diff > mx){
27952             diff = mx - v;
27953         }
27954         return diff;
27955     },
27956
27957     // private
27958     onMouseMove : function(e){
27959         if(this.enabled){
27960             try{// try catch so if something goes wrong the user doesn't get hung
27961
27962             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27963                 return;
27964             }
27965
27966             //var curXY = this.startPoint;
27967             var curSize = this.curSize || this.startBox;
27968             var x = this.startBox.x, y = this.startBox.y;
27969             var ox = x, oy = y;
27970             var w = curSize.width, h = curSize.height;
27971             var ow = w, oh = h;
27972             var mw = this.minWidth, mh = this.minHeight;
27973             var mxw = this.maxWidth, mxh = this.maxHeight;
27974             var wi = this.widthIncrement;
27975             var hi = this.heightIncrement;
27976
27977             var eventXY = e.getXY();
27978             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27979             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27980
27981             var pos = this.activeHandle.position;
27982
27983             switch(pos){
27984                 case "east":
27985                     w += diffX;
27986                     w = Math.min(Math.max(mw, w), mxw);
27987                     break;
27988              
27989                 case "south":
27990                     h += diffY;
27991                     h = Math.min(Math.max(mh, h), mxh);
27992                     break;
27993                 case "southeast":
27994                     w += diffX;
27995                     h += diffY;
27996                     w = Math.min(Math.max(mw, w), mxw);
27997                     h = Math.min(Math.max(mh, h), mxh);
27998                     break;
27999                 case "north":
28000                     diffY = this.constrain(h, diffY, mh, mxh);
28001                     y += diffY;
28002                     h -= diffY;
28003                     break;
28004                 case "hdrag":
28005                     
28006                     if (wi) {
28007                         var adiffX = Math.abs(diffX);
28008                         var sub = (adiffX % wi); // how much 
28009                         if (sub > (wi/2)) { // far enough to snap
28010                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28011                         } else {
28012                             // remove difference.. 
28013                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28014                         }
28015                     }
28016                     x += diffX;
28017                     x = Math.max(this.minX, x);
28018                     break;
28019                 case "west":
28020                     diffX = this.constrain(w, diffX, mw, mxw);
28021                     x += diffX;
28022                     w -= diffX;
28023                     break;
28024                 case "northeast":
28025                     w += diffX;
28026                     w = Math.min(Math.max(mw, w), mxw);
28027                     diffY = this.constrain(h, diffY, mh, mxh);
28028                     y += diffY;
28029                     h -= diffY;
28030                     break;
28031                 case "northwest":
28032                     diffX = this.constrain(w, diffX, mw, mxw);
28033                     diffY = this.constrain(h, diffY, mh, mxh);
28034                     y += diffY;
28035                     h -= diffY;
28036                     x += diffX;
28037                     w -= diffX;
28038                     break;
28039                case "southwest":
28040                     diffX = this.constrain(w, diffX, mw, mxw);
28041                     h += diffY;
28042                     h = Math.min(Math.max(mh, h), mxh);
28043                     x += diffX;
28044                     w -= diffX;
28045                     break;
28046             }
28047
28048             var sw = this.snap(w, wi, mw);
28049             var sh = this.snap(h, hi, mh);
28050             if(sw != w || sh != h){
28051                 switch(pos){
28052                     case "northeast":
28053                         y -= sh - h;
28054                     break;
28055                     case "north":
28056                         y -= sh - h;
28057                         break;
28058                     case "southwest":
28059                         x -= sw - w;
28060                     break;
28061                     case "west":
28062                         x -= sw - w;
28063                         break;
28064                     case "northwest":
28065                         x -= sw - w;
28066                         y -= sh - h;
28067                     break;
28068                 }
28069                 w = sw;
28070                 h = sh;
28071             }
28072
28073             if(this.preserveRatio){
28074                 switch(pos){
28075                     case "southeast":
28076                     case "east":
28077                         h = oh * (w/ow);
28078                         h = Math.min(Math.max(mh, h), mxh);
28079                         w = ow * (h/oh);
28080                        break;
28081                     case "south":
28082                         w = ow * (h/oh);
28083                         w = Math.min(Math.max(mw, w), mxw);
28084                         h = oh * (w/ow);
28085                         break;
28086                     case "northeast":
28087                         w = ow * (h/oh);
28088                         w = Math.min(Math.max(mw, w), mxw);
28089                         h = oh * (w/ow);
28090                     break;
28091                     case "north":
28092                         var tw = w;
28093                         w = ow * (h/oh);
28094                         w = Math.min(Math.max(mw, w), mxw);
28095                         h = oh * (w/ow);
28096                         x += (tw - w) / 2;
28097                         break;
28098                     case "southwest":
28099                         h = oh * (w/ow);
28100                         h = Math.min(Math.max(mh, h), mxh);
28101                         var tw = w;
28102                         w = ow * (h/oh);
28103                         x += tw - w;
28104                         break;
28105                     case "west":
28106                         var th = h;
28107                         h = oh * (w/ow);
28108                         h = Math.min(Math.max(mh, h), mxh);
28109                         y += (th - h) / 2;
28110                         var tw = w;
28111                         w = ow * (h/oh);
28112                         x += tw - w;
28113                        break;
28114                     case "northwest":
28115                         var tw = w;
28116                         var th = h;
28117                         h = oh * (w/ow);
28118                         h = Math.min(Math.max(mh, h), mxh);
28119                         w = ow * (h/oh);
28120                         y += th - h;
28121                         x += tw - w;
28122                        break;
28123
28124                 }
28125             }
28126             if (pos == 'hdrag') {
28127                 w = ow;
28128             }
28129             this.proxy.setBounds(x, y, w, h);
28130             if(this.dynamic){
28131                 this.resizeElement();
28132             }
28133             }catch(e){}
28134         }
28135     },
28136
28137     // private
28138     handleOver : function(){
28139         if(this.enabled){
28140             this.el.addClass("x-resizable-over");
28141         }
28142     },
28143
28144     // private
28145     handleOut : function(){
28146         if(!this.resizing){
28147             this.el.removeClass("x-resizable-over");
28148         }
28149     },
28150
28151     /**
28152      * Returns the element this component is bound to.
28153      * @return {Roo.Element}
28154      */
28155     getEl : function(){
28156         return this.el;
28157     },
28158
28159     /**
28160      * Returns the resizeChild element (or null).
28161      * @return {Roo.Element}
28162      */
28163     getResizeChild : function(){
28164         return this.resizeChild;
28165     },
28166
28167     /**
28168      * Destroys this resizable. If the element was wrapped and
28169      * removeEl is not true then the element remains.
28170      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28171      */
28172     destroy : function(removeEl){
28173         this.proxy.remove();
28174         if(this.overlay){
28175             this.overlay.removeAllListeners();
28176             this.overlay.remove();
28177         }
28178         var ps = Roo.Resizable.positions;
28179         for(var k in ps){
28180             if(typeof ps[k] != "function" && this[ps[k]]){
28181                 var h = this[ps[k]];
28182                 h.el.removeAllListeners();
28183                 h.el.remove();
28184             }
28185         }
28186         if(removeEl){
28187             this.el.update("");
28188             this.el.remove();
28189         }
28190     }
28191 });
28192
28193 // private
28194 // hash to map config positions to true positions
28195 Roo.Resizable.positions = {
28196     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28197     hd: "hdrag"
28198 };
28199
28200 // private
28201 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28202     if(!this.tpl){
28203         // only initialize the template if resizable is used
28204         var tpl = Roo.DomHelper.createTemplate(
28205             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28206         );
28207         tpl.compile();
28208         Roo.Resizable.Handle.prototype.tpl = tpl;
28209     }
28210     this.position = pos;
28211     this.rz = rz;
28212     // show north drag fro topdra
28213     var handlepos = pos == 'hdrag' ? 'north' : pos;
28214     
28215     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28216     if (pos == 'hdrag') {
28217         this.el.setStyle('cursor', 'pointer');
28218     }
28219     this.el.unselectable();
28220     if(transparent){
28221         this.el.setOpacity(0);
28222     }
28223     this.el.on("mousedown", this.onMouseDown, this);
28224     if(!disableTrackOver){
28225         this.el.on("mouseover", this.onMouseOver, this);
28226         this.el.on("mouseout", this.onMouseOut, this);
28227     }
28228 };
28229
28230 // private
28231 Roo.Resizable.Handle.prototype = {
28232     afterResize : function(rz){
28233         // do nothing
28234     },
28235     // private
28236     onMouseDown : function(e){
28237         this.rz.onMouseDown(this, e);
28238     },
28239     // private
28240     onMouseOver : function(e){
28241         this.rz.handleOver(this, e);
28242     },
28243     // private
28244     onMouseOut : function(e){
28245         this.rz.handleOut(this, e);
28246     }
28247 };/*
28248  * Based on:
28249  * Ext JS Library 1.1.1
28250  * Copyright(c) 2006-2007, Ext JS, LLC.
28251  *
28252  * Originally Released Under LGPL - original licence link has changed is not relivant.
28253  *
28254  * Fork - LGPL
28255  * <script type="text/javascript">
28256  */
28257
28258 /**
28259  * @class Roo.Editor
28260  * @extends Roo.Component
28261  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28262  * @constructor
28263  * Create a new Editor
28264  * @param {Roo.form.Field} field The Field object (or descendant)
28265  * @param {Object} config The config object
28266  */
28267 Roo.Editor = function(field, config){
28268     Roo.Editor.superclass.constructor.call(this, config);
28269     this.field = field;
28270     this.addEvents({
28271         /**
28272              * @event beforestartedit
28273              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28274              * false from the handler of this event.
28275              * @param {Editor} this
28276              * @param {Roo.Element} boundEl The underlying element bound to this editor
28277              * @param {Mixed} value The field value being set
28278              */
28279         "beforestartedit" : true,
28280         /**
28281              * @event startedit
28282              * Fires when this editor is displayed
28283              * @param {Roo.Element} boundEl The underlying element bound to this editor
28284              * @param {Mixed} value The starting field value
28285              */
28286         "startedit" : true,
28287         /**
28288              * @event beforecomplete
28289              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28290              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28291              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28292              * event will not fire since no edit actually occurred.
28293              * @param {Editor} this
28294              * @param {Mixed} value The current field value
28295              * @param {Mixed} startValue The original field value
28296              */
28297         "beforecomplete" : true,
28298         /**
28299              * @event complete
28300              * Fires after editing is complete and any changed value has been written to the underlying field.
28301              * @param {Editor} this
28302              * @param {Mixed} value The current field value
28303              * @param {Mixed} startValue The original field value
28304              */
28305         "complete" : true,
28306         /**
28307          * @event specialkey
28308          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28309          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28310          * @param {Roo.form.Field} this
28311          * @param {Roo.EventObject} e The event object
28312          */
28313         "specialkey" : true
28314     });
28315 };
28316
28317 Roo.extend(Roo.Editor, Roo.Component, {
28318     /**
28319      * @cfg {Boolean/String} autosize
28320      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28321      * or "height" to adopt the height only (defaults to false)
28322      */
28323     /**
28324      * @cfg {Boolean} revertInvalid
28325      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28326      * validation fails (defaults to true)
28327      */
28328     /**
28329      * @cfg {Boolean} ignoreNoChange
28330      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28331      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28332      * will never be ignored.
28333      */
28334     /**
28335      * @cfg {Boolean} hideEl
28336      * False to keep the bound element visible while the editor is displayed (defaults to true)
28337      */
28338     /**
28339      * @cfg {Mixed} value
28340      * The data value of the underlying field (defaults to "")
28341      */
28342     value : "",
28343     /**
28344      * @cfg {String} alignment
28345      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28346      */
28347     alignment: "c-c?",
28348     /**
28349      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28350      * for bottom-right shadow (defaults to "frame")
28351      */
28352     shadow : "frame",
28353     /**
28354      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28355      */
28356     constrain : false,
28357     /**
28358      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28359      */
28360     completeOnEnter : false,
28361     /**
28362      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28363      */
28364     cancelOnEsc : false,
28365     /**
28366      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28367      */
28368     updateEl : false,
28369
28370     // private
28371     onRender : function(ct, position){
28372         this.el = new Roo.Layer({
28373             shadow: this.shadow,
28374             cls: "x-editor",
28375             parentEl : ct,
28376             shim : this.shim,
28377             shadowOffset:4,
28378             id: this.id,
28379             constrain: this.constrain
28380         });
28381         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28382         if(this.field.msgTarget != 'title'){
28383             this.field.msgTarget = 'qtip';
28384         }
28385         this.field.render(this.el);
28386         if(Roo.isGecko){
28387             this.field.el.dom.setAttribute('autocomplete', 'off');
28388         }
28389         this.field.on("specialkey", this.onSpecialKey, this);
28390         if(this.swallowKeys){
28391             this.field.el.swallowEvent(['keydown','keypress']);
28392         }
28393         this.field.show();
28394         this.field.on("blur", this.onBlur, this);
28395         if(this.field.grow){
28396             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28397         }
28398     },
28399
28400     onSpecialKey : function(field, e)
28401     {
28402         //Roo.log('editor onSpecialKey');
28403         if(this.completeOnEnter && e.getKey() == e.ENTER){
28404             e.stopEvent();
28405             this.completeEdit();
28406             return;
28407         }
28408         // do not fire special key otherwise it might hide close the editor...
28409         if(e.getKey() == e.ENTER){    
28410             return;
28411         }
28412         if(this.cancelOnEsc && e.getKey() == e.ESC){
28413             this.cancelEdit();
28414             return;
28415         } 
28416         this.fireEvent('specialkey', field, e);
28417     
28418     },
28419
28420     /**
28421      * Starts the editing process and shows the editor.
28422      * @param {String/HTMLElement/Element} el The element to edit
28423      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28424       * to the innerHTML of el.
28425      */
28426     startEdit : function(el, value){
28427         if(this.editing){
28428             this.completeEdit();
28429         }
28430         this.boundEl = Roo.get(el);
28431         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28432         if(!this.rendered){
28433             this.render(this.parentEl || document.body);
28434         }
28435         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28436             return;
28437         }
28438         this.startValue = v;
28439         this.field.setValue(v);
28440         if(this.autoSize){
28441             var sz = this.boundEl.getSize();
28442             switch(this.autoSize){
28443                 case "width":
28444                 this.setSize(sz.width,  "");
28445                 break;
28446                 case "height":
28447                 this.setSize("",  sz.height);
28448                 break;
28449                 default:
28450                 this.setSize(sz.width,  sz.height);
28451             }
28452         }
28453         this.el.alignTo(this.boundEl, this.alignment);
28454         this.editing = true;
28455         if(Roo.QuickTips){
28456             Roo.QuickTips.disable();
28457         }
28458         this.show();
28459     },
28460
28461     /**
28462      * Sets the height and width of this editor.
28463      * @param {Number} width The new width
28464      * @param {Number} height The new height
28465      */
28466     setSize : function(w, h){
28467         this.field.setSize(w, h);
28468         if(this.el){
28469             this.el.sync();
28470         }
28471     },
28472
28473     /**
28474      * Realigns the editor to the bound field based on the current alignment config value.
28475      */
28476     realign : function(){
28477         this.el.alignTo(this.boundEl, this.alignment);
28478     },
28479
28480     /**
28481      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28482      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28483      */
28484     completeEdit : function(remainVisible){
28485         if(!this.editing){
28486             return;
28487         }
28488         var v = this.getValue();
28489         if(this.revertInvalid !== false && !this.field.isValid()){
28490             v = this.startValue;
28491             this.cancelEdit(true);
28492         }
28493         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28494             this.editing = false;
28495             this.hide();
28496             return;
28497         }
28498         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28499             this.editing = false;
28500             if(this.updateEl && this.boundEl){
28501                 this.boundEl.update(v);
28502             }
28503             if(remainVisible !== true){
28504                 this.hide();
28505             }
28506             this.fireEvent("complete", this, v, this.startValue);
28507         }
28508     },
28509
28510     // private
28511     onShow : function(){
28512         this.el.show();
28513         if(this.hideEl !== false){
28514             this.boundEl.hide();
28515         }
28516         this.field.show();
28517         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28518             this.fixIEFocus = true;
28519             this.deferredFocus.defer(50, this);
28520         }else{
28521             this.field.focus();
28522         }
28523         this.fireEvent("startedit", this.boundEl, this.startValue);
28524     },
28525
28526     deferredFocus : function(){
28527         if(this.editing){
28528             this.field.focus();
28529         }
28530     },
28531
28532     /**
28533      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28534      * reverted to the original starting value.
28535      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28536      * cancel (defaults to false)
28537      */
28538     cancelEdit : function(remainVisible){
28539         if(this.editing){
28540             this.setValue(this.startValue);
28541             if(remainVisible !== true){
28542                 this.hide();
28543             }
28544         }
28545     },
28546
28547     // private
28548     onBlur : function(){
28549         if(this.allowBlur !== true && this.editing){
28550             this.completeEdit();
28551         }
28552     },
28553
28554     // private
28555     onHide : function(){
28556         if(this.editing){
28557             this.completeEdit();
28558             return;
28559         }
28560         this.field.blur();
28561         if(this.field.collapse){
28562             this.field.collapse();
28563         }
28564         this.el.hide();
28565         if(this.hideEl !== false){
28566             this.boundEl.show();
28567         }
28568         if(Roo.QuickTips){
28569             Roo.QuickTips.enable();
28570         }
28571     },
28572
28573     /**
28574      * Sets the data value of the editor
28575      * @param {Mixed} value Any valid value supported by the underlying field
28576      */
28577     setValue : function(v){
28578         this.field.setValue(v);
28579     },
28580
28581     /**
28582      * Gets the data value of the editor
28583      * @return {Mixed} The data value
28584      */
28585     getValue : function(){
28586         return this.field.getValue();
28587     }
28588 });/*
28589  * Based on:
28590  * Ext JS Library 1.1.1
28591  * Copyright(c) 2006-2007, Ext JS, LLC.
28592  *
28593  * Originally Released Under LGPL - original licence link has changed is not relivant.
28594  *
28595  * Fork - LGPL
28596  * <script type="text/javascript">
28597  */
28598  
28599 /**
28600  * @class Roo.BasicDialog
28601  * @extends Roo.util.Observable
28602  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28603  * <pre><code>
28604 var dlg = new Roo.BasicDialog("my-dlg", {
28605     height: 200,
28606     width: 300,
28607     minHeight: 100,
28608     minWidth: 150,
28609     modal: true,
28610     proxyDrag: true,
28611     shadow: true
28612 });
28613 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28614 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28615 dlg.addButton('Cancel', dlg.hide, dlg);
28616 dlg.show();
28617 </code></pre>
28618   <b>A Dialog should always be a direct child of the body element.</b>
28619  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28620  * @cfg {String} title Default text to display in the title bar (defaults to null)
28621  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28622  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28623  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28624  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28625  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28626  * (defaults to null with no animation)
28627  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28628  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28629  * property for valid values (defaults to 'all')
28630  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28631  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28632  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28633  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28634  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28635  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28636  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28637  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28638  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28639  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28640  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28641  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28642  * draggable = true (defaults to false)
28643  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28644  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28645  * shadow (defaults to false)
28646  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28647  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28648  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28649  * @cfg {Array} buttons Array of buttons
28650  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28651  * @constructor
28652  * Create a new BasicDialog.
28653  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28654  * @param {Object} config Configuration options
28655  */
28656 Roo.BasicDialog = function(el, config){
28657     this.el = Roo.get(el);
28658     var dh = Roo.DomHelper;
28659     if(!this.el && config && config.autoCreate){
28660         if(typeof config.autoCreate == "object"){
28661             if(!config.autoCreate.id){
28662                 config.autoCreate.id = el;
28663             }
28664             this.el = dh.append(document.body,
28665                         config.autoCreate, true);
28666         }else{
28667             this.el = dh.append(document.body,
28668                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28669         }
28670     }
28671     el = this.el;
28672     el.setDisplayed(true);
28673     el.hide = this.hideAction;
28674     this.id = el.id;
28675     el.addClass("x-dlg");
28676
28677     Roo.apply(this, config);
28678
28679     this.proxy = el.createProxy("x-dlg-proxy");
28680     this.proxy.hide = this.hideAction;
28681     this.proxy.setOpacity(.5);
28682     this.proxy.hide();
28683
28684     if(config.width){
28685         el.setWidth(config.width);
28686     }
28687     if(config.height){
28688         el.setHeight(config.height);
28689     }
28690     this.size = el.getSize();
28691     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28692         this.xy = [config.x,config.y];
28693     }else{
28694         this.xy = el.getCenterXY(true);
28695     }
28696     /** The header element @type Roo.Element */
28697     this.header = el.child("> .x-dlg-hd");
28698     /** The body element @type Roo.Element */
28699     this.body = el.child("> .x-dlg-bd");
28700     /** The footer element @type Roo.Element */
28701     this.footer = el.child("> .x-dlg-ft");
28702
28703     if(!this.header){
28704         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28705     }
28706     if(!this.body){
28707         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28708     }
28709
28710     this.header.unselectable();
28711     if(this.title){
28712         this.header.update(this.title);
28713     }
28714     // this element allows the dialog to be focused for keyboard event
28715     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28716     this.focusEl.swallowEvent("click", true);
28717
28718     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28719
28720     // wrap the body and footer for special rendering
28721     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28722     if(this.footer){
28723         this.bwrap.dom.appendChild(this.footer.dom);
28724     }
28725
28726     this.bg = this.el.createChild({
28727         tag: "div", cls:"x-dlg-bg",
28728         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28729     });
28730     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28731
28732
28733     if(this.autoScroll !== false && !this.autoTabs){
28734         this.body.setStyle("overflow", "auto");
28735     }
28736
28737     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28738
28739     if(this.closable !== false){
28740         this.el.addClass("x-dlg-closable");
28741         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28742         this.close.on("click", this.closeClick, this);
28743         this.close.addClassOnOver("x-dlg-close-over");
28744     }
28745     if(this.collapsible !== false){
28746         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28747         this.collapseBtn.on("click", this.collapseClick, this);
28748         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28749         this.header.on("dblclick", this.collapseClick, this);
28750     }
28751     if(this.resizable !== false){
28752         this.el.addClass("x-dlg-resizable");
28753         this.resizer = new Roo.Resizable(el, {
28754             minWidth: this.minWidth || 80,
28755             minHeight:this.minHeight || 80,
28756             handles: this.resizeHandles || "all",
28757             pinned: true
28758         });
28759         this.resizer.on("beforeresize", this.beforeResize, this);
28760         this.resizer.on("resize", this.onResize, this);
28761     }
28762     if(this.draggable !== false){
28763         el.addClass("x-dlg-draggable");
28764         if (!this.proxyDrag) {
28765             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28766         }
28767         else {
28768             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28769         }
28770         dd.setHandleElId(this.header.id);
28771         dd.endDrag = this.endMove.createDelegate(this);
28772         dd.startDrag = this.startMove.createDelegate(this);
28773         dd.onDrag = this.onDrag.createDelegate(this);
28774         dd.scroll = false;
28775         this.dd = dd;
28776     }
28777     if(this.modal){
28778         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28779         this.mask.enableDisplayMode("block");
28780         this.mask.hide();
28781         this.el.addClass("x-dlg-modal");
28782     }
28783     if(this.shadow){
28784         this.shadow = new Roo.Shadow({
28785             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28786             offset : this.shadowOffset
28787         });
28788     }else{
28789         this.shadowOffset = 0;
28790     }
28791     if(Roo.useShims && this.shim !== false){
28792         this.shim = this.el.createShim();
28793         this.shim.hide = this.hideAction;
28794         this.shim.hide();
28795     }else{
28796         this.shim = false;
28797     }
28798     if(this.autoTabs){
28799         this.initTabs();
28800     }
28801     if (this.buttons) { 
28802         var bts= this.buttons;
28803         this.buttons = [];
28804         Roo.each(bts, function(b) {
28805             this.addButton(b);
28806         }, this);
28807     }
28808     
28809     
28810     this.addEvents({
28811         /**
28812          * @event keydown
28813          * Fires when a key is pressed
28814          * @param {Roo.BasicDialog} this
28815          * @param {Roo.EventObject} e
28816          */
28817         "keydown" : true,
28818         /**
28819          * @event move
28820          * Fires when this dialog is moved by the user.
28821          * @param {Roo.BasicDialog} this
28822          * @param {Number} x The new page X
28823          * @param {Number} y The new page Y
28824          */
28825         "move" : true,
28826         /**
28827          * @event resize
28828          * Fires when this dialog is resized by the user.
28829          * @param {Roo.BasicDialog} this
28830          * @param {Number} width The new width
28831          * @param {Number} height The new height
28832          */
28833         "resize" : true,
28834         /**
28835          * @event beforehide
28836          * Fires before this dialog is hidden.
28837          * @param {Roo.BasicDialog} this
28838          */
28839         "beforehide" : true,
28840         /**
28841          * @event hide
28842          * Fires when this dialog is hidden.
28843          * @param {Roo.BasicDialog} this
28844          */
28845         "hide" : true,
28846         /**
28847          * @event beforeshow
28848          * Fires before this dialog is shown.
28849          * @param {Roo.BasicDialog} this
28850          */
28851         "beforeshow" : true,
28852         /**
28853          * @event show
28854          * Fires when this dialog is shown.
28855          * @param {Roo.BasicDialog} this
28856          */
28857         "show" : true
28858     });
28859     el.on("keydown", this.onKeyDown, this);
28860     el.on("mousedown", this.toFront, this);
28861     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28862     this.el.hide();
28863     Roo.DialogManager.register(this);
28864     Roo.BasicDialog.superclass.constructor.call(this);
28865 };
28866
28867 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28868     shadowOffset: Roo.isIE ? 6 : 5,
28869     minHeight: 80,
28870     minWidth: 200,
28871     minButtonWidth: 75,
28872     defaultButton: null,
28873     buttonAlign: "right",
28874     tabTag: 'div',
28875     firstShow: true,
28876
28877     /**
28878      * Sets the dialog title text
28879      * @param {String} text The title text to display
28880      * @return {Roo.BasicDialog} this
28881      */
28882     setTitle : function(text){
28883         this.header.update(text);
28884         return this;
28885     },
28886
28887     // private
28888     closeClick : function(){
28889         this.hide();
28890     },
28891
28892     // private
28893     collapseClick : function(){
28894         this[this.collapsed ? "expand" : "collapse"]();
28895     },
28896
28897     /**
28898      * Collapses the dialog to its minimized state (only the title bar is visible).
28899      * Equivalent to the user clicking the collapse dialog button.
28900      */
28901     collapse : function(){
28902         if(!this.collapsed){
28903             this.collapsed = true;
28904             this.el.addClass("x-dlg-collapsed");
28905             this.restoreHeight = this.el.getHeight();
28906             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28907         }
28908     },
28909
28910     /**
28911      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28912      * clicking the expand dialog button.
28913      */
28914     expand : function(){
28915         if(this.collapsed){
28916             this.collapsed = false;
28917             this.el.removeClass("x-dlg-collapsed");
28918             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28919         }
28920     },
28921
28922     /**
28923      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28924      * @return {Roo.TabPanel} The tabs component
28925      */
28926     initTabs : function(){
28927         var tabs = this.getTabs();
28928         while(tabs.getTab(0)){
28929             tabs.removeTab(0);
28930         }
28931         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28932             var dom = el.dom;
28933             tabs.addTab(Roo.id(dom), dom.title);
28934             dom.title = "";
28935         });
28936         tabs.activate(0);
28937         return tabs;
28938     },
28939
28940     // private
28941     beforeResize : function(){
28942         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28943     },
28944
28945     // private
28946     onResize : function(){
28947         this.refreshSize();
28948         this.syncBodyHeight();
28949         this.adjustAssets();
28950         this.focus();
28951         this.fireEvent("resize", this, this.size.width, this.size.height);
28952     },
28953
28954     // private
28955     onKeyDown : function(e){
28956         if(this.isVisible()){
28957             this.fireEvent("keydown", this, e);
28958         }
28959     },
28960
28961     /**
28962      * Resizes the dialog.
28963      * @param {Number} width
28964      * @param {Number} height
28965      * @return {Roo.BasicDialog} this
28966      */
28967     resizeTo : function(width, height){
28968         this.el.setSize(width, height);
28969         this.size = {width: width, height: height};
28970         this.syncBodyHeight();
28971         if(this.fixedcenter){
28972             this.center();
28973         }
28974         if(this.isVisible()){
28975             this.constrainXY();
28976             this.adjustAssets();
28977         }
28978         this.fireEvent("resize", this, width, height);
28979         return this;
28980     },
28981
28982
28983     /**
28984      * Resizes the dialog to fit the specified content size.
28985      * @param {Number} width
28986      * @param {Number} height
28987      * @return {Roo.BasicDialog} this
28988      */
28989     setContentSize : function(w, h){
28990         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28991         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28992         //if(!this.el.isBorderBox()){
28993             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28994             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28995         //}
28996         if(this.tabs){
28997             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28998             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28999         }
29000         this.resizeTo(w, h);
29001         return this;
29002     },
29003
29004     /**
29005      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29006      * executed in response to a particular key being pressed while the dialog is active.
29007      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29008      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29009      * @param {Function} fn The function to call
29010      * @param {Object} scope (optional) The scope of the function
29011      * @return {Roo.BasicDialog} this
29012      */
29013     addKeyListener : function(key, fn, scope){
29014         var keyCode, shift, ctrl, alt;
29015         if(typeof key == "object" && !(key instanceof Array)){
29016             keyCode = key["key"];
29017             shift = key["shift"];
29018             ctrl = key["ctrl"];
29019             alt = key["alt"];
29020         }else{
29021             keyCode = key;
29022         }
29023         var handler = function(dlg, e){
29024             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29025                 var k = e.getKey();
29026                 if(keyCode instanceof Array){
29027                     for(var i = 0, len = keyCode.length; i < len; i++){
29028                         if(keyCode[i] == k){
29029                           fn.call(scope || window, dlg, k, e);
29030                           return;
29031                         }
29032                     }
29033                 }else{
29034                     if(k == keyCode){
29035                         fn.call(scope || window, dlg, k, e);
29036                     }
29037                 }
29038             }
29039         };
29040         this.on("keydown", handler);
29041         return this;
29042     },
29043
29044     /**
29045      * Returns the TabPanel component (creates it if it doesn't exist).
29046      * Note: If you wish to simply check for the existence of tabs without creating them,
29047      * check for a null 'tabs' property.
29048      * @return {Roo.TabPanel} The tabs component
29049      */
29050     getTabs : function(){
29051         if(!this.tabs){
29052             this.el.addClass("x-dlg-auto-tabs");
29053             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29054             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29055         }
29056         return this.tabs;
29057     },
29058
29059     /**
29060      * Adds a button to the footer section of the dialog.
29061      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29062      * object or a valid Roo.DomHelper element config
29063      * @param {Function} handler The function called when the button is clicked
29064      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29065      * @return {Roo.Button} The new button
29066      */
29067     addButton : function(config, handler, scope){
29068         var dh = Roo.DomHelper;
29069         if(!this.footer){
29070             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29071         }
29072         if(!this.btnContainer){
29073             var tb = this.footer.createChild({
29074
29075                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29076                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29077             }, null, true);
29078             this.btnContainer = tb.firstChild.firstChild.firstChild;
29079         }
29080         var bconfig = {
29081             handler: handler,
29082             scope: scope,
29083             minWidth: this.minButtonWidth,
29084             hideParent:true
29085         };
29086         if(typeof config == "string"){
29087             bconfig.text = config;
29088         }else{
29089             if(config.tag){
29090                 bconfig.dhconfig = config;
29091             }else{
29092                 Roo.apply(bconfig, config);
29093             }
29094         }
29095         var fc = false;
29096         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29097             bconfig.position = Math.max(0, bconfig.position);
29098             fc = this.btnContainer.childNodes[bconfig.position];
29099         }
29100          
29101         var btn = new Roo.Button(
29102             fc ? 
29103                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29104                 : this.btnContainer.appendChild(document.createElement("td")),
29105             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29106             bconfig
29107         );
29108         this.syncBodyHeight();
29109         if(!this.buttons){
29110             /**
29111              * Array of all the buttons that have been added to this dialog via addButton
29112              * @type Array
29113              */
29114             this.buttons = [];
29115         }
29116         this.buttons.push(btn);
29117         return btn;
29118     },
29119
29120     /**
29121      * Sets the default button to be focused when the dialog is displayed.
29122      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29123      * @return {Roo.BasicDialog} this
29124      */
29125     setDefaultButton : function(btn){
29126         this.defaultButton = btn;
29127         return this;
29128     },
29129
29130     // private
29131     getHeaderFooterHeight : function(safe){
29132         var height = 0;
29133         if(this.header){
29134            height += this.header.getHeight();
29135         }
29136         if(this.footer){
29137            var fm = this.footer.getMargins();
29138             height += (this.footer.getHeight()+fm.top+fm.bottom);
29139         }
29140         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29141         height += this.centerBg.getPadding("tb");
29142         return height;
29143     },
29144
29145     // private
29146     syncBodyHeight : function(){
29147         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29148         var height = this.size.height - this.getHeaderFooterHeight(false);
29149         bd.setHeight(height-bd.getMargins("tb"));
29150         var hh = this.header.getHeight();
29151         var h = this.size.height-hh;
29152         cb.setHeight(h);
29153         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29154         bw.setHeight(h-cb.getPadding("tb"));
29155         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29156         bd.setWidth(bw.getWidth(true));
29157         if(this.tabs){
29158             this.tabs.syncHeight();
29159             if(Roo.isIE){
29160                 this.tabs.el.repaint();
29161             }
29162         }
29163     },
29164
29165     /**
29166      * Restores the previous state of the dialog if Roo.state is configured.
29167      * @return {Roo.BasicDialog} this
29168      */
29169     restoreState : function(){
29170         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29171         if(box && box.width){
29172             this.xy = [box.x, box.y];
29173             this.resizeTo(box.width, box.height);
29174         }
29175         return this;
29176     },
29177
29178     // private
29179     beforeShow : function(){
29180         this.expand();
29181         if(this.fixedcenter){
29182             this.xy = this.el.getCenterXY(true);
29183         }
29184         if(this.modal){
29185             Roo.get(document.body).addClass("x-body-masked");
29186             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29187             this.mask.show();
29188         }
29189         this.constrainXY();
29190     },
29191
29192     // private
29193     animShow : function(){
29194         var b = Roo.get(this.animateTarget).getBox();
29195         this.proxy.setSize(b.width, b.height);
29196         this.proxy.setLocation(b.x, b.y);
29197         this.proxy.show();
29198         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29199                     true, .35, this.showEl.createDelegate(this));
29200     },
29201
29202     /**
29203      * Shows the dialog.
29204      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29205      * @return {Roo.BasicDialog} this
29206      */
29207     show : function(animateTarget){
29208         if (this.fireEvent("beforeshow", this) === false){
29209             return;
29210         }
29211         if(this.syncHeightBeforeShow){
29212             this.syncBodyHeight();
29213         }else if(this.firstShow){
29214             this.firstShow = false;
29215             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29216         }
29217         this.animateTarget = animateTarget || this.animateTarget;
29218         if(!this.el.isVisible()){
29219             this.beforeShow();
29220             if(this.animateTarget && Roo.get(this.animateTarget)){
29221                 this.animShow();
29222             }else{
29223                 this.showEl();
29224             }
29225         }
29226         return this;
29227     },
29228
29229     // private
29230     showEl : function(){
29231         this.proxy.hide();
29232         this.el.setXY(this.xy);
29233         this.el.show();
29234         this.adjustAssets(true);
29235         this.toFront();
29236         this.focus();
29237         // IE peekaboo bug - fix found by Dave Fenwick
29238         if(Roo.isIE){
29239             this.el.repaint();
29240         }
29241         this.fireEvent("show", this);
29242     },
29243
29244     /**
29245      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29246      * dialog itself will receive focus.
29247      */
29248     focus : function(){
29249         if(this.defaultButton){
29250             this.defaultButton.focus();
29251         }else{
29252             this.focusEl.focus();
29253         }
29254     },
29255
29256     // private
29257     constrainXY : function(){
29258         if(this.constraintoviewport !== false){
29259             if(!this.viewSize){
29260                 if(this.container){
29261                     var s = this.container.getSize();
29262                     this.viewSize = [s.width, s.height];
29263                 }else{
29264                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29265                 }
29266             }
29267             var s = Roo.get(this.container||document).getScroll();
29268
29269             var x = this.xy[0], y = this.xy[1];
29270             var w = this.size.width, h = this.size.height;
29271             var vw = this.viewSize[0], vh = this.viewSize[1];
29272             // only move it if it needs it
29273             var moved = false;
29274             // first validate right/bottom
29275             if(x + w > vw+s.left){
29276                 x = vw - w;
29277                 moved = true;
29278             }
29279             if(y + h > vh+s.top){
29280                 y = vh - h;
29281                 moved = true;
29282             }
29283             // then make sure top/left isn't negative
29284             if(x < s.left){
29285                 x = s.left;
29286                 moved = true;
29287             }
29288             if(y < s.top){
29289                 y = s.top;
29290                 moved = true;
29291             }
29292             if(moved){
29293                 // cache xy
29294                 this.xy = [x, y];
29295                 if(this.isVisible()){
29296                     this.el.setLocation(x, y);
29297                     this.adjustAssets();
29298                 }
29299             }
29300         }
29301     },
29302
29303     // private
29304     onDrag : function(){
29305         if(!this.proxyDrag){
29306             this.xy = this.el.getXY();
29307             this.adjustAssets();
29308         }
29309     },
29310
29311     // private
29312     adjustAssets : function(doShow){
29313         var x = this.xy[0], y = this.xy[1];
29314         var w = this.size.width, h = this.size.height;
29315         if(doShow === true){
29316             if(this.shadow){
29317                 this.shadow.show(this.el);
29318             }
29319             if(this.shim){
29320                 this.shim.show();
29321             }
29322         }
29323         if(this.shadow && this.shadow.isVisible()){
29324             this.shadow.show(this.el);
29325         }
29326         if(this.shim && this.shim.isVisible()){
29327             this.shim.setBounds(x, y, w, h);
29328         }
29329     },
29330
29331     // private
29332     adjustViewport : function(w, h){
29333         if(!w || !h){
29334             w = Roo.lib.Dom.getViewWidth();
29335             h = Roo.lib.Dom.getViewHeight();
29336         }
29337         // cache the size
29338         this.viewSize = [w, h];
29339         if(this.modal && this.mask.isVisible()){
29340             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29341             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29342         }
29343         if(this.isVisible()){
29344             this.constrainXY();
29345         }
29346     },
29347
29348     /**
29349      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29350      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29351      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29352      */
29353     destroy : function(removeEl){
29354         if(this.isVisible()){
29355             this.animateTarget = null;
29356             this.hide();
29357         }
29358         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29359         if(this.tabs){
29360             this.tabs.destroy(removeEl);
29361         }
29362         Roo.destroy(
29363              this.shim,
29364              this.proxy,
29365              this.resizer,
29366              this.close,
29367              this.mask
29368         );
29369         if(this.dd){
29370             this.dd.unreg();
29371         }
29372         if(this.buttons){
29373            for(var i = 0, len = this.buttons.length; i < len; i++){
29374                this.buttons[i].destroy();
29375            }
29376         }
29377         this.el.removeAllListeners();
29378         if(removeEl === true){
29379             this.el.update("");
29380             this.el.remove();
29381         }
29382         Roo.DialogManager.unregister(this);
29383     },
29384
29385     // private
29386     startMove : function(){
29387         if(this.proxyDrag){
29388             this.proxy.show();
29389         }
29390         if(this.constraintoviewport !== false){
29391             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29392         }
29393     },
29394
29395     // private
29396     endMove : function(){
29397         if(!this.proxyDrag){
29398             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29399         }else{
29400             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29401             this.proxy.hide();
29402         }
29403         this.refreshSize();
29404         this.adjustAssets();
29405         this.focus();
29406         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29407     },
29408
29409     /**
29410      * Brings this dialog to the front of any other visible dialogs
29411      * @return {Roo.BasicDialog} this
29412      */
29413     toFront : function(){
29414         Roo.DialogManager.bringToFront(this);
29415         return this;
29416     },
29417
29418     /**
29419      * Sends this dialog to the back (under) of any other visible dialogs
29420      * @return {Roo.BasicDialog} this
29421      */
29422     toBack : function(){
29423         Roo.DialogManager.sendToBack(this);
29424         return this;
29425     },
29426
29427     /**
29428      * Centers this dialog in the viewport
29429      * @return {Roo.BasicDialog} this
29430      */
29431     center : function(){
29432         var xy = this.el.getCenterXY(true);
29433         this.moveTo(xy[0], xy[1]);
29434         return this;
29435     },
29436
29437     /**
29438      * Moves the dialog's top-left corner to the specified point
29439      * @param {Number} x
29440      * @param {Number} y
29441      * @return {Roo.BasicDialog} this
29442      */
29443     moveTo : function(x, y){
29444         this.xy = [x,y];
29445         if(this.isVisible()){
29446             this.el.setXY(this.xy);
29447             this.adjustAssets();
29448         }
29449         return this;
29450     },
29451
29452     /**
29453      * Aligns the dialog to the specified element
29454      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29455      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29456      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29457      * @return {Roo.BasicDialog} this
29458      */
29459     alignTo : function(element, position, offsets){
29460         this.xy = this.el.getAlignToXY(element, position, offsets);
29461         if(this.isVisible()){
29462             this.el.setXY(this.xy);
29463             this.adjustAssets();
29464         }
29465         return this;
29466     },
29467
29468     /**
29469      * Anchors an element to another element and realigns it when the window is resized.
29470      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29471      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29472      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29473      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29474      * is a number, it is used as the buffer delay (defaults to 50ms).
29475      * @return {Roo.BasicDialog} this
29476      */
29477     anchorTo : function(el, alignment, offsets, monitorScroll){
29478         var action = function(){
29479             this.alignTo(el, alignment, offsets);
29480         };
29481         Roo.EventManager.onWindowResize(action, this);
29482         var tm = typeof monitorScroll;
29483         if(tm != 'undefined'){
29484             Roo.EventManager.on(window, 'scroll', action, this,
29485                 {buffer: tm == 'number' ? monitorScroll : 50});
29486         }
29487         action.call(this);
29488         return this;
29489     },
29490
29491     /**
29492      * Returns true if the dialog is visible
29493      * @return {Boolean}
29494      */
29495     isVisible : function(){
29496         return this.el.isVisible();
29497     },
29498
29499     // private
29500     animHide : function(callback){
29501         var b = Roo.get(this.animateTarget).getBox();
29502         this.proxy.show();
29503         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29504         this.el.hide();
29505         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29506                     this.hideEl.createDelegate(this, [callback]));
29507     },
29508
29509     /**
29510      * Hides the dialog.
29511      * @param {Function} callback (optional) Function to call when the dialog is hidden
29512      * @return {Roo.BasicDialog} this
29513      */
29514     hide : function(callback){
29515         if (this.fireEvent("beforehide", this) === false){
29516             return;
29517         }
29518         if(this.shadow){
29519             this.shadow.hide();
29520         }
29521         if(this.shim) {
29522           this.shim.hide();
29523         }
29524         // sometimes animateTarget seems to get set.. causing problems...
29525         // this just double checks..
29526         if(this.animateTarget && Roo.get(this.animateTarget)) {
29527            this.animHide(callback);
29528         }else{
29529             this.el.hide();
29530             this.hideEl(callback);
29531         }
29532         return this;
29533     },
29534
29535     // private
29536     hideEl : function(callback){
29537         this.proxy.hide();
29538         if(this.modal){
29539             this.mask.hide();
29540             Roo.get(document.body).removeClass("x-body-masked");
29541         }
29542         this.fireEvent("hide", this);
29543         if(typeof callback == "function"){
29544             callback();
29545         }
29546     },
29547
29548     // private
29549     hideAction : function(){
29550         this.setLeft("-10000px");
29551         this.setTop("-10000px");
29552         this.setStyle("visibility", "hidden");
29553     },
29554
29555     // private
29556     refreshSize : function(){
29557         this.size = this.el.getSize();
29558         this.xy = this.el.getXY();
29559         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29560     },
29561
29562     // private
29563     // z-index is managed by the DialogManager and may be overwritten at any time
29564     setZIndex : function(index){
29565         if(this.modal){
29566             this.mask.setStyle("z-index", index);
29567         }
29568         if(this.shim){
29569             this.shim.setStyle("z-index", ++index);
29570         }
29571         if(this.shadow){
29572             this.shadow.setZIndex(++index);
29573         }
29574         this.el.setStyle("z-index", ++index);
29575         if(this.proxy){
29576             this.proxy.setStyle("z-index", ++index);
29577         }
29578         if(this.resizer){
29579             this.resizer.proxy.setStyle("z-index", ++index);
29580         }
29581
29582         this.lastZIndex = index;
29583     },
29584
29585     /**
29586      * Returns the element for this dialog
29587      * @return {Roo.Element} The underlying dialog Element
29588      */
29589     getEl : function(){
29590         return this.el;
29591     }
29592 });
29593
29594 /**
29595  * @class Roo.DialogManager
29596  * Provides global access to BasicDialogs that have been created and
29597  * support for z-indexing (layering) multiple open dialogs.
29598  */
29599 Roo.DialogManager = function(){
29600     var list = {};
29601     var accessList = [];
29602     var front = null;
29603
29604     // private
29605     var sortDialogs = function(d1, d2){
29606         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29607     };
29608
29609     // private
29610     var orderDialogs = function(){
29611         accessList.sort(sortDialogs);
29612         var seed = Roo.DialogManager.zseed;
29613         for(var i = 0, len = accessList.length; i < len; i++){
29614             var dlg = accessList[i];
29615             if(dlg){
29616                 dlg.setZIndex(seed + (i*10));
29617             }
29618         }
29619     };
29620
29621     return {
29622         /**
29623          * The starting z-index for BasicDialogs (defaults to 9000)
29624          * @type Number The z-index value
29625          */
29626         zseed : 9000,
29627
29628         // private
29629         register : function(dlg){
29630             list[dlg.id] = dlg;
29631             accessList.push(dlg);
29632         },
29633
29634         // private
29635         unregister : function(dlg){
29636             delete list[dlg.id];
29637             var i=0;
29638             var len=0;
29639             if(!accessList.indexOf){
29640                 for(  i = 0, len = accessList.length; i < len; i++){
29641                     if(accessList[i] == dlg){
29642                         accessList.splice(i, 1);
29643                         return;
29644                     }
29645                 }
29646             }else{
29647                  i = accessList.indexOf(dlg);
29648                 if(i != -1){
29649                     accessList.splice(i, 1);
29650                 }
29651             }
29652         },
29653
29654         /**
29655          * Gets a registered dialog by id
29656          * @param {String/Object} id The id of the dialog or a dialog
29657          * @return {Roo.BasicDialog} this
29658          */
29659         get : function(id){
29660             return typeof id == "object" ? id : list[id];
29661         },
29662
29663         /**
29664          * Brings the specified dialog to the front
29665          * @param {String/Object} dlg The id of the dialog or a dialog
29666          * @return {Roo.BasicDialog} this
29667          */
29668         bringToFront : function(dlg){
29669             dlg = this.get(dlg);
29670             if(dlg != front){
29671                 front = dlg;
29672                 dlg._lastAccess = new Date().getTime();
29673                 orderDialogs();
29674             }
29675             return dlg;
29676         },
29677
29678         /**
29679          * Sends the specified dialog to the back
29680          * @param {String/Object} dlg The id of the dialog or a dialog
29681          * @return {Roo.BasicDialog} this
29682          */
29683         sendToBack : function(dlg){
29684             dlg = this.get(dlg);
29685             dlg._lastAccess = -(new Date().getTime());
29686             orderDialogs();
29687             return dlg;
29688         },
29689
29690         /**
29691          * Hides all dialogs
29692          */
29693         hideAll : function(){
29694             for(var id in list){
29695                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29696                     list[id].hide();
29697                 }
29698             }
29699         }
29700     };
29701 }();
29702
29703 /**
29704  * @class Roo.LayoutDialog
29705  * @extends Roo.BasicDialog
29706  * Dialog which provides adjustments for working with a layout in a Dialog.
29707  * Add your necessary layout config options to the dialog's config.<br>
29708  * Example usage (including a nested layout):
29709  * <pre><code>
29710 if(!dialog){
29711     dialog = new Roo.LayoutDialog("download-dlg", {
29712         modal: true,
29713         width:600,
29714         height:450,
29715         shadow:true,
29716         minWidth:500,
29717         minHeight:350,
29718         autoTabs:true,
29719         proxyDrag:true,
29720         // layout config merges with the dialog config
29721         center:{
29722             tabPosition: "top",
29723             alwaysShowTabs: true
29724         }
29725     });
29726     dialog.addKeyListener(27, dialog.hide, dialog);
29727     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29728     dialog.addButton("Build It!", this.getDownload, this);
29729
29730     // we can even add nested layouts
29731     var innerLayout = new Roo.BorderLayout("dl-inner", {
29732         east: {
29733             initialSize: 200,
29734             autoScroll:true,
29735             split:true
29736         },
29737         center: {
29738             autoScroll:true
29739         }
29740     });
29741     innerLayout.beginUpdate();
29742     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29743     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29744     innerLayout.endUpdate(true);
29745
29746     var layout = dialog.getLayout();
29747     layout.beginUpdate();
29748     layout.add("center", new Roo.ContentPanel("standard-panel",
29749                         {title: "Download the Source", fitToFrame:true}));
29750     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29751                {title: "Build your own roo.js"}));
29752     layout.getRegion("center").showPanel(sp);
29753     layout.endUpdate();
29754 }
29755 </code></pre>
29756     * @constructor
29757     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29758     * @param {Object} config configuration options
29759   */
29760 Roo.LayoutDialog = function(el, cfg){
29761     
29762     var config=  cfg;
29763     if (typeof(cfg) == 'undefined') {
29764         config = Roo.apply({}, el);
29765         // not sure why we use documentElement here.. - it should always be body.
29766         // IE7 borks horribly if we use documentElement.
29767         // webkit also does not like documentElement - it creates a body element...
29768         el = Roo.get( document.body || document.documentElement ).createChild();
29769         //config.autoCreate = true;
29770     }
29771     
29772     
29773     config.autoTabs = false;
29774     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29775     this.body.setStyle({overflow:"hidden", position:"relative"});
29776     this.layout = new Roo.BorderLayout(this.body.dom, config);
29777     this.layout.monitorWindowResize = false;
29778     this.el.addClass("x-dlg-auto-layout");
29779     // fix case when center region overwrites center function
29780     this.center = Roo.BasicDialog.prototype.center;
29781     this.on("show", this.layout.layout, this.layout, true);
29782     if (config.items) {
29783         var xitems = config.items;
29784         delete config.items;
29785         Roo.each(xitems, this.addxtype, this);
29786     }
29787     
29788     
29789 };
29790 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29791     /**
29792      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29793      * @deprecated
29794      */
29795     endUpdate : function(){
29796         this.layout.endUpdate();
29797     },
29798
29799     /**
29800      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29801      *  @deprecated
29802      */
29803     beginUpdate : function(){
29804         this.layout.beginUpdate();
29805     },
29806
29807     /**
29808      * Get the BorderLayout for this dialog
29809      * @return {Roo.BorderLayout}
29810      */
29811     getLayout : function(){
29812         return this.layout;
29813     },
29814
29815     showEl : function(){
29816         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29817         if(Roo.isIE7){
29818             this.layout.layout();
29819         }
29820     },
29821
29822     // private
29823     // Use the syncHeightBeforeShow config option to control this automatically
29824     syncBodyHeight : function(){
29825         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29826         if(this.layout){this.layout.layout();}
29827     },
29828     
29829       /**
29830      * Add an xtype element (actually adds to the layout.)
29831      * @return {Object} xdata xtype object data.
29832      */
29833     
29834     addxtype : function(c) {
29835         return this.layout.addxtype(c);
29836     }
29837 });/*
29838  * Based on:
29839  * Ext JS Library 1.1.1
29840  * Copyright(c) 2006-2007, Ext JS, LLC.
29841  *
29842  * Originally Released Under LGPL - original licence link has changed is not relivant.
29843  *
29844  * Fork - LGPL
29845  * <script type="text/javascript">
29846  */
29847  
29848 /**
29849  * @class Roo.MessageBox
29850  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29851  * Example usage:
29852  *<pre><code>
29853 // Basic alert:
29854 Roo.Msg.alert('Status', 'Changes saved successfully.');
29855
29856 // Prompt for user data:
29857 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29858     if (btn == 'ok'){
29859         // process text value...
29860     }
29861 });
29862
29863 // Show a dialog using config options:
29864 Roo.Msg.show({
29865    title:'Save Changes?',
29866    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29867    buttons: Roo.Msg.YESNOCANCEL,
29868    fn: processResult,
29869    animEl: 'elId'
29870 });
29871 </code></pre>
29872  * @singleton
29873  */
29874 Roo.MessageBox = function(){
29875     var dlg, opt, mask, waitTimer;
29876     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29877     var buttons, activeTextEl, bwidth;
29878
29879     // private
29880     var handleButton = function(button){
29881         dlg.hide();
29882         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29883     };
29884
29885     // private
29886     var handleHide = function(){
29887         if(opt && opt.cls){
29888             dlg.el.removeClass(opt.cls);
29889         }
29890         if(waitTimer){
29891             Roo.TaskMgr.stop(waitTimer);
29892             waitTimer = null;
29893         }
29894     };
29895
29896     // private
29897     var updateButtons = function(b){
29898         var width = 0;
29899         if(!b){
29900             buttons["ok"].hide();
29901             buttons["cancel"].hide();
29902             buttons["yes"].hide();
29903             buttons["no"].hide();
29904             dlg.footer.dom.style.display = 'none';
29905             return width;
29906         }
29907         dlg.footer.dom.style.display = '';
29908         for(var k in buttons){
29909             if(typeof buttons[k] != "function"){
29910                 if(b[k]){
29911                     buttons[k].show();
29912                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29913                     width += buttons[k].el.getWidth()+15;
29914                 }else{
29915                     buttons[k].hide();
29916                 }
29917             }
29918         }
29919         return width;
29920     };
29921
29922     // private
29923     var handleEsc = function(d, k, e){
29924         if(opt && opt.closable !== false){
29925             dlg.hide();
29926         }
29927         if(e){
29928             e.stopEvent();
29929         }
29930     };
29931
29932     return {
29933         /**
29934          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29935          * @return {Roo.BasicDialog} The BasicDialog element
29936          */
29937         getDialog : function(){
29938            if(!dlg){
29939                 dlg = new Roo.BasicDialog("x-msg-box", {
29940                     autoCreate : true,
29941                     shadow: true,
29942                     draggable: true,
29943                     resizable:false,
29944                     constraintoviewport:false,
29945                     fixedcenter:true,
29946                     collapsible : false,
29947                     shim:true,
29948                     modal: true,
29949                     width:400, height:100,
29950                     buttonAlign:"center",
29951                     closeClick : function(){
29952                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29953                             handleButton("no");
29954                         }else{
29955                             handleButton("cancel");
29956                         }
29957                     }
29958                 });
29959                 dlg.on("hide", handleHide);
29960                 mask = dlg.mask;
29961                 dlg.addKeyListener(27, handleEsc);
29962                 buttons = {};
29963                 var bt = this.buttonText;
29964                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29965                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29966                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29967                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29968                 bodyEl = dlg.body.createChild({
29969
29970                     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>'
29971                 });
29972                 msgEl = bodyEl.dom.firstChild;
29973                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29974                 textboxEl.enableDisplayMode();
29975                 textboxEl.addKeyListener([10,13], function(){
29976                     if(dlg.isVisible() && opt && opt.buttons){
29977                         if(opt.buttons.ok){
29978                             handleButton("ok");
29979                         }else if(opt.buttons.yes){
29980                             handleButton("yes");
29981                         }
29982                     }
29983                 });
29984                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29985                 textareaEl.enableDisplayMode();
29986                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29987                 progressEl.enableDisplayMode();
29988                 var pf = progressEl.dom.firstChild;
29989                 if (pf) {
29990                     pp = Roo.get(pf.firstChild);
29991                     pp.setHeight(pf.offsetHeight);
29992                 }
29993                 
29994             }
29995             return dlg;
29996         },
29997
29998         /**
29999          * Updates the message box body text
30000          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30001          * the XHTML-compliant non-breaking space character '&amp;#160;')
30002          * @return {Roo.MessageBox} This message box
30003          */
30004         updateText : function(text){
30005             if(!dlg.isVisible() && !opt.width){
30006                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30007             }
30008             msgEl.innerHTML = text || '&#160;';
30009             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30010                         Math.max(opt.minWidth || this.minWidth, bwidth));
30011             if(opt.prompt){
30012                 activeTextEl.setWidth(w);
30013             }
30014             if(dlg.isVisible()){
30015                 dlg.fixedcenter = false;
30016             }
30017             dlg.setContentSize(w, bodyEl.getHeight());
30018             if(dlg.isVisible()){
30019                 dlg.fixedcenter = true;
30020             }
30021             return this;
30022         },
30023
30024         /**
30025          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30026          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30027          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30028          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30029          * @return {Roo.MessageBox} This message box
30030          */
30031         updateProgress : function(value, text){
30032             if(text){
30033                 this.updateText(text);
30034             }
30035             if (pp) { // weird bug on my firefox - for some reason this is not defined
30036                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30037             }
30038             return this;
30039         },        
30040
30041         /**
30042          * Returns true if the message box is currently displayed
30043          * @return {Boolean} True if the message box is visible, else false
30044          */
30045         isVisible : function(){
30046             return dlg && dlg.isVisible();  
30047         },
30048
30049         /**
30050          * Hides the message box if it is displayed
30051          */
30052         hide : function(){
30053             if(this.isVisible()){
30054                 dlg.hide();
30055             }  
30056         },
30057
30058         /**
30059          * Displays a new message box, or reinitializes an existing message box, based on the config options
30060          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30061          * The following config object properties are supported:
30062          * <pre>
30063 Property    Type             Description
30064 ----------  ---------------  ------------------------------------------------------------------------------------
30065 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30066                                    closes (defaults to undefined)
30067 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30068                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30069 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30070                                    progress and wait dialogs will ignore this property and always hide the
30071                                    close button as they can only be closed programmatically.
30072 cls               String           A custom CSS class to apply to the message box element
30073 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30074                                    displayed (defaults to 75)
30075 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30076                                    function will be btn (the name of the button that was clicked, if applicable,
30077                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30078                                    Progress and wait dialogs will ignore this option since they do not respond to
30079                                    user actions and can only be closed programmatically, so any required function
30080                                    should be called by the same code after it closes the dialog.
30081 icon              String           A CSS class that provides a background image to be used as an icon for
30082                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30083 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30084 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30085 modal             Boolean          False to allow user interaction with the page while the message box is
30086                                    displayed (defaults to true)
30087 msg               String           A string that will replace the existing message box body text (defaults
30088                                    to the XHTML-compliant non-breaking space character '&#160;')
30089 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30090 progress          Boolean          True to display a progress bar (defaults to false)
30091 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30092 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30093 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30094 title             String           The title text
30095 value             String           The string value to set into the active textbox element if displayed
30096 wait              Boolean          True to display a progress bar (defaults to false)
30097 width             Number           The width of the dialog in pixels
30098 </pre>
30099          *
30100          * Example usage:
30101          * <pre><code>
30102 Roo.Msg.show({
30103    title: 'Address',
30104    msg: 'Please enter your address:',
30105    width: 300,
30106    buttons: Roo.MessageBox.OKCANCEL,
30107    multiline: true,
30108    fn: saveAddress,
30109    animEl: 'addAddressBtn'
30110 });
30111 </code></pre>
30112          * @param {Object} config Configuration options
30113          * @return {Roo.MessageBox} This message box
30114          */
30115         show : function(options){
30116             if(this.isVisible()){
30117                 this.hide();
30118             }
30119             var d = this.getDialog();
30120             opt = options;
30121             d.setTitle(opt.title || "&#160;");
30122             d.close.setDisplayed(opt.closable !== false);
30123             activeTextEl = textboxEl;
30124             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30125             if(opt.prompt){
30126                 if(opt.multiline){
30127                     textboxEl.hide();
30128                     textareaEl.show();
30129                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30130                         opt.multiline : this.defaultTextHeight);
30131                     activeTextEl = textareaEl;
30132                 }else{
30133                     textboxEl.show();
30134                     textareaEl.hide();
30135                 }
30136             }else{
30137                 textboxEl.hide();
30138                 textareaEl.hide();
30139             }
30140             progressEl.setDisplayed(opt.progress === true);
30141             this.updateProgress(0);
30142             activeTextEl.dom.value = opt.value || "";
30143             if(opt.prompt){
30144                 dlg.setDefaultButton(activeTextEl);
30145             }else{
30146                 var bs = opt.buttons;
30147                 var db = null;
30148                 if(bs && bs.ok){
30149                     db = buttons["ok"];
30150                 }else if(bs && bs.yes){
30151                     db = buttons["yes"];
30152                 }
30153                 dlg.setDefaultButton(db);
30154             }
30155             bwidth = updateButtons(opt.buttons);
30156             this.updateText(opt.msg);
30157             if(opt.cls){
30158                 d.el.addClass(opt.cls);
30159             }
30160             d.proxyDrag = opt.proxyDrag === true;
30161             d.modal = opt.modal !== false;
30162             d.mask = opt.modal !== false ? mask : false;
30163             if(!d.isVisible()){
30164                 // force it to the end of the z-index stack so it gets a cursor in FF
30165                 document.body.appendChild(dlg.el.dom);
30166                 d.animateTarget = null;
30167                 d.show(options.animEl);
30168             }
30169             return this;
30170         },
30171
30172         /**
30173          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30174          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30175          * and closing the message box when the process is complete.
30176          * @param {String} title The title bar text
30177          * @param {String} msg The message box body text
30178          * @return {Roo.MessageBox} This message box
30179          */
30180         progress : function(title, msg){
30181             this.show({
30182                 title : title,
30183                 msg : msg,
30184                 buttons: false,
30185                 progress:true,
30186                 closable:false,
30187                 minWidth: this.minProgressWidth,
30188                 modal : true
30189             });
30190             return this;
30191         },
30192
30193         /**
30194          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30195          * If a callback function is passed it will be called after the user clicks the button, and the
30196          * id of the button that was clicked will be passed as the only parameter to the callback
30197          * (could also be the top-right close button).
30198          * @param {String} title The title bar text
30199          * @param {String} msg The message box body text
30200          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30201          * @param {Object} scope (optional) The scope of the callback function
30202          * @return {Roo.MessageBox} This message box
30203          */
30204         alert : function(title, msg, fn, scope){
30205             this.show({
30206                 title : title,
30207                 msg : msg,
30208                 buttons: this.OK,
30209                 fn: fn,
30210                 scope : scope,
30211                 modal : true
30212             });
30213             return this;
30214         },
30215
30216         /**
30217          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30218          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30219          * You are responsible for closing the message box when the process is complete.
30220          * @param {String} msg The message box body text
30221          * @param {String} title (optional) The title bar text
30222          * @return {Roo.MessageBox} This message box
30223          */
30224         wait : function(msg, title){
30225             this.show({
30226                 title : title,
30227                 msg : msg,
30228                 buttons: false,
30229                 closable:false,
30230                 progress:true,
30231                 modal:true,
30232                 width:300,
30233                 wait:true
30234             });
30235             waitTimer = Roo.TaskMgr.start({
30236                 run: function(i){
30237                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30238                 },
30239                 interval: 1000
30240             });
30241             return this;
30242         },
30243
30244         /**
30245          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30246          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30247          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30248          * @param {String} title The title bar text
30249          * @param {String} msg The message box body text
30250          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30251          * @param {Object} scope (optional) The scope of the callback function
30252          * @return {Roo.MessageBox} This message box
30253          */
30254         confirm : function(title, msg, fn, scope){
30255             this.show({
30256                 title : title,
30257                 msg : msg,
30258                 buttons: this.YESNO,
30259                 fn: fn,
30260                 scope : scope,
30261                 modal : true
30262             });
30263             return this;
30264         },
30265
30266         /**
30267          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30268          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30269          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30270          * (could also be the top-right close button) and the text that was entered will be passed as the two
30271          * parameters to the callback.
30272          * @param {String} title The title bar text
30273          * @param {String} msg The message box body text
30274          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30275          * @param {Object} scope (optional) The scope of the callback function
30276          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30277          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30278          * @return {Roo.MessageBox} This message box
30279          */
30280         prompt : function(title, msg, fn, scope, multiline){
30281             this.show({
30282                 title : title,
30283                 msg : msg,
30284                 buttons: this.OKCANCEL,
30285                 fn: fn,
30286                 minWidth:250,
30287                 scope : scope,
30288                 prompt:true,
30289                 multiline: multiline,
30290                 modal : true
30291             });
30292             return this;
30293         },
30294
30295         /**
30296          * Button config that displays a single OK button
30297          * @type Object
30298          */
30299         OK : {ok:true},
30300         /**
30301          * Button config that displays Yes and No buttons
30302          * @type Object
30303          */
30304         YESNO : {yes:true, no:true},
30305         /**
30306          * Button config that displays OK and Cancel buttons
30307          * @type Object
30308          */
30309         OKCANCEL : {ok:true, cancel:true},
30310         /**
30311          * Button config that displays Yes, No and Cancel buttons
30312          * @type Object
30313          */
30314         YESNOCANCEL : {yes:true, no:true, cancel:true},
30315
30316         /**
30317          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30318          * @type Number
30319          */
30320         defaultTextHeight : 75,
30321         /**
30322          * The maximum width in pixels of the message box (defaults to 600)
30323          * @type Number
30324          */
30325         maxWidth : 600,
30326         /**
30327          * The minimum width in pixels of the message box (defaults to 100)
30328          * @type Number
30329          */
30330         minWidth : 100,
30331         /**
30332          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30333          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30334          * @type Number
30335          */
30336         minProgressWidth : 250,
30337         /**
30338          * An object containing the default button text strings that can be overriden for localized language support.
30339          * Supported properties are: ok, cancel, yes and no.
30340          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30341          * @type Object
30342          */
30343         buttonText : {
30344             ok : "OK",
30345             cancel : "Cancel",
30346             yes : "Yes",
30347             no : "No"
30348         }
30349     };
30350 }();
30351
30352 /**
30353  * Shorthand for {@link Roo.MessageBox}
30354  */
30355 Roo.Msg = Roo.MessageBox;/*
30356  * Based on:
30357  * Ext JS Library 1.1.1
30358  * Copyright(c) 2006-2007, Ext JS, LLC.
30359  *
30360  * Originally Released Under LGPL - original licence link has changed is not relivant.
30361  *
30362  * Fork - LGPL
30363  * <script type="text/javascript">
30364  */
30365 /**
30366  * @class Roo.QuickTips
30367  * Provides attractive and customizable tooltips for any element.
30368  * @singleton
30369  */
30370 Roo.QuickTips = function(){
30371     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30372     var ce, bd, xy, dd;
30373     var visible = false, disabled = true, inited = false;
30374     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30375     
30376     var onOver = function(e){
30377         if(disabled){
30378             return;
30379         }
30380         var t = e.getTarget();
30381         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30382             return;
30383         }
30384         if(ce && t == ce.el){
30385             clearTimeout(hideProc);
30386             return;
30387         }
30388         if(t && tagEls[t.id]){
30389             tagEls[t.id].el = t;
30390             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30391             return;
30392         }
30393         var ttp, et = Roo.fly(t);
30394         var ns = cfg.namespace;
30395         if(tm.interceptTitles && t.title){
30396             ttp = t.title;
30397             t.qtip = ttp;
30398             t.removeAttribute("title");
30399             e.preventDefault();
30400         }else{
30401             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30402         }
30403         if(ttp){
30404             showProc = show.defer(tm.showDelay, tm, [{
30405                 el: t, 
30406                 text: ttp, 
30407                 width: et.getAttributeNS(ns, cfg.width),
30408                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30409                 title: et.getAttributeNS(ns, cfg.title),
30410                     cls: et.getAttributeNS(ns, cfg.cls)
30411             }]);
30412         }
30413     };
30414     
30415     var onOut = function(e){
30416         clearTimeout(showProc);
30417         var t = e.getTarget();
30418         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30419             hideProc = setTimeout(hide, tm.hideDelay);
30420         }
30421     };
30422     
30423     var onMove = function(e){
30424         if(disabled){
30425             return;
30426         }
30427         xy = e.getXY();
30428         xy[1] += 18;
30429         if(tm.trackMouse && ce){
30430             el.setXY(xy);
30431         }
30432     };
30433     
30434     var onDown = function(e){
30435         clearTimeout(showProc);
30436         clearTimeout(hideProc);
30437         if(!e.within(el)){
30438             if(tm.hideOnClick){
30439                 hide();
30440                 tm.disable();
30441                 tm.enable.defer(100, tm);
30442             }
30443         }
30444     };
30445     
30446     var getPad = function(){
30447         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30448     };
30449
30450     var show = function(o){
30451         if(disabled){
30452             return;
30453         }
30454         clearTimeout(dismissProc);
30455         ce = o;
30456         if(removeCls){ // in case manually hidden
30457             el.removeClass(removeCls);
30458             removeCls = null;
30459         }
30460         if(ce.cls){
30461             el.addClass(ce.cls);
30462             removeCls = ce.cls;
30463         }
30464         if(ce.title){
30465             tipTitle.update(ce.title);
30466             tipTitle.show();
30467         }else{
30468             tipTitle.update('');
30469             tipTitle.hide();
30470         }
30471         el.dom.style.width  = tm.maxWidth+'px';
30472         //tipBody.dom.style.width = '';
30473         tipBodyText.update(o.text);
30474         var p = getPad(), w = ce.width;
30475         if(!w){
30476             var td = tipBodyText.dom;
30477             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30478             if(aw > tm.maxWidth){
30479                 w = tm.maxWidth;
30480             }else if(aw < tm.minWidth){
30481                 w = tm.minWidth;
30482             }else{
30483                 w = aw;
30484             }
30485         }
30486         //tipBody.setWidth(w);
30487         el.setWidth(parseInt(w, 10) + p);
30488         if(ce.autoHide === false){
30489             close.setDisplayed(true);
30490             if(dd){
30491                 dd.unlock();
30492             }
30493         }else{
30494             close.setDisplayed(false);
30495             if(dd){
30496                 dd.lock();
30497             }
30498         }
30499         if(xy){
30500             el.avoidY = xy[1]-18;
30501             el.setXY(xy);
30502         }
30503         if(tm.animate){
30504             el.setOpacity(.1);
30505             el.setStyle("visibility", "visible");
30506             el.fadeIn({callback: afterShow});
30507         }else{
30508             afterShow();
30509         }
30510     };
30511     
30512     var afterShow = function(){
30513         if(ce){
30514             el.show();
30515             esc.enable();
30516             if(tm.autoDismiss && ce.autoHide !== false){
30517                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30518             }
30519         }
30520     };
30521     
30522     var hide = function(noanim){
30523         clearTimeout(dismissProc);
30524         clearTimeout(hideProc);
30525         ce = null;
30526         if(el.isVisible()){
30527             esc.disable();
30528             if(noanim !== true && tm.animate){
30529                 el.fadeOut({callback: afterHide});
30530             }else{
30531                 afterHide();
30532             } 
30533         }
30534     };
30535     
30536     var afterHide = function(){
30537         el.hide();
30538         if(removeCls){
30539             el.removeClass(removeCls);
30540             removeCls = null;
30541         }
30542     };
30543     
30544     return {
30545         /**
30546         * @cfg {Number} minWidth
30547         * The minimum width of the quick tip (defaults to 40)
30548         */
30549        minWidth : 40,
30550         /**
30551         * @cfg {Number} maxWidth
30552         * The maximum width of the quick tip (defaults to 300)
30553         */
30554        maxWidth : 300,
30555         /**
30556         * @cfg {Boolean} interceptTitles
30557         * True to automatically use the element's DOM title value if available (defaults to false)
30558         */
30559        interceptTitles : false,
30560         /**
30561         * @cfg {Boolean} trackMouse
30562         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30563         */
30564        trackMouse : false,
30565         /**
30566         * @cfg {Boolean} hideOnClick
30567         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30568         */
30569        hideOnClick : true,
30570         /**
30571         * @cfg {Number} showDelay
30572         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30573         */
30574        showDelay : 500,
30575         /**
30576         * @cfg {Number} hideDelay
30577         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30578         */
30579        hideDelay : 200,
30580         /**
30581         * @cfg {Boolean} autoHide
30582         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30583         * Used in conjunction with hideDelay.
30584         */
30585        autoHide : true,
30586         /**
30587         * @cfg {Boolean}
30588         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30589         * (defaults to true).  Used in conjunction with autoDismissDelay.
30590         */
30591        autoDismiss : true,
30592         /**
30593         * @cfg {Number}
30594         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30595         */
30596        autoDismissDelay : 5000,
30597        /**
30598         * @cfg {Boolean} animate
30599         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30600         */
30601        animate : false,
30602
30603        /**
30604         * @cfg {String} title
30605         * Title text to display (defaults to '').  This can be any valid HTML markup.
30606         */
30607         title: '',
30608        /**
30609         * @cfg {String} text
30610         * Body text to display (defaults to '').  This can be any valid HTML markup.
30611         */
30612         text : '',
30613        /**
30614         * @cfg {String} cls
30615         * A CSS class to apply to the base quick tip element (defaults to '').
30616         */
30617         cls : '',
30618        /**
30619         * @cfg {Number} width
30620         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30621         * minWidth or maxWidth.
30622         */
30623         width : null,
30624
30625     /**
30626      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30627      * or display QuickTips in a page.
30628      */
30629        init : function(){
30630           tm = Roo.QuickTips;
30631           cfg = tm.tagConfig;
30632           if(!inited){
30633               if(!Roo.isReady){ // allow calling of init() before onReady
30634                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30635                   return;
30636               }
30637               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30638               el.fxDefaults = {stopFx: true};
30639               // maximum custom styling
30640               //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>');
30641               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>');              
30642               tipTitle = el.child('h3');
30643               tipTitle.enableDisplayMode("block");
30644               tipBody = el.child('div.x-tip-bd');
30645               tipBodyText = el.child('div.x-tip-bd-inner');
30646               //bdLeft = el.child('div.x-tip-bd-left');
30647               //bdRight = el.child('div.x-tip-bd-right');
30648               close = el.child('div.x-tip-close');
30649               close.enableDisplayMode("block");
30650               close.on("click", hide);
30651               var d = Roo.get(document);
30652               d.on("mousedown", onDown);
30653               d.on("mouseover", onOver);
30654               d.on("mouseout", onOut);
30655               d.on("mousemove", onMove);
30656               esc = d.addKeyListener(27, hide);
30657               esc.disable();
30658               if(Roo.dd.DD){
30659                   dd = el.initDD("default", null, {
30660                       onDrag : function(){
30661                           el.sync();  
30662                       }
30663                   });
30664                   dd.setHandleElId(tipTitle.id);
30665                   dd.lock();
30666               }
30667               inited = true;
30668           }
30669           this.enable(); 
30670        },
30671
30672     /**
30673      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30674      * are supported:
30675      * <pre>
30676 Property    Type                   Description
30677 ----------  ---------------------  ------------------------------------------------------------------------
30678 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30679      * </ul>
30680      * @param {Object} config The config object
30681      */
30682        register : function(config){
30683            var cs = config instanceof Array ? config : arguments;
30684            for(var i = 0, len = cs.length; i < len; i++) {
30685                var c = cs[i];
30686                var target = c.target;
30687                if(target){
30688                    if(target instanceof Array){
30689                        for(var j = 0, jlen = target.length; j < jlen; j++){
30690                            tagEls[target[j]] = c;
30691                        }
30692                    }else{
30693                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30694                    }
30695                }
30696            }
30697        },
30698
30699     /**
30700      * Removes this quick tip from its element and destroys it.
30701      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30702      */
30703        unregister : function(el){
30704            delete tagEls[Roo.id(el)];
30705        },
30706
30707     /**
30708      * Enable this quick tip.
30709      */
30710        enable : function(){
30711            if(inited && disabled){
30712                locks.pop();
30713                if(locks.length < 1){
30714                    disabled = false;
30715                }
30716            }
30717        },
30718
30719     /**
30720      * Disable this quick tip.
30721      */
30722        disable : function(){
30723           disabled = true;
30724           clearTimeout(showProc);
30725           clearTimeout(hideProc);
30726           clearTimeout(dismissProc);
30727           if(ce){
30728               hide(true);
30729           }
30730           locks.push(1);
30731        },
30732
30733     /**
30734      * Returns true if the quick tip is enabled, else false.
30735      */
30736        isEnabled : function(){
30737             return !disabled;
30738        },
30739
30740         // private
30741        tagConfig : {
30742            namespace : "ext",
30743            attribute : "qtip",
30744            width : "width",
30745            target : "target",
30746            title : "qtitle",
30747            hide : "hide",
30748            cls : "qclass"
30749        }
30750    };
30751 }();
30752
30753 // backwards compat
30754 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30755  * Based on:
30756  * Ext JS Library 1.1.1
30757  * Copyright(c) 2006-2007, Ext JS, LLC.
30758  *
30759  * Originally Released Under LGPL - original licence link has changed is not relivant.
30760  *
30761  * Fork - LGPL
30762  * <script type="text/javascript">
30763  */
30764  
30765
30766 /**
30767  * @class Roo.tree.TreePanel
30768  * @extends Roo.data.Tree
30769
30770  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30771  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30772  * @cfg {Boolean} enableDD true to enable drag and drop
30773  * @cfg {Boolean} enableDrag true to enable just drag
30774  * @cfg {Boolean} enableDrop true to enable just drop
30775  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30776  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30777  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30778  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30779  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30780  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30781  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30782  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30783  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30784  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30785  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30786  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30787  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30788  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30789  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30790  * 
30791  * @constructor
30792  * @param {String/HTMLElement/Element} el The container element
30793  * @param {Object} config
30794  */
30795 Roo.tree.TreePanel = function(el, config){
30796     var root = false;
30797     var loader = false;
30798     if (config.root) {
30799         root = config.root;
30800         delete config.root;
30801     }
30802     if (config.loader) {
30803         loader = config.loader;
30804         delete config.loader;
30805     }
30806     
30807     Roo.apply(this, config);
30808     Roo.tree.TreePanel.superclass.constructor.call(this);
30809     this.el = Roo.get(el);
30810     this.el.addClass('x-tree');
30811     //console.log(root);
30812     if (root) {
30813         this.setRootNode( Roo.factory(root, Roo.tree));
30814     }
30815     if (loader) {
30816         this.loader = Roo.factory(loader, Roo.tree);
30817     }
30818    /**
30819     * Read-only. The id of the container element becomes this TreePanel's id.
30820     */
30821    this.id = this.el.id;
30822    this.addEvents({
30823         /**
30824         * @event beforeload
30825         * Fires before a node is loaded, return false to cancel
30826         * @param {Node} node The node being loaded
30827         */
30828         "beforeload" : true,
30829         /**
30830         * @event load
30831         * Fires when a node is loaded
30832         * @param {Node} node The node that was loaded
30833         */
30834         "load" : true,
30835         /**
30836         * @event textchange
30837         * Fires when the text for a node is changed
30838         * @param {Node} node The node
30839         * @param {String} text The new text
30840         * @param {String} oldText The old text
30841         */
30842         "textchange" : true,
30843         /**
30844         * @event beforeexpand
30845         * Fires before a node is expanded, return false to cancel.
30846         * @param {Node} node The node
30847         * @param {Boolean} deep
30848         * @param {Boolean} anim
30849         */
30850         "beforeexpand" : true,
30851         /**
30852         * @event beforecollapse
30853         * Fires before a node is collapsed, return false to cancel.
30854         * @param {Node} node The node
30855         * @param {Boolean} deep
30856         * @param {Boolean} anim
30857         */
30858         "beforecollapse" : true,
30859         /**
30860         * @event expand
30861         * Fires when a node is expanded
30862         * @param {Node} node The node
30863         */
30864         "expand" : true,
30865         /**
30866         * @event disabledchange
30867         * Fires when the disabled status of a node changes
30868         * @param {Node} node The node
30869         * @param {Boolean} disabled
30870         */
30871         "disabledchange" : true,
30872         /**
30873         * @event collapse
30874         * Fires when a node is collapsed
30875         * @param {Node} node The node
30876         */
30877         "collapse" : true,
30878         /**
30879         * @event beforeclick
30880         * Fires before click processing on a node. Return false to cancel the default action.
30881         * @param {Node} node The node
30882         * @param {Roo.EventObject} e The event object
30883         */
30884         "beforeclick":true,
30885         /**
30886         * @event checkchange
30887         * Fires when a node with a checkbox's checked property changes
30888         * @param {Node} this This node
30889         * @param {Boolean} checked
30890         */
30891         "checkchange":true,
30892         /**
30893         * @event click
30894         * Fires when a node is clicked
30895         * @param {Node} node The node
30896         * @param {Roo.EventObject} e The event object
30897         */
30898         "click":true,
30899         /**
30900         * @event dblclick
30901         * Fires when a node is double clicked
30902         * @param {Node} node The node
30903         * @param {Roo.EventObject} e The event object
30904         */
30905         "dblclick":true,
30906         /**
30907         * @event contextmenu
30908         * Fires when a node is right clicked
30909         * @param {Node} node The node
30910         * @param {Roo.EventObject} e The event object
30911         */
30912         "contextmenu":true,
30913         /**
30914         * @event beforechildrenrendered
30915         * Fires right before the child nodes for a node are rendered
30916         * @param {Node} node The node
30917         */
30918         "beforechildrenrendered":true,
30919        /**
30920              * @event startdrag
30921              * Fires when a node starts being dragged
30922              * @param {Roo.tree.TreePanel} this
30923              * @param {Roo.tree.TreeNode} node
30924              * @param {event} e The raw browser event
30925              */ 
30926             "startdrag" : true,
30927             /**
30928              * @event enddrag
30929              * Fires when a drag operation is complete
30930              * @param {Roo.tree.TreePanel} this
30931              * @param {Roo.tree.TreeNode} node
30932              * @param {event} e The raw browser event
30933              */
30934             "enddrag" : true,
30935             /**
30936              * @event dragdrop
30937              * Fires when a dragged node is dropped on a valid DD target
30938              * @param {Roo.tree.TreePanel} this
30939              * @param {Roo.tree.TreeNode} node
30940              * @param {DD} dd The dd it was dropped on
30941              * @param {event} e The raw browser event
30942              */
30943             "dragdrop" : true,
30944             /**
30945              * @event beforenodedrop
30946              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30947              * passed to handlers has the following properties:<br />
30948              * <ul style="padding:5px;padding-left:16px;">
30949              * <li>tree - The TreePanel</li>
30950              * <li>target - The node being targeted for the drop</li>
30951              * <li>data - The drag data from the drag source</li>
30952              * <li>point - The point of the drop - append, above or below</li>
30953              * <li>source - The drag source</li>
30954              * <li>rawEvent - Raw mouse event</li>
30955              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30956              * to be inserted by setting them on this object.</li>
30957              * <li>cancel - Set this to true to cancel the drop.</li>
30958              * </ul>
30959              * @param {Object} dropEvent
30960              */
30961             "beforenodedrop" : true,
30962             /**
30963              * @event nodedrop
30964              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30965              * passed to handlers has the following properties:<br />
30966              * <ul style="padding:5px;padding-left:16px;">
30967              * <li>tree - The TreePanel</li>
30968              * <li>target - The node being targeted for the drop</li>
30969              * <li>data - The drag data from the drag source</li>
30970              * <li>point - The point of the drop - append, above or below</li>
30971              * <li>source - The drag source</li>
30972              * <li>rawEvent - Raw mouse event</li>
30973              * <li>dropNode - Dropped node(s).</li>
30974              * </ul>
30975              * @param {Object} dropEvent
30976              */
30977             "nodedrop" : true,
30978              /**
30979              * @event nodedragover
30980              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30981              * passed to handlers has the following properties:<br />
30982              * <ul style="padding:5px;padding-left:16px;">
30983              * <li>tree - The TreePanel</li>
30984              * <li>target - The node being targeted for the drop</li>
30985              * <li>data - The drag data from the drag source</li>
30986              * <li>point - The point of the drop - append, above or below</li>
30987              * <li>source - The drag source</li>
30988              * <li>rawEvent - Raw mouse event</li>
30989              * <li>dropNode - Drop node(s) provided by the source.</li>
30990              * <li>cancel - Set this to true to signal drop not allowed.</li>
30991              * </ul>
30992              * @param {Object} dragOverEvent
30993              */
30994             "nodedragover" : true
30995         
30996    });
30997    if(this.singleExpand){
30998        this.on("beforeexpand", this.restrictExpand, this);
30999    }
31000 };
31001 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31002     rootVisible : true,
31003     animate: Roo.enableFx,
31004     lines : true,
31005     enableDD : false,
31006     hlDrop : Roo.enableFx,
31007   
31008     renderer: false,
31009     
31010     rendererTip: false,
31011     // private
31012     restrictExpand : function(node){
31013         var p = node.parentNode;
31014         if(p){
31015             if(p.expandedChild && p.expandedChild.parentNode == p){
31016                 p.expandedChild.collapse();
31017             }
31018             p.expandedChild = node;
31019         }
31020     },
31021
31022     // private override
31023     setRootNode : function(node){
31024         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31025         if(!this.rootVisible){
31026             node.ui = new Roo.tree.RootTreeNodeUI(node);
31027         }
31028         return node;
31029     },
31030
31031     /**
31032      * Returns the container element for this TreePanel
31033      */
31034     getEl : function(){
31035         return this.el;
31036     },
31037
31038     /**
31039      * Returns the default TreeLoader for this TreePanel
31040      */
31041     getLoader : function(){
31042         return this.loader;
31043     },
31044
31045     /**
31046      * Expand all nodes
31047      */
31048     expandAll : function(){
31049         this.root.expand(true);
31050     },
31051
31052     /**
31053      * Collapse all nodes
31054      */
31055     collapseAll : function(){
31056         this.root.collapse(true);
31057     },
31058
31059     /**
31060      * Returns the selection model used by this TreePanel
31061      */
31062     getSelectionModel : function(){
31063         if(!this.selModel){
31064             this.selModel = new Roo.tree.DefaultSelectionModel();
31065         }
31066         return this.selModel;
31067     },
31068
31069     /**
31070      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31071      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31072      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31073      * @return {Array}
31074      */
31075     getChecked : function(a, startNode){
31076         startNode = startNode || this.root;
31077         var r = [];
31078         var f = function(){
31079             if(this.attributes.checked){
31080                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31081             }
31082         }
31083         startNode.cascade(f);
31084         return r;
31085     },
31086
31087     /**
31088      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31089      * @param {String} path
31090      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31091      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31092      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31093      */
31094     expandPath : function(path, attr, callback){
31095         attr = attr || "id";
31096         var keys = path.split(this.pathSeparator);
31097         var curNode = this.root;
31098         if(curNode.attributes[attr] != keys[1]){ // invalid root
31099             if(callback){
31100                 callback(false, null);
31101             }
31102             return;
31103         }
31104         var index = 1;
31105         var f = function(){
31106             if(++index == keys.length){
31107                 if(callback){
31108                     callback(true, curNode);
31109                 }
31110                 return;
31111             }
31112             var c = curNode.findChild(attr, keys[index]);
31113             if(!c){
31114                 if(callback){
31115                     callback(false, curNode);
31116                 }
31117                 return;
31118             }
31119             curNode = c;
31120             c.expand(false, false, f);
31121         };
31122         curNode.expand(false, false, f);
31123     },
31124
31125     /**
31126      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31127      * @param {String} path
31128      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31129      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31130      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31131      */
31132     selectPath : function(path, attr, callback){
31133         attr = attr || "id";
31134         var keys = path.split(this.pathSeparator);
31135         var v = keys.pop();
31136         if(keys.length > 0){
31137             var f = function(success, node){
31138                 if(success && node){
31139                     var n = node.findChild(attr, v);
31140                     if(n){
31141                         n.select();
31142                         if(callback){
31143                             callback(true, n);
31144                         }
31145                     }else if(callback){
31146                         callback(false, n);
31147                     }
31148                 }else{
31149                     if(callback){
31150                         callback(false, n);
31151                     }
31152                 }
31153             };
31154             this.expandPath(keys.join(this.pathSeparator), attr, f);
31155         }else{
31156             this.root.select();
31157             if(callback){
31158                 callback(true, this.root);
31159             }
31160         }
31161     },
31162
31163     getTreeEl : function(){
31164         return this.el;
31165     },
31166
31167     /**
31168      * Trigger rendering of this TreePanel
31169      */
31170     render : function(){
31171         if (this.innerCt) {
31172             return this; // stop it rendering more than once!!
31173         }
31174         
31175         this.innerCt = this.el.createChild({tag:"ul",
31176                cls:"x-tree-root-ct " +
31177                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31178
31179         if(this.containerScroll){
31180             Roo.dd.ScrollManager.register(this.el);
31181         }
31182         if((this.enableDD || this.enableDrop) && !this.dropZone){
31183            /**
31184             * The dropZone used by this tree if drop is enabled
31185             * @type Roo.tree.TreeDropZone
31186             */
31187              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31188                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31189            });
31190         }
31191         if((this.enableDD || this.enableDrag) && !this.dragZone){
31192            /**
31193             * The dragZone used by this tree if drag is enabled
31194             * @type Roo.tree.TreeDragZone
31195             */
31196             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31197                ddGroup: this.ddGroup || "TreeDD",
31198                scroll: this.ddScroll
31199            });
31200         }
31201         this.getSelectionModel().init(this);
31202         if (!this.root) {
31203             console.log("ROOT not set in tree");
31204             return;
31205         }
31206         this.root.render();
31207         if(!this.rootVisible){
31208             this.root.renderChildren();
31209         }
31210         return this;
31211     }
31212 });/*
31213  * Based on:
31214  * Ext JS Library 1.1.1
31215  * Copyright(c) 2006-2007, Ext JS, LLC.
31216  *
31217  * Originally Released Under LGPL - original licence link has changed is not relivant.
31218  *
31219  * Fork - LGPL
31220  * <script type="text/javascript">
31221  */
31222  
31223
31224 /**
31225  * @class Roo.tree.DefaultSelectionModel
31226  * @extends Roo.util.Observable
31227  * The default single selection for a TreePanel.
31228  */
31229 Roo.tree.DefaultSelectionModel = function(){
31230    this.selNode = null;
31231    
31232    this.addEvents({
31233        /**
31234         * @event selectionchange
31235         * Fires when the selected node changes
31236         * @param {DefaultSelectionModel} this
31237         * @param {TreeNode} node the new selection
31238         */
31239        "selectionchange" : true,
31240
31241        /**
31242         * @event beforeselect
31243         * Fires before the selected node changes, return false to cancel the change
31244         * @param {DefaultSelectionModel} this
31245         * @param {TreeNode} node the new selection
31246         * @param {TreeNode} node the old selection
31247         */
31248        "beforeselect" : true
31249    });
31250 };
31251
31252 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31253     init : function(tree){
31254         this.tree = tree;
31255         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31256         tree.on("click", this.onNodeClick, this);
31257     },
31258     
31259     onNodeClick : function(node, e){
31260         if (e.ctrlKey && this.selNode == node)  {
31261             this.unselect(node);
31262             return;
31263         }
31264         this.select(node);
31265     },
31266     
31267     /**
31268      * Select a node.
31269      * @param {TreeNode} node The node to select
31270      * @return {TreeNode} The selected node
31271      */
31272     select : function(node){
31273         var last = this.selNode;
31274         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31275             if(last){
31276                 last.ui.onSelectedChange(false);
31277             }
31278             this.selNode = node;
31279             node.ui.onSelectedChange(true);
31280             this.fireEvent("selectionchange", this, node, last);
31281         }
31282         return node;
31283     },
31284     
31285     /**
31286      * Deselect a node.
31287      * @param {TreeNode} node The node to unselect
31288      */
31289     unselect : function(node){
31290         if(this.selNode == node){
31291             this.clearSelections();
31292         }    
31293     },
31294     
31295     /**
31296      * Clear all selections
31297      */
31298     clearSelections : function(){
31299         var n = this.selNode;
31300         if(n){
31301             n.ui.onSelectedChange(false);
31302             this.selNode = null;
31303             this.fireEvent("selectionchange", this, null);
31304         }
31305         return n;
31306     },
31307     
31308     /**
31309      * Get the selected node
31310      * @return {TreeNode} The selected node
31311      */
31312     getSelectedNode : function(){
31313         return this.selNode;    
31314     },
31315     
31316     /**
31317      * Returns true if the node is selected
31318      * @param {TreeNode} node The node to check
31319      * @return {Boolean}
31320      */
31321     isSelected : function(node){
31322         return this.selNode == node;  
31323     },
31324
31325     /**
31326      * Selects the node above the selected node in the tree, intelligently walking the nodes
31327      * @return TreeNode The new selection
31328      */
31329     selectPrevious : function(){
31330         var s = this.selNode || this.lastSelNode;
31331         if(!s){
31332             return null;
31333         }
31334         var ps = s.previousSibling;
31335         if(ps){
31336             if(!ps.isExpanded() || ps.childNodes.length < 1){
31337                 return this.select(ps);
31338             } else{
31339                 var lc = ps.lastChild;
31340                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31341                     lc = lc.lastChild;
31342                 }
31343                 return this.select(lc);
31344             }
31345         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31346             return this.select(s.parentNode);
31347         }
31348         return null;
31349     },
31350
31351     /**
31352      * Selects the node above the selected node in the tree, intelligently walking the nodes
31353      * @return TreeNode The new selection
31354      */
31355     selectNext : function(){
31356         var s = this.selNode || this.lastSelNode;
31357         if(!s){
31358             return null;
31359         }
31360         if(s.firstChild && s.isExpanded()){
31361              return this.select(s.firstChild);
31362          }else if(s.nextSibling){
31363              return this.select(s.nextSibling);
31364          }else if(s.parentNode){
31365             var newS = null;
31366             s.parentNode.bubble(function(){
31367                 if(this.nextSibling){
31368                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31369                     return false;
31370                 }
31371             });
31372             return newS;
31373          }
31374         return null;
31375     },
31376
31377     onKeyDown : function(e){
31378         var s = this.selNode || this.lastSelNode;
31379         // undesirable, but required
31380         var sm = this;
31381         if(!s){
31382             return;
31383         }
31384         var k = e.getKey();
31385         switch(k){
31386              case e.DOWN:
31387                  e.stopEvent();
31388                  this.selectNext();
31389              break;
31390              case e.UP:
31391                  e.stopEvent();
31392                  this.selectPrevious();
31393              break;
31394              case e.RIGHT:
31395                  e.preventDefault();
31396                  if(s.hasChildNodes()){
31397                      if(!s.isExpanded()){
31398                          s.expand();
31399                      }else if(s.firstChild){
31400                          this.select(s.firstChild, e);
31401                      }
31402                  }
31403              break;
31404              case e.LEFT:
31405                  e.preventDefault();
31406                  if(s.hasChildNodes() && s.isExpanded()){
31407                      s.collapse();
31408                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31409                      this.select(s.parentNode, e);
31410                  }
31411              break;
31412         };
31413     }
31414 });
31415
31416 /**
31417  * @class Roo.tree.MultiSelectionModel
31418  * @extends Roo.util.Observable
31419  * Multi selection for a TreePanel.
31420  */
31421 Roo.tree.MultiSelectionModel = function(){
31422    this.selNodes = [];
31423    this.selMap = {};
31424    this.addEvents({
31425        /**
31426         * @event selectionchange
31427         * Fires when the selected nodes change
31428         * @param {MultiSelectionModel} this
31429         * @param {Array} nodes Array of the selected nodes
31430         */
31431        "selectionchange" : true
31432    });
31433 };
31434
31435 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31436     init : function(tree){
31437         this.tree = tree;
31438         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31439         tree.on("click", this.onNodeClick, this);
31440     },
31441     
31442     onNodeClick : function(node, e){
31443         this.select(node, e, e.ctrlKey);
31444     },
31445     
31446     /**
31447      * Select a node.
31448      * @param {TreeNode} node The node to select
31449      * @param {EventObject} e (optional) An event associated with the selection
31450      * @param {Boolean} keepExisting True to retain existing selections
31451      * @return {TreeNode} The selected node
31452      */
31453     select : function(node, e, keepExisting){
31454         if(keepExisting !== true){
31455             this.clearSelections(true);
31456         }
31457         if(this.isSelected(node)){
31458             this.lastSelNode = node;
31459             return node;
31460         }
31461         this.selNodes.push(node);
31462         this.selMap[node.id] = node;
31463         this.lastSelNode = node;
31464         node.ui.onSelectedChange(true);
31465         this.fireEvent("selectionchange", this, this.selNodes);
31466         return node;
31467     },
31468     
31469     /**
31470      * Deselect a node.
31471      * @param {TreeNode} node The node to unselect
31472      */
31473     unselect : function(node){
31474         if(this.selMap[node.id]){
31475             node.ui.onSelectedChange(false);
31476             var sn = this.selNodes;
31477             var index = -1;
31478             if(sn.indexOf){
31479                 index = sn.indexOf(node);
31480             }else{
31481                 for(var i = 0, len = sn.length; i < len; i++){
31482                     if(sn[i] == node){
31483                         index = i;
31484                         break;
31485                     }
31486                 }
31487             }
31488             if(index != -1){
31489                 this.selNodes.splice(index, 1);
31490             }
31491             delete this.selMap[node.id];
31492             this.fireEvent("selectionchange", this, this.selNodes);
31493         }
31494     },
31495     
31496     /**
31497      * Clear all selections
31498      */
31499     clearSelections : function(suppressEvent){
31500         var sn = this.selNodes;
31501         if(sn.length > 0){
31502             for(var i = 0, len = sn.length; i < len; i++){
31503                 sn[i].ui.onSelectedChange(false);
31504             }
31505             this.selNodes = [];
31506             this.selMap = {};
31507             if(suppressEvent !== true){
31508                 this.fireEvent("selectionchange", this, this.selNodes);
31509             }
31510         }
31511     },
31512     
31513     /**
31514      * Returns true if the node is selected
31515      * @param {TreeNode} node The node to check
31516      * @return {Boolean}
31517      */
31518     isSelected : function(node){
31519         return this.selMap[node.id] ? true : false;  
31520     },
31521     
31522     /**
31523      * Returns an array of the selected nodes
31524      * @return {Array}
31525      */
31526     getSelectedNodes : function(){
31527         return this.selNodes;    
31528     },
31529
31530     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31531
31532     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31533
31534     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31535 });/*
31536  * Based on:
31537  * Ext JS Library 1.1.1
31538  * Copyright(c) 2006-2007, Ext JS, LLC.
31539  *
31540  * Originally Released Under LGPL - original licence link has changed is not relivant.
31541  *
31542  * Fork - LGPL
31543  * <script type="text/javascript">
31544  */
31545  
31546 /**
31547  * @class Roo.tree.TreeNode
31548  * @extends Roo.data.Node
31549  * @cfg {String} text The text for this node
31550  * @cfg {Boolean} expanded true to start the node expanded
31551  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31552  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31553  * @cfg {Boolean} disabled true to start the node disabled
31554  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31555  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31556  * @cfg {String} cls A css class to be added to the node
31557  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31558  * @cfg {String} href URL of the link used for the node (defaults to #)
31559  * @cfg {String} hrefTarget target frame for the link
31560  * @cfg {String} qtip An Ext QuickTip for the node
31561  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31562  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31563  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31564  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31565  * (defaults to undefined with no checkbox rendered)
31566  * @constructor
31567  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31568  */
31569 Roo.tree.TreeNode = function(attributes){
31570     attributes = attributes || {};
31571     if(typeof attributes == "string"){
31572         attributes = {text: attributes};
31573     }
31574     this.childrenRendered = false;
31575     this.rendered = false;
31576     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31577     this.expanded = attributes.expanded === true;
31578     this.isTarget = attributes.isTarget !== false;
31579     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31580     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31581
31582     /**
31583      * Read-only. The text for this node. To change it use setText().
31584      * @type String
31585      */
31586     this.text = attributes.text;
31587     /**
31588      * True if this node is disabled.
31589      * @type Boolean
31590      */
31591     this.disabled = attributes.disabled === true;
31592
31593     this.addEvents({
31594         /**
31595         * @event textchange
31596         * Fires when the text for this node is changed
31597         * @param {Node} this This node
31598         * @param {String} text The new text
31599         * @param {String} oldText The old text
31600         */
31601         "textchange" : true,
31602         /**
31603         * @event beforeexpand
31604         * Fires before this node is expanded, return false to cancel.
31605         * @param {Node} this This node
31606         * @param {Boolean} deep
31607         * @param {Boolean} anim
31608         */
31609         "beforeexpand" : true,
31610         /**
31611         * @event beforecollapse
31612         * Fires before this node is collapsed, return false to cancel.
31613         * @param {Node} this This node
31614         * @param {Boolean} deep
31615         * @param {Boolean} anim
31616         */
31617         "beforecollapse" : true,
31618         /**
31619         * @event expand
31620         * Fires when this node is expanded
31621         * @param {Node} this This node
31622         */
31623         "expand" : true,
31624         /**
31625         * @event disabledchange
31626         * Fires when the disabled status of this node changes
31627         * @param {Node} this This node
31628         * @param {Boolean} disabled
31629         */
31630         "disabledchange" : true,
31631         /**
31632         * @event collapse
31633         * Fires when this node is collapsed
31634         * @param {Node} this This node
31635         */
31636         "collapse" : true,
31637         /**
31638         * @event beforeclick
31639         * Fires before click processing. Return false to cancel the default action.
31640         * @param {Node} this This node
31641         * @param {Roo.EventObject} e The event object
31642         */
31643         "beforeclick":true,
31644         /**
31645         * @event checkchange
31646         * Fires when a node with a checkbox's checked property changes
31647         * @param {Node} this This node
31648         * @param {Boolean} checked
31649         */
31650         "checkchange":true,
31651         /**
31652         * @event click
31653         * Fires when this node is clicked
31654         * @param {Node} this This node
31655         * @param {Roo.EventObject} e The event object
31656         */
31657         "click":true,
31658         /**
31659         * @event dblclick
31660         * Fires when this node is double clicked
31661         * @param {Node} this This node
31662         * @param {Roo.EventObject} e The event object
31663         */
31664         "dblclick":true,
31665         /**
31666         * @event contextmenu
31667         * Fires when this node is right clicked
31668         * @param {Node} this This node
31669         * @param {Roo.EventObject} e The event object
31670         */
31671         "contextmenu":true,
31672         /**
31673         * @event beforechildrenrendered
31674         * Fires right before the child nodes for this node are rendered
31675         * @param {Node} this This node
31676         */
31677         "beforechildrenrendered":true
31678     });
31679
31680     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31681
31682     /**
31683      * Read-only. The UI for this node
31684      * @type TreeNodeUI
31685      */
31686     this.ui = new uiClass(this);
31687 };
31688 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31689     preventHScroll: true,
31690     /**
31691      * Returns true if this node is expanded
31692      * @return {Boolean}
31693      */
31694     isExpanded : function(){
31695         return this.expanded;
31696     },
31697
31698     /**
31699      * Returns the UI object for this node
31700      * @return {TreeNodeUI}
31701      */
31702     getUI : function(){
31703         return this.ui;
31704     },
31705
31706     // private override
31707     setFirstChild : function(node){
31708         var of = this.firstChild;
31709         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31710         if(this.childrenRendered && of && node != of){
31711             of.renderIndent(true, true);
31712         }
31713         if(this.rendered){
31714             this.renderIndent(true, true);
31715         }
31716     },
31717
31718     // private override
31719     setLastChild : function(node){
31720         var ol = this.lastChild;
31721         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31722         if(this.childrenRendered && ol && node != ol){
31723             ol.renderIndent(true, true);
31724         }
31725         if(this.rendered){
31726             this.renderIndent(true, true);
31727         }
31728     },
31729
31730     // these methods are overridden to provide lazy rendering support
31731     // private override
31732     appendChild : function(){
31733         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31734         if(node && this.childrenRendered){
31735             node.render();
31736         }
31737         this.ui.updateExpandIcon();
31738         return node;
31739     },
31740
31741     // private override
31742     removeChild : function(node){
31743         this.ownerTree.getSelectionModel().unselect(node);
31744         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31745         // if it's been rendered remove dom node
31746         if(this.childrenRendered){
31747             node.ui.remove();
31748         }
31749         if(this.childNodes.length < 1){
31750             this.collapse(false, false);
31751         }else{
31752             this.ui.updateExpandIcon();
31753         }
31754         if(!this.firstChild) {
31755             this.childrenRendered = false;
31756         }
31757         return node;
31758     },
31759
31760     // private override
31761     insertBefore : function(node, refNode){
31762         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31763         if(newNode && refNode && this.childrenRendered){
31764             node.render();
31765         }
31766         this.ui.updateExpandIcon();
31767         return newNode;
31768     },
31769
31770     /**
31771      * Sets the text for this node
31772      * @param {String} text
31773      */
31774     setText : function(text){
31775         var oldText = this.text;
31776         this.text = text;
31777         this.attributes.text = text;
31778         if(this.rendered){ // event without subscribing
31779             this.ui.onTextChange(this, text, oldText);
31780         }
31781         this.fireEvent("textchange", this, text, oldText);
31782     },
31783
31784     /**
31785      * Triggers selection of this node
31786      */
31787     select : function(){
31788         this.getOwnerTree().getSelectionModel().select(this);
31789     },
31790
31791     /**
31792      * Triggers deselection of this node
31793      */
31794     unselect : function(){
31795         this.getOwnerTree().getSelectionModel().unselect(this);
31796     },
31797
31798     /**
31799      * Returns true if this node is selected
31800      * @return {Boolean}
31801      */
31802     isSelected : function(){
31803         return this.getOwnerTree().getSelectionModel().isSelected(this);
31804     },
31805
31806     /**
31807      * Expand this node.
31808      * @param {Boolean} deep (optional) True to expand all children as well
31809      * @param {Boolean} anim (optional) false to cancel the default animation
31810      * @param {Function} callback (optional) A callback to be called when
31811      * expanding this node completes (does not wait for deep expand to complete).
31812      * Called with 1 parameter, this node.
31813      */
31814     expand : function(deep, anim, callback){
31815         if(!this.expanded){
31816             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31817                 return;
31818             }
31819             if(!this.childrenRendered){
31820                 this.renderChildren();
31821             }
31822             this.expanded = true;
31823             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31824                 this.ui.animExpand(function(){
31825                     this.fireEvent("expand", this);
31826                     if(typeof callback == "function"){
31827                         callback(this);
31828                     }
31829                     if(deep === true){
31830                         this.expandChildNodes(true);
31831                     }
31832                 }.createDelegate(this));
31833                 return;
31834             }else{
31835                 this.ui.expand();
31836                 this.fireEvent("expand", this);
31837                 if(typeof callback == "function"){
31838                     callback(this);
31839                 }
31840             }
31841         }else{
31842            if(typeof callback == "function"){
31843                callback(this);
31844            }
31845         }
31846         if(deep === true){
31847             this.expandChildNodes(true);
31848         }
31849     },
31850
31851     isHiddenRoot : function(){
31852         return this.isRoot && !this.getOwnerTree().rootVisible;
31853     },
31854
31855     /**
31856      * Collapse this node.
31857      * @param {Boolean} deep (optional) True to collapse all children as well
31858      * @param {Boolean} anim (optional) false to cancel the default animation
31859      */
31860     collapse : function(deep, anim){
31861         if(this.expanded && !this.isHiddenRoot()){
31862             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31863                 return;
31864             }
31865             this.expanded = false;
31866             if((this.getOwnerTree().animate && anim !== false) || anim){
31867                 this.ui.animCollapse(function(){
31868                     this.fireEvent("collapse", this);
31869                     if(deep === true){
31870                         this.collapseChildNodes(true);
31871                     }
31872                 }.createDelegate(this));
31873                 return;
31874             }else{
31875                 this.ui.collapse();
31876                 this.fireEvent("collapse", this);
31877             }
31878         }
31879         if(deep === true){
31880             var cs = this.childNodes;
31881             for(var i = 0, len = cs.length; i < len; i++) {
31882                 cs[i].collapse(true, false);
31883             }
31884         }
31885     },
31886
31887     // private
31888     delayedExpand : function(delay){
31889         if(!this.expandProcId){
31890             this.expandProcId = this.expand.defer(delay, this);
31891         }
31892     },
31893
31894     // private
31895     cancelExpand : function(){
31896         if(this.expandProcId){
31897             clearTimeout(this.expandProcId);
31898         }
31899         this.expandProcId = false;
31900     },
31901
31902     /**
31903      * Toggles expanded/collapsed state of the node
31904      */
31905     toggle : function(){
31906         if(this.expanded){
31907             this.collapse();
31908         }else{
31909             this.expand();
31910         }
31911     },
31912
31913     /**
31914      * Ensures all parent nodes are expanded
31915      */
31916     ensureVisible : function(callback){
31917         var tree = this.getOwnerTree();
31918         tree.expandPath(this.parentNode.getPath(), false, function(){
31919             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31920             Roo.callback(callback);
31921         }.createDelegate(this));
31922     },
31923
31924     /**
31925      * Expand all child nodes
31926      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31927      */
31928     expandChildNodes : function(deep){
31929         var cs = this.childNodes;
31930         for(var i = 0, len = cs.length; i < len; i++) {
31931                 cs[i].expand(deep);
31932         }
31933     },
31934
31935     /**
31936      * Collapse all child nodes
31937      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31938      */
31939     collapseChildNodes : function(deep){
31940         var cs = this.childNodes;
31941         for(var i = 0, len = cs.length; i < len; i++) {
31942                 cs[i].collapse(deep);
31943         }
31944     },
31945
31946     /**
31947      * Disables this node
31948      */
31949     disable : function(){
31950         this.disabled = true;
31951         this.unselect();
31952         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31953             this.ui.onDisableChange(this, true);
31954         }
31955         this.fireEvent("disabledchange", this, true);
31956     },
31957
31958     /**
31959      * Enables this node
31960      */
31961     enable : function(){
31962         this.disabled = false;
31963         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31964             this.ui.onDisableChange(this, false);
31965         }
31966         this.fireEvent("disabledchange", this, false);
31967     },
31968
31969     // private
31970     renderChildren : function(suppressEvent){
31971         if(suppressEvent !== false){
31972             this.fireEvent("beforechildrenrendered", this);
31973         }
31974         var cs = this.childNodes;
31975         for(var i = 0, len = cs.length; i < len; i++){
31976             cs[i].render(true);
31977         }
31978         this.childrenRendered = true;
31979     },
31980
31981     // private
31982     sort : function(fn, scope){
31983         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31984         if(this.childrenRendered){
31985             var cs = this.childNodes;
31986             for(var i = 0, len = cs.length; i < len; i++){
31987                 cs[i].render(true);
31988             }
31989         }
31990     },
31991
31992     // private
31993     render : function(bulkRender){
31994         this.ui.render(bulkRender);
31995         if(!this.rendered){
31996             this.rendered = true;
31997             if(this.expanded){
31998                 this.expanded = false;
31999                 this.expand(false, false);
32000             }
32001         }
32002     },
32003
32004     // private
32005     renderIndent : function(deep, refresh){
32006         if(refresh){
32007             this.ui.childIndent = null;
32008         }
32009         this.ui.renderIndent();
32010         if(deep === true && this.childrenRendered){
32011             var cs = this.childNodes;
32012             for(var i = 0, len = cs.length; i < len; i++){
32013                 cs[i].renderIndent(true, refresh);
32014             }
32015         }
32016     }
32017 });/*
32018  * Based on:
32019  * Ext JS Library 1.1.1
32020  * Copyright(c) 2006-2007, Ext JS, LLC.
32021  *
32022  * Originally Released Under LGPL - original licence link has changed is not relivant.
32023  *
32024  * Fork - LGPL
32025  * <script type="text/javascript">
32026  */
32027  
32028 /**
32029  * @class Roo.tree.AsyncTreeNode
32030  * @extends Roo.tree.TreeNode
32031  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32032  * @constructor
32033  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32034  */
32035  Roo.tree.AsyncTreeNode = function(config){
32036     this.loaded = false;
32037     this.loading = false;
32038     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32039     /**
32040     * @event beforeload
32041     * Fires before this node is loaded, return false to cancel
32042     * @param {Node} this This node
32043     */
32044     this.addEvents({'beforeload':true, 'load': true});
32045     /**
32046     * @event load
32047     * Fires when this node is loaded
32048     * @param {Node} this This node
32049     */
32050     /**
32051      * The loader used by this node (defaults to using the tree's defined loader)
32052      * @type TreeLoader
32053      * @property loader
32054      */
32055 };
32056 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32057     expand : function(deep, anim, callback){
32058         if(this.loading){ // if an async load is already running, waiting til it's done
32059             var timer;
32060             var f = function(){
32061                 if(!this.loading){ // done loading
32062                     clearInterval(timer);
32063                     this.expand(deep, anim, callback);
32064                 }
32065             }.createDelegate(this);
32066             timer = setInterval(f, 200);
32067             return;
32068         }
32069         if(!this.loaded){
32070             if(this.fireEvent("beforeload", this) === false){
32071                 return;
32072             }
32073             this.loading = true;
32074             this.ui.beforeLoad(this);
32075             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32076             if(loader){
32077                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32078                 return;
32079             }
32080         }
32081         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32082     },
32083     
32084     /**
32085      * Returns true if this node is currently loading
32086      * @return {Boolean}
32087      */
32088     isLoading : function(){
32089         return this.loading;  
32090     },
32091     
32092     loadComplete : function(deep, anim, callback){
32093         this.loading = false;
32094         this.loaded = true;
32095         this.ui.afterLoad(this);
32096         this.fireEvent("load", this);
32097         this.expand(deep, anim, callback);
32098     },
32099     
32100     /**
32101      * Returns true if this node has been loaded
32102      * @return {Boolean}
32103      */
32104     isLoaded : function(){
32105         return this.loaded;
32106     },
32107     
32108     hasChildNodes : function(){
32109         if(!this.isLeaf() && !this.loaded){
32110             return true;
32111         }else{
32112             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32113         }
32114     },
32115
32116     /**
32117      * Trigger a reload for this node
32118      * @param {Function} callback
32119      */
32120     reload : function(callback){
32121         this.collapse(false, false);
32122         while(this.firstChild){
32123             this.removeChild(this.firstChild);
32124         }
32125         this.childrenRendered = false;
32126         this.loaded = false;
32127         if(this.isHiddenRoot()){
32128             this.expanded = false;
32129         }
32130         this.expand(false, false, callback);
32131     }
32132 });/*
32133  * Based on:
32134  * Ext JS Library 1.1.1
32135  * Copyright(c) 2006-2007, Ext JS, LLC.
32136  *
32137  * Originally Released Under LGPL - original licence link has changed is not relivant.
32138  *
32139  * Fork - LGPL
32140  * <script type="text/javascript">
32141  */
32142  
32143 /**
32144  * @class Roo.tree.TreeNodeUI
32145  * @constructor
32146  * @param {Object} node The node to render
32147  * The TreeNode UI implementation is separate from the
32148  * tree implementation. Unless you are customizing the tree UI,
32149  * you should never have to use this directly.
32150  */
32151 Roo.tree.TreeNodeUI = function(node){
32152     this.node = node;
32153     this.rendered = false;
32154     this.animating = false;
32155     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32156 };
32157
32158 Roo.tree.TreeNodeUI.prototype = {
32159     removeChild : function(node){
32160         if(this.rendered){
32161             this.ctNode.removeChild(node.ui.getEl());
32162         }
32163     },
32164
32165     beforeLoad : function(){
32166          this.addClass("x-tree-node-loading");
32167     },
32168
32169     afterLoad : function(){
32170          this.removeClass("x-tree-node-loading");
32171     },
32172
32173     onTextChange : function(node, text, oldText){
32174         if(this.rendered){
32175             this.textNode.innerHTML = text;
32176         }
32177     },
32178
32179     onDisableChange : function(node, state){
32180         this.disabled = state;
32181         if(state){
32182             this.addClass("x-tree-node-disabled");
32183         }else{
32184             this.removeClass("x-tree-node-disabled");
32185         }
32186     },
32187
32188     onSelectedChange : function(state){
32189         if(state){
32190             this.focus();
32191             this.addClass("x-tree-selected");
32192         }else{
32193             //this.blur();
32194             this.removeClass("x-tree-selected");
32195         }
32196     },
32197
32198     onMove : function(tree, node, oldParent, newParent, index, refNode){
32199         this.childIndent = null;
32200         if(this.rendered){
32201             var targetNode = newParent.ui.getContainer();
32202             if(!targetNode){//target not rendered
32203                 this.holder = document.createElement("div");
32204                 this.holder.appendChild(this.wrap);
32205                 return;
32206             }
32207             var insertBefore = refNode ? refNode.ui.getEl() : null;
32208             if(insertBefore){
32209                 targetNode.insertBefore(this.wrap, insertBefore);
32210             }else{
32211                 targetNode.appendChild(this.wrap);
32212             }
32213             this.node.renderIndent(true);
32214         }
32215     },
32216
32217     addClass : function(cls){
32218         if(this.elNode){
32219             Roo.fly(this.elNode).addClass(cls);
32220         }
32221     },
32222
32223     removeClass : function(cls){
32224         if(this.elNode){
32225             Roo.fly(this.elNode).removeClass(cls);
32226         }
32227     },
32228
32229     remove : function(){
32230         if(this.rendered){
32231             this.holder = document.createElement("div");
32232             this.holder.appendChild(this.wrap);
32233         }
32234     },
32235
32236     fireEvent : function(){
32237         return this.node.fireEvent.apply(this.node, arguments);
32238     },
32239
32240     initEvents : function(){
32241         this.node.on("move", this.onMove, this);
32242         var E = Roo.EventManager;
32243         var a = this.anchor;
32244
32245         var el = Roo.fly(a, '_treeui');
32246
32247         if(Roo.isOpera){ // opera render bug ignores the CSS
32248             el.setStyle("text-decoration", "none");
32249         }
32250
32251         el.on("click", this.onClick, this);
32252         el.on("dblclick", this.onDblClick, this);
32253
32254         if(this.checkbox){
32255             Roo.EventManager.on(this.checkbox,
32256                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32257         }
32258
32259         el.on("contextmenu", this.onContextMenu, this);
32260
32261         var icon = Roo.fly(this.iconNode);
32262         icon.on("click", this.onClick, this);
32263         icon.on("dblclick", this.onDblClick, this);
32264         icon.on("contextmenu", this.onContextMenu, this);
32265         E.on(this.ecNode, "click", this.ecClick, this, true);
32266
32267         if(this.node.disabled){
32268             this.addClass("x-tree-node-disabled");
32269         }
32270         if(this.node.hidden){
32271             this.addClass("x-tree-node-disabled");
32272         }
32273         var ot = this.node.getOwnerTree();
32274         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32275         if(dd && (!this.node.isRoot || ot.rootVisible)){
32276             Roo.dd.Registry.register(this.elNode, {
32277                 node: this.node,
32278                 handles: this.getDDHandles(),
32279                 isHandle: false
32280             });
32281         }
32282     },
32283
32284     getDDHandles : function(){
32285         return [this.iconNode, this.textNode];
32286     },
32287
32288     hide : function(){
32289         if(this.rendered){
32290             this.wrap.style.display = "none";
32291         }
32292     },
32293
32294     show : function(){
32295         if(this.rendered){
32296             this.wrap.style.display = "";
32297         }
32298     },
32299
32300     onContextMenu : function(e){
32301         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32302             e.preventDefault();
32303             this.focus();
32304             this.fireEvent("contextmenu", this.node, e);
32305         }
32306     },
32307
32308     onClick : function(e){
32309         if(this.dropping){
32310             e.stopEvent();
32311             return;
32312         }
32313         if(this.fireEvent("beforeclick", this.node, e) !== false){
32314             if(!this.disabled && this.node.attributes.href){
32315                 this.fireEvent("click", this.node, e);
32316                 return;
32317             }
32318             e.preventDefault();
32319             if(this.disabled){
32320                 return;
32321             }
32322
32323             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32324                 this.node.toggle();
32325             }
32326
32327             this.fireEvent("click", this.node, e);
32328         }else{
32329             e.stopEvent();
32330         }
32331     },
32332
32333     onDblClick : function(e){
32334         e.preventDefault();
32335         if(this.disabled){
32336             return;
32337         }
32338         if(this.checkbox){
32339             this.toggleCheck();
32340         }
32341         if(!this.animating && this.node.hasChildNodes()){
32342             this.node.toggle();
32343         }
32344         this.fireEvent("dblclick", this.node, e);
32345     },
32346
32347     onCheckChange : function(){
32348         var checked = this.checkbox.checked;
32349         this.node.attributes.checked = checked;
32350         this.fireEvent('checkchange', this.node, checked);
32351     },
32352
32353     ecClick : function(e){
32354         if(!this.animating && this.node.hasChildNodes()){
32355             this.node.toggle();
32356         }
32357     },
32358
32359     startDrop : function(){
32360         this.dropping = true;
32361     },
32362
32363     // delayed drop so the click event doesn't get fired on a drop
32364     endDrop : function(){
32365        setTimeout(function(){
32366            this.dropping = false;
32367        }.createDelegate(this), 50);
32368     },
32369
32370     expand : function(){
32371         this.updateExpandIcon();
32372         this.ctNode.style.display = "";
32373     },
32374
32375     focus : function(){
32376         if(!this.node.preventHScroll){
32377             try{this.anchor.focus();
32378             }catch(e){}
32379         }else if(!Roo.isIE){
32380             try{
32381                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32382                 var l = noscroll.scrollLeft;
32383                 this.anchor.focus();
32384                 noscroll.scrollLeft = l;
32385             }catch(e){}
32386         }
32387     },
32388
32389     toggleCheck : function(value){
32390         var cb = this.checkbox;
32391         if(cb){
32392             cb.checked = (value === undefined ? !cb.checked : value);
32393         }
32394     },
32395
32396     blur : function(){
32397         try{
32398             this.anchor.blur();
32399         }catch(e){}
32400     },
32401
32402     animExpand : function(callback){
32403         var ct = Roo.get(this.ctNode);
32404         ct.stopFx();
32405         if(!this.node.hasChildNodes()){
32406             this.updateExpandIcon();
32407             this.ctNode.style.display = "";
32408             Roo.callback(callback);
32409             return;
32410         }
32411         this.animating = true;
32412         this.updateExpandIcon();
32413
32414         ct.slideIn('t', {
32415            callback : function(){
32416                this.animating = false;
32417                Roo.callback(callback);
32418             },
32419             scope: this,
32420             duration: this.node.ownerTree.duration || .25
32421         });
32422     },
32423
32424     highlight : function(){
32425         var tree = this.node.getOwnerTree();
32426         Roo.fly(this.wrap).highlight(
32427             tree.hlColor || "C3DAF9",
32428             {endColor: tree.hlBaseColor}
32429         );
32430     },
32431
32432     collapse : function(){
32433         this.updateExpandIcon();
32434         this.ctNode.style.display = "none";
32435     },
32436
32437     animCollapse : function(callback){
32438         var ct = Roo.get(this.ctNode);
32439         ct.enableDisplayMode('block');
32440         ct.stopFx();
32441
32442         this.animating = true;
32443         this.updateExpandIcon();
32444
32445         ct.slideOut('t', {
32446             callback : function(){
32447                this.animating = false;
32448                Roo.callback(callback);
32449             },
32450             scope: this,
32451             duration: this.node.ownerTree.duration || .25
32452         });
32453     },
32454
32455     getContainer : function(){
32456         return this.ctNode;
32457     },
32458
32459     getEl : function(){
32460         return this.wrap;
32461     },
32462
32463     appendDDGhost : function(ghostNode){
32464         ghostNode.appendChild(this.elNode.cloneNode(true));
32465     },
32466
32467     getDDRepairXY : function(){
32468         return Roo.lib.Dom.getXY(this.iconNode);
32469     },
32470
32471     onRender : function(){
32472         this.render();
32473     },
32474
32475     render : function(bulkRender){
32476         var n = this.node, a = n.attributes;
32477         var targetNode = n.parentNode ?
32478               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32479
32480         if(!this.rendered){
32481             this.rendered = true;
32482
32483             this.renderElements(n, a, targetNode, bulkRender);
32484
32485             if(a.qtip){
32486                if(this.textNode.setAttributeNS){
32487                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32488                    if(a.qtipTitle){
32489                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32490                    }
32491                }else{
32492                    this.textNode.setAttribute("ext:qtip", a.qtip);
32493                    if(a.qtipTitle){
32494                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32495                    }
32496                }
32497             }else if(a.qtipCfg){
32498                 a.qtipCfg.target = Roo.id(this.textNode);
32499                 Roo.QuickTips.register(a.qtipCfg);
32500             }
32501             this.initEvents();
32502             if(!this.node.expanded){
32503                 this.updateExpandIcon();
32504             }
32505         }else{
32506             if(bulkRender === true) {
32507                 targetNode.appendChild(this.wrap);
32508             }
32509         }
32510     },
32511
32512     renderElements : function(n, a, targetNode, bulkRender){
32513         // add some indent caching, this helps performance when rendering a large tree
32514         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32515         var t = n.getOwnerTree();
32516         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32517         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32518         var cb = typeof a.checked == 'boolean';
32519         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32520         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32521             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32522             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32523             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32524             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32525             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32526              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32527                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32528             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32529             "</li>"];
32530
32531         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32532             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32533                                 n.nextSibling.ui.getEl(), buf.join(""));
32534         }else{
32535             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32536         }
32537
32538         this.elNode = this.wrap.childNodes[0];
32539         this.ctNode = this.wrap.childNodes[1];
32540         var cs = this.elNode.childNodes;
32541         this.indentNode = cs[0];
32542         this.ecNode = cs[1];
32543         this.iconNode = cs[2];
32544         var index = 3;
32545         if(cb){
32546             this.checkbox = cs[3];
32547             index++;
32548         }
32549         this.anchor = cs[index];
32550         this.textNode = cs[index].firstChild;
32551     },
32552
32553     getAnchor : function(){
32554         return this.anchor;
32555     },
32556
32557     getTextEl : function(){
32558         return this.textNode;
32559     },
32560
32561     getIconEl : function(){
32562         return this.iconNode;
32563     },
32564
32565     isChecked : function(){
32566         return this.checkbox ? this.checkbox.checked : false;
32567     },
32568
32569     updateExpandIcon : function(){
32570         if(this.rendered){
32571             var n = this.node, c1, c2;
32572             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32573             var hasChild = n.hasChildNodes();
32574             if(hasChild){
32575                 if(n.expanded){
32576                     cls += "-minus";
32577                     c1 = "x-tree-node-collapsed";
32578                     c2 = "x-tree-node-expanded";
32579                 }else{
32580                     cls += "-plus";
32581                     c1 = "x-tree-node-expanded";
32582                     c2 = "x-tree-node-collapsed";
32583                 }
32584                 if(this.wasLeaf){
32585                     this.removeClass("x-tree-node-leaf");
32586                     this.wasLeaf = false;
32587                 }
32588                 if(this.c1 != c1 || this.c2 != c2){
32589                     Roo.fly(this.elNode).replaceClass(c1, c2);
32590                     this.c1 = c1; this.c2 = c2;
32591                 }
32592             }else{
32593                 if(!this.wasLeaf){
32594                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32595                     delete this.c1;
32596                     delete this.c2;
32597                     this.wasLeaf = true;
32598                 }
32599             }
32600             var ecc = "x-tree-ec-icon "+cls;
32601             if(this.ecc != ecc){
32602                 this.ecNode.className = ecc;
32603                 this.ecc = ecc;
32604             }
32605         }
32606     },
32607
32608     getChildIndent : function(){
32609         if(!this.childIndent){
32610             var buf = [];
32611             var p = this.node;
32612             while(p){
32613                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32614                     if(!p.isLast()) {
32615                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32616                     } else {
32617                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32618                     }
32619                 }
32620                 p = p.parentNode;
32621             }
32622             this.childIndent = buf.join("");
32623         }
32624         return this.childIndent;
32625     },
32626
32627     renderIndent : function(){
32628         if(this.rendered){
32629             var indent = "";
32630             var p = this.node.parentNode;
32631             if(p){
32632                 indent = p.ui.getChildIndent();
32633             }
32634             if(this.indentMarkup != indent){ // don't rerender if not required
32635                 this.indentNode.innerHTML = indent;
32636                 this.indentMarkup = indent;
32637             }
32638             this.updateExpandIcon();
32639         }
32640     }
32641 };
32642
32643 Roo.tree.RootTreeNodeUI = function(){
32644     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32645 };
32646 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32647     render : function(){
32648         if(!this.rendered){
32649             var targetNode = this.node.ownerTree.innerCt.dom;
32650             this.node.expanded = true;
32651             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32652             this.wrap = this.ctNode = targetNode.firstChild;
32653         }
32654     },
32655     collapse : function(){
32656     },
32657     expand : function(){
32658     }
32659 });/*
32660  * Based on:
32661  * Ext JS Library 1.1.1
32662  * Copyright(c) 2006-2007, Ext JS, LLC.
32663  *
32664  * Originally Released Under LGPL - original licence link has changed is not relivant.
32665  *
32666  * Fork - LGPL
32667  * <script type="text/javascript">
32668  */
32669 /**
32670  * @class Roo.tree.TreeLoader
32671  * @extends Roo.util.Observable
32672  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32673  * nodes from a specified URL. The response must be a javascript Array definition
32674  * who's elements are node definition objects. eg:
32675  * <pre><code>
32676    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32677     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32678 </code></pre>
32679  * <br><br>
32680  * A server request is sent, and child nodes are loaded only when a node is expanded.
32681  * The loading node's id is passed to the server under the parameter name "node" to
32682  * enable the server to produce the correct child nodes.
32683  * <br><br>
32684  * To pass extra parameters, an event handler may be attached to the "beforeload"
32685  * event, and the parameters specified in the TreeLoader's baseParams property:
32686  * <pre><code>
32687     myTreeLoader.on("beforeload", function(treeLoader, node) {
32688         this.baseParams.category = node.attributes.category;
32689     }, this);
32690 </code></pre><
32691  * This would pass an HTTP parameter called "category" to the server containing
32692  * the value of the Node's "category" attribute.
32693  * @constructor
32694  * Creates a new Treeloader.
32695  * @param {Object} config A config object containing config properties.
32696  */
32697 Roo.tree.TreeLoader = function(config){
32698     this.baseParams = {};
32699     this.requestMethod = "POST";
32700     Roo.apply(this, config);
32701
32702     this.addEvents({
32703     
32704         /**
32705          * @event beforeload
32706          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32707          * @param {Object} This TreeLoader object.
32708          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32709          * @param {Object} callback The callback function specified in the {@link #load} call.
32710          */
32711         beforeload : true,
32712         /**
32713          * @event load
32714          * Fires when the node has been successfuly loaded.
32715          * @param {Object} This TreeLoader object.
32716          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32717          * @param {Object} response The response object containing the data from the server.
32718          */
32719         load : true,
32720         /**
32721          * @event loadexception
32722          * Fires if the network request failed.
32723          * @param {Object} This TreeLoader object.
32724          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32725          * @param {Object} response The response object containing the data from the server.
32726          */
32727         loadexception : true,
32728         /**
32729          * @event create
32730          * Fires before a node is created, enabling you to return custom Node types 
32731          * @param {Object} This TreeLoader object.
32732          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32733          */
32734         create : true
32735     });
32736
32737     Roo.tree.TreeLoader.superclass.constructor.call(this);
32738 };
32739
32740 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32741     /**
32742     * @cfg {String} dataUrl The URL from which to request a Json string which
32743     * specifies an array of node definition object representing the child nodes
32744     * to be loaded.
32745     */
32746     /**
32747     * @cfg {Object} baseParams (optional) An object containing properties which
32748     * specify HTTP parameters to be passed to each request for child nodes.
32749     */
32750     /**
32751     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32752     * created by this loader. If the attributes sent by the server have an attribute in this object,
32753     * they take priority.
32754     */
32755     /**
32756     * @cfg {Object} uiProviders (optional) An object containing properties which
32757     * 
32758     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32759     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32760     * <i>uiProvider</i> attribute of a returned child node is a string rather
32761     * than a reference to a TreeNodeUI implementation, this that string value
32762     * is used as a property name in the uiProviders object. You can define the provider named
32763     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32764     */
32765     uiProviders : {},
32766
32767     /**
32768     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32769     * child nodes before loading.
32770     */
32771     clearOnLoad : true,
32772
32773     /**
32774     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32775     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32776     * Grid query { data : [ .....] }
32777     */
32778     
32779     root : false,
32780      /**
32781     * @cfg {String} queryParam (optional) 
32782     * Name of the query as it will be passed on the querystring (defaults to 'node')
32783     * eg. the request will be ?node=[id]
32784     */
32785     
32786     
32787     queryParam: false,
32788     
32789     /**
32790      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32791      * This is called automatically when a node is expanded, but may be used to reload
32792      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32793      * @param {Roo.tree.TreeNode} node
32794      * @param {Function} callback
32795      */
32796     load : function(node, callback){
32797         if(this.clearOnLoad){
32798             while(node.firstChild){
32799                 node.removeChild(node.firstChild);
32800             }
32801         }
32802         if(node.attributes.children){ // preloaded json children
32803             var cs = node.attributes.children;
32804             for(var i = 0, len = cs.length; i < len; i++){
32805                 node.appendChild(this.createNode(cs[i]));
32806             }
32807             if(typeof callback == "function"){
32808                 callback();
32809             }
32810         }else if(this.dataUrl){
32811             this.requestData(node, callback);
32812         }
32813     },
32814
32815     getParams: function(node){
32816         var buf = [], bp = this.baseParams;
32817         for(var key in bp){
32818             if(typeof bp[key] != "function"){
32819                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32820             }
32821         }
32822         var n = this.queryParam === false ? 'node' : this.queryParam;
32823         buf.push(n + "=", encodeURIComponent(node.id));
32824         return buf.join("");
32825     },
32826
32827     requestData : function(node, callback){
32828         if(this.fireEvent("beforeload", this, node, callback) !== false){
32829             this.transId = Roo.Ajax.request({
32830                 method:this.requestMethod,
32831                 url: this.dataUrl||this.url,
32832                 success: this.handleResponse,
32833                 failure: this.handleFailure,
32834                 scope: this,
32835                 argument: {callback: callback, node: node},
32836                 params: this.getParams(node)
32837             });
32838         }else{
32839             // if the load is cancelled, make sure we notify
32840             // the node that we are done
32841             if(typeof callback == "function"){
32842                 callback();
32843             }
32844         }
32845     },
32846
32847     isLoading : function(){
32848         return this.transId ? true : false;
32849     },
32850
32851     abort : function(){
32852         if(this.isLoading()){
32853             Roo.Ajax.abort(this.transId);
32854         }
32855     },
32856
32857     // private
32858     createNode : function(attr){
32859         // apply baseAttrs, nice idea Corey!
32860         if(this.baseAttrs){
32861             Roo.applyIf(attr, this.baseAttrs);
32862         }
32863         if(this.applyLoader !== false){
32864             attr.loader = this;
32865         }
32866         // uiProvider = depreciated..
32867         
32868         if(typeof(attr.uiProvider) == 'string'){
32869            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32870                 /**  eval:var:attr */ eval(attr.uiProvider);
32871         }
32872         if(typeof(this.uiProviders['default']) != 'undefined') {
32873             attr.uiProvider = this.uiProviders['default'];
32874         }
32875         
32876         this.fireEvent('create', this, attr);
32877         
32878         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32879         return(attr.leaf ?
32880                         new Roo.tree.TreeNode(attr) :
32881                         new Roo.tree.AsyncTreeNode(attr));
32882     },
32883
32884     processResponse : function(response, node, callback){
32885         var json = response.responseText;
32886         try {
32887             
32888             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32889             if (this.root !== false) {
32890                 o = o[this.root];
32891             }
32892             
32893             for(var i = 0, len = o.length; i < len; i++){
32894                 var n = this.createNode(o[i]);
32895                 if(n){
32896                     node.appendChild(n);
32897                 }
32898             }
32899             if(typeof callback == "function"){
32900                 callback(this, node);
32901             }
32902         }catch(e){
32903             this.handleFailure(response);
32904         }
32905     },
32906
32907     handleResponse : function(response){
32908         this.transId = false;
32909         var a = response.argument;
32910         this.processResponse(response, a.node, a.callback);
32911         this.fireEvent("load", this, a.node, response);
32912     },
32913
32914     handleFailure : function(response){
32915         this.transId = false;
32916         var a = response.argument;
32917         this.fireEvent("loadexception", this, a.node, response);
32918         if(typeof a.callback == "function"){
32919             a.callback(this, a.node);
32920         }
32921     }
32922 });/*
32923  * Based on:
32924  * Ext JS Library 1.1.1
32925  * Copyright(c) 2006-2007, Ext JS, LLC.
32926  *
32927  * Originally Released Under LGPL - original licence link has changed is not relivant.
32928  *
32929  * Fork - LGPL
32930  * <script type="text/javascript">
32931  */
32932
32933 /**
32934 * @class Roo.tree.TreeFilter
32935 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32936 * @param {TreePanel} tree
32937 * @param {Object} config (optional)
32938  */
32939 Roo.tree.TreeFilter = function(tree, config){
32940     this.tree = tree;
32941     this.filtered = {};
32942     Roo.apply(this, config);
32943 };
32944
32945 Roo.tree.TreeFilter.prototype = {
32946     clearBlank:false,
32947     reverse:false,
32948     autoClear:false,
32949     remove:false,
32950
32951      /**
32952      * Filter the data by a specific attribute.
32953      * @param {String/RegExp} value Either string that the attribute value
32954      * should start with or a RegExp to test against the attribute
32955      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32956      * @param {TreeNode} startNode (optional) The node to start the filter at.
32957      */
32958     filter : function(value, attr, startNode){
32959         attr = attr || "text";
32960         var f;
32961         if(typeof value == "string"){
32962             var vlen = value.length;
32963             // auto clear empty filter
32964             if(vlen == 0 && this.clearBlank){
32965                 this.clear();
32966                 return;
32967             }
32968             value = value.toLowerCase();
32969             f = function(n){
32970                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32971             };
32972         }else if(value.exec){ // regex?
32973             f = function(n){
32974                 return value.test(n.attributes[attr]);
32975             };
32976         }else{
32977             throw 'Illegal filter type, must be string or regex';
32978         }
32979         this.filterBy(f, null, startNode);
32980         },
32981
32982     /**
32983      * Filter by a function. The passed function will be called with each
32984      * node in the tree (or from the startNode). If the function returns true, the node is kept
32985      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32986      * @param {Function} fn The filter function
32987      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32988      */
32989     filterBy : function(fn, scope, startNode){
32990         startNode = startNode || this.tree.root;
32991         if(this.autoClear){
32992             this.clear();
32993         }
32994         var af = this.filtered, rv = this.reverse;
32995         var f = function(n){
32996             if(n == startNode){
32997                 return true;
32998             }
32999             if(af[n.id]){
33000                 return false;
33001             }
33002             var m = fn.call(scope || n, n);
33003             if(!m || rv){
33004                 af[n.id] = n;
33005                 n.ui.hide();
33006                 return false;
33007             }
33008             return true;
33009         };
33010         startNode.cascade(f);
33011         if(this.remove){
33012            for(var id in af){
33013                if(typeof id != "function"){
33014                    var n = af[id];
33015                    if(n && n.parentNode){
33016                        n.parentNode.removeChild(n);
33017                    }
33018                }
33019            }
33020         }
33021     },
33022
33023     /**
33024      * Clears the current filter. Note: with the "remove" option
33025      * set a filter cannot be cleared.
33026      */
33027     clear : function(){
33028         var t = this.tree;
33029         var af = this.filtered;
33030         for(var id in af){
33031             if(typeof id != "function"){
33032                 var n = af[id];
33033                 if(n){
33034                     n.ui.show();
33035                 }
33036             }
33037         }
33038         this.filtered = {};
33039     }
33040 };
33041 /*
33042  * Based on:
33043  * Ext JS Library 1.1.1
33044  * Copyright(c) 2006-2007, Ext JS, LLC.
33045  *
33046  * Originally Released Under LGPL - original licence link has changed is not relivant.
33047  *
33048  * Fork - LGPL
33049  * <script type="text/javascript">
33050  */
33051  
33052
33053 /**
33054  * @class Roo.tree.TreeSorter
33055  * Provides sorting of nodes in a TreePanel
33056  * 
33057  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33058  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33059  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33060  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33061  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33062  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33063  * @constructor
33064  * @param {TreePanel} tree
33065  * @param {Object} config
33066  */
33067 Roo.tree.TreeSorter = function(tree, config){
33068     Roo.apply(this, config);
33069     tree.on("beforechildrenrendered", this.doSort, this);
33070     tree.on("append", this.updateSort, this);
33071     tree.on("insert", this.updateSort, this);
33072     
33073     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33074     var p = this.property || "text";
33075     var sortType = this.sortType;
33076     var fs = this.folderSort;
33077     var cs = this.caseSensitive === true;
33078     var leafAttr = this.leafAttr || 'leaf';
33079
33080     this.sortFn = function(n1, n2){
33081         if(fs){
33082             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33083                 return 1;
33084             }
33085             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33086                 return -1;
33087             }
33088         }
33089         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33090         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33091         if(v1 < v2){
33092                         return dsc ? +1 : -1;
33093                 }else if(v1 > v2){
33094                         return dsc ? -1 : +1;
33095         }else{
33096                 return 0;
33097         }
33098     };
33099 };
33100
33101 Roo.tree.TreeSorter.prototype = {
33102     doSort : function(node){
33103         node.sort(this.sortFn);
33104     },
33105     
33106     compareNodes : function(n1, n2){
33107         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33108     },
33109     
33110     updateSort : function(tree, node){
33111         if(node.childrenRendered){
33112             this.doSort.defer(1, this, [node]);
33113         }
33114     }
33115 };/*
33116  * Based on:
33117  * Ext JS Library 1.1.1
33118  * Copyright(c) 2006-2007, Ext JS, LLC.
33119  *
33120  * Originally Released Under LGPL - original licence link has changed is not relivant.
33121  *
33122  * Fork - LGPL
33123  * <script type="text/javascript">
33124  */
33125
33126 if(Roo.dd.DropZone){
33127     
33128 Roo.tree.TreeDropZone = function(tree, config){
33129     this.allowParentInsert = false;
33130     this.allowContainerDrop = false;
33131     this.appendOnly = false;
33132     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33133     this.tree = tree;
33134     this.lastInsertClass = "x-tree-no-status";
33135     this.dragOverData = {};
33136 };
33137
33138 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33139     ddGroup : "TreeDD",
33140     
33141     expandDelay : 1000,
33142     
33143     expandNode : function(node){
33144         if(node.hasChildNodes() && !node.isExpanded()){
33145             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33146         }
33147     },
33148     
33149     queueExpand : function(node){
33150         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33151     },
33152     
33153     cancelExpand : function(){
33154         if(this.expandProcId){
33155             clearTimeout(this.expandProcId);
33156             this.expandProcId = false;
33157         }
33158     },
33159     
33160     isValidDropPoint : function(n, pt, dd, e, data){
33161         if(!n || !data){ return false; }
33162         var targetNode = n.node;
33163         var dropNode = data.node;
33164         // default drop rules
33165         if(!(targetNode && targetNode.isTarget && pt)){
33166             return false;
33167         }
33168         if(pt == "append" && targetNode.allowChildren === false){
33169             return false;
33170         }
33171         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33172             return false;
33173         }
33174         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33175             return false;
33176         }
33177         // reuse the object
33178         var overEvent = this.dragOverData;
33179         overEvent.tree = this.tree;
33180         overEvent.target = targetNode;
33181         overEvent.data = data;
33182         overEvent.point = pt;
33183         overEvent.source = dd;
33184         overEvent.rawEvent = e;
33185         overEvent.dropNode = dropNode;
33186         overEvent.cancel = false;  
33187         var result = this.tree.fireEvent("nodedragover", overEvent);
33188         return overEvent.cancel === false && result !== false;
33189     },
33190     
33191     getDropPoint : function(e, n, dd){
33192         var tn = n.node;
33193         if(tn.isRoot){
33194             return tn.allowChildren !== false ? "append" : false; // always append for root
33195         }
33196         var dragEl = n.ddel;
33197         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33198         var y = Roo.lib.Event.getPageY(e);
33199         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33200         
33201         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33202         var noAppend = tn.allowChildren === false;
33203         if(this.appendOnly || tn.parentNode.allowChildren === false){
33204             return noAppend ? false : "append";
33205         }
33206         var noBelow = false;
33207         if(!this.allowParentInsert){
33208             noBelow = tn.hasChildNodes() && tn.isExpanded();
33209         }
33210         var q = (b - t) / (noAppend ? 2 : 3);
33211         if(y >= t && y < (t + q)){
33212             return "above";
33213         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33214             return "below";
33215         }else{
33216             return "append";
33217         }
33218     },
33219     
33220     onNodeEnter : function(n, dd, e, data){
33221         this.cancelExpand();
33222     },
33223     
33224     onNodeOver : function(n, dd, e, data){
33225         var pt = this.getDropPoint(e, n, dd);
33226         var node = n.node;
33227         
33228         // auto node expand check
33229         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33230             this.queueExpand(node);
33231         }else if(pt != "append"){
33232             this.cancelExpand();
33233         }
33234         
33235         // set the insert point style on the target node
33236         var returnCls = this.dropNotAllowed;
33237         if(this.isValidDropPoint(n, pt, dd, e, data)){
33238            if(pt){
33239                var el = n.ddel;
33240                var cls;
33241                if(pt == "above"){
33242                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33243                    cls = "x-tree-drag-insert-above";
33244                }else if(pt == "below"){
33245                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33246                    cls = "x-tree-drag-insert-below";
33247                }else{
33248                    returnCls = "x-tree-drop-ok-append";
33249                    cls = "x-tree-drag-append";
33250                }
33251                if(this.lastInsertClass != cls){
33252                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33253                    this.lastInsertClass = cls;
33254                }
33255            }
33256        }
33257        return returnCls;
33258     },
33259     
33260     onNodeOut : function(n, dd, e, data){
33261         this.cancelExpand();
33262         this.removeDropIndicators(n);
33263     },
33264     
33265     onNodeDrop : function(n, dd, e, data){
33266         var point = this.getDropPoint(e, n, dd);
33267         var targetNode = n.node;
33268         targetNode.ui.startDrop();
33269         if(!this.isValidDropPoint(n, point, dd, e, data)){
33270             targetNode.ui.endDrop();
33271             return false;
33272         }
33273         // first try to find the drop node
33274         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33275         var dropEvent = {
33276             tree : this.tree,
33277             target: targetNode,
33278             data: data,
33279             point: point,
33280             source: dd,
33281             rawEvent: e,
33282             dropNode: dropNode,
33283             cancel: !dropNode   
33284         };
33285         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33286         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33287             targetNode.ui.endDrop();
33288             return false;
33289         }
33290         // allow target changing
33291         targetNode = dropEvent.target;
33292         if(point == "append" && !targetNode.isExpanded()){
33293             targetNode.expand(false, null, function(){
33294                 this.completeDrop(dropEvent);
33295             }.createDelegate(this));
33296         }else{
33297             this.completeDrop(dropEvent);
33298         }
33299         return true;
33300     },
33301     
33302     completeDrop : function(de){
33303         var ns = de.dropNode, p = de.point, t = de.target;
33304         if(!(ns instanceof Array)){
33305             ns = [ns];
33306         }
33307         var n;
33308         for(var i = 0, len = ns.length; i < len; i++){
33309             n = ns[i];
33310             if(p == "above"){
33311                 t.parentNode.insertBefore(n, t);
33312             }else if(p == "below"){
33313                 t.parentNode.insertBefore(n, t.nextSibling);
33314             }else{
33315                 t.appendChild(n);
33316             }
33317         }
33318         n.ui.focus();
33319         if(this.tree.hlDrop){
33320             n.ui.highlight();
33321         }
33322         t.ui.endDrop();
33323         this.tree.fireEvent("nodedrop", de);
33324     },
33325     
33326     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33327         if(this.tree.hlDrop){
33328             dropNode.ui.focus();
33329             dropNode.ui.highlight();
33330         }
33331         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33332     },
33333     
33334     getTree : function(){
33335         return this.tree;
33336     },
33337     
33338     removeDropIndicators : function(n){
33339         if(n && n.ddel){
33340             var el = n.ddel;
33341             Roo.fly(el).removeClass([
33342                     "x-tree-drag-insert-above",
33343                     "x-tree-drag-insert-below",
33344                     "x-tree-drag-append"]);
33345             this.lastInsertClass = "_noclass";
33346         }
33347     },
33348     
33349     beforeDragDrop : function(target, e, id){
33350         this.cancelExpand();
33351         return true;
33352     },
33353     
33354     afterRepair : function(data){
33355         if(data && Roo.enableFx){
33356             data.node.ui.highlight();
33357         }
33358         this.hideProxy();
33359     }    
33360 });
33361
33362 }
33363 /*
33364  * Based on:
33365  * Ext JS Library 1.1.1
33366  * Copyright(c) 2006-2007, Ext JS, LLC.
33367  *
33368  * Originally Released Under LGPL - original licence link has changed is not relivant.
33369  *
33370  * Fork - LGPL
33371  * <script type="text/javascript">
33372  */
33373  
33374
33375 if(Roo.dd.DragZone){
33376 Roo.tree.TreeDragZone = function(tree, config){
33377     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33378     this.tree = tree;
33379 };
33380
33381 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33382     ddGroup : "TreeDD",
33383     
33384     onBeforeDrag : function(data, e){
33385         var n = data.node;
33386         return n && n.draggable && !n.disabled;
33387     },
33388     
33389     onInitDrag : function(e){
33390         var data = this.dragData;
33391         this.tree.getSelectionModel().select(data.node);
33392         this.proxy.update("");
33393         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33394         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33395     },
33396     
33397     getRepairXY : function(e, data){
33398         return data.node.ui.getDDRepairXY();
33399     },
33400     
33401     onEndDrag : function(data, e){
33402         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33403     },
33404     
33405     onValidDrop : function(dd, e, id){
33406         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33407         this.hideProxy();
33408     },
33409     
33410     beforeInvalidDrop : function(e, id){
33411         // this scrolls the original position back into view
33412         var sm = this.tree.getSelectionModel();
33413         sm.clearSelections();
33414         sm.select(this.dragData.node);
33415     }
33416 });
33417 }/*
33418  * Based on:
33419  * Ext JS Library 1.1.1
33420  * Copyright(c) 2006-2007, Ext JS, LLC.
33421  *
33422  * Originally Released Under LGPL - original licence link has changed is not relivant.
33423  *
33424  * Fork - LGPL
33425  * <script type="text/javascript">
33426  */
33427 /**
33428  * @class Roo.tree.TreeEditor
33429  * @extends Roo.Editor
33430  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33431  * as the editor field.
33432  * @constructor
33433  * @param {TreePanel} tree
33434  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33435  */
33436 Roo.tree.TreeEditor = function(tree, config){
33437     config = config || {};
33438     var field = config.events ? config : new Roo.form.TextField(config);
33439     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33440
33441     this.tree = tree;
33442
33443     tree.on('beforeclick', this.beforeNodeClick, this);
33444     tree.getTreeEl().on('mousedown', this.hide, this);
33445     this.on('complete', this.updateNode, this);
33446     this.on('beforestartedit', this.fitToTree, this);
33447     this.on('startedit', this.bindScroll, this, {delay:10});
33448     this.on('specialkey', this.onSpecialKey, this);
33449 };
33450
33451 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33452     /**
33453      * @cfg {String} alignment
33454      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33455      */
33456     alignment: "l-l",
33457     // inherit
33458     autoSize: false,
33459     /**
33460      * @cfg {Boolean} hideEl
33461      * True to hide the bound element while the editor is displayed (defaults to false)
33462      */
33463     hideEl : false,
33464     /**
33465      * @cfg {String} cls
33466      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33467      */
33468     cls: "x-small-editor x-tree-editor",
33469     /**
33470      * @cfg {Boolean} shim
33471      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33472      */
33473     shim:false,
33474     // inherit
33475     shadow:"frame",
33476     /**
33477      * @cfg {Number} maxWidth
33478      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33479      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33480      * scroll and client offsets into account prior to each edit.
33481      */
33482     maxWidth: 250,
33483
33484     editDelay : 350,
33485
33486     // private
33487     fitToTree : function(ed, el){
33488         var td = this.tree.getTreeEl().dom, nd = el.dom;
33489         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33490             td.scrollLeft = nd.offsetLeft;
33491         }
33492         var w = Math.min(
33493                 this.maxWidth,
33494                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33495         this.setSize(w, '');
33496     },
33497
33498     // private
33499     triggerEdit : function(node){
33500         this.completeEdit();
33501         this.editNode = node;
33502         this.startEdit(node.ui.textNode, node.text);
33503     },
33504
33505     // private
33506     bindScroll : function(){
33507         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33508     },
33509
33510     // private
33511     beforeNodeClick : function(node, e){
33512         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33513         this.lastClick = new Date();
33514         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33515             e.stopEvent();
33516             this.triggerEdit(node);
33517             return false;
33518         }
33519     },
33520
33521     // private
33522     updateNode : function(ed, value){
33523         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33524         this.editNode.setText(value);
33525     },
33526
33527     // private
33528     onHide : function(){
33529         Roo.tree.TreeEditor.superclass.onHide.call(this);
33530         if(this.editNode){
33531             this.editNode.ui.focus();
33532         }
33533     },
33534
33535     // private
33536     onSpecialKey : function(field, e){
33537         var k = e.getKey();
33538         if(k == e.ESC){
33539             e.stopEvent();
33540             this.cancelEdit();
33541         }else if(k == e.ENTER && !e.hasModifier()){
33542             e.stopEvent();
33543             this.completeEdit();
33544         }
33545     }
33546 });//<Script type="text/javascript">
33547 /*
33548  * Based on:
33549  * Ext JS Library 1.1.1
33550  * Copyright(c) 2006-2007, Ext JS, LLC.
33551  *
33552  * Originally Released Under LGPL - original licence link has changed is not relivant.
33553  *
33554  * Fork - LGPL
33555  * <script type="text/javascript">
33556  */
33557  
33558 /**
33559  * Not documented??? - probably should be...
33560  */
33561
33562 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33563     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33564     
33565     renderElements : function(n, a, targetNode, bulkRender){
33566         //consel.log("renderElements?");
33567         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33568
33569         var t = n.getOwnerTree();
33570         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33571         
33572         var cols = t.columns;
33573         var bw = t.borderWidth;
33574         var c = cols[0];
33575         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33576          var cb = typeof a.checked == "boolean";
33577         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33578         var colcls = 'x-t-' + tid + '-c0';
33579         var buf = [
33580             '<li class="x-tree-node">',
33581             
33582                 
33583                 '<div class="x-tree-node-el ', a.cls,'">',
33584                     // extran...
33585                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33586                 
33587                 
33588                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33589                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33590                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33591                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33592                            (a.iconCls ? ' '+a.iconCls : ''),
33593                            '" unselectable="on" />',
33594                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33595                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33596                              
33597                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33598                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33599                             '<span unselectable="on" qtip="' + tx + '">',
33600                              tx,
33601                              '</span></a>' ,
33602                     '</div>',
33603                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33604                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33605                  ];
33606         for(var i = 1, len = cols.length; i < len; i++){
33607             c = cols[i];
33608             colcls = 'x-t-' + tid + '-c' +i;
33609             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33610             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33611                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33612                       "</div>");
33613          }
33614          
33615          buf.push(
33616             '</a>',
33617             '<div class="x-clear"></div></div>',
33618             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33619             "</li>");
33620         
33621         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33622             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33623                                 n.nextSibling.ui.getEl(), buf.join(""));
33624         }else{
33625             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33626         }
33627         var el = this.wrap.firstChild;
33628         this.elRow = el;
33629         this.elNode = el.firstChild;
33630         this.ranchor = el.childNodes[1];
33631         this.ctNode = this.wrap.childNodes[1];
33632         var cs = el.firstChild.childNodes;
33633         this.indentNode = cs[0];
33634         this.ecNode = cs[1];
33635         this.iconNode = cs[2];
33636         var index = 3;
33637         if(cb){
33638             this.checkbox = cs[3];
33639             index++;
33640         }
33641         this.anchor = cs[index];
33642         
33643         this.textNode = cs[index].firstChild;
33644         
33645         //el.on("click", this.onClick, this);
33646         //el.on("dblclick", this.onDblClick, this);
33647         
33648         
33649        // console.log(this);
33650     },
33651     initEvents : function(){
33652         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33653         
33654             
33655         var a = this.ranchor;
33656
33657         var el = Roo.get(a);
33658
33659         if(Roo.isOpera){ // opera render bug ignores the CSS
33660             el.setStyle("text-decoration", "none");
33661         }
33662
33663         el.on("click", this.onClick, this);
33664         el.on("dblclick", this.onDblClick, this);
33665         el.on("contextmenu", this.onContextMenu, this);
33666         
33667     },
33668     
33669     /*onSelectedChange : function(state){
33670         if(state){
33671             this.focus();
33672             this.addClass("x-tree-selected");
33673         }else{
33674             //this.blur();
33675             this.removeClass("x-tree-selected");
33676         }
33677     },*/
33678     addClass : function(cls){
33679         if(this.elRow){
33680             Roo.fly(this.elRow).addClass(cls);
33681         }
33682         
33683     },
33684     
33685     
33686     removeClass : function(cls){
33687         if(this.elRow){
33688             Roo.fly(this.elRow).removeClass(cls);
33689         }
33690     }
33691
33692     
33693     
33694 });//<Script type="text/javascript">
33695
33696 /*
33697  * Based on:
33698  * Ext JS Library 1.1.1
33699  * Copyright(c) 2006-2007, Ext JS, LLC.
33700  *
33701  * Originally Released Under LGPL - original licence link has changed is not relivant.
33702  *
33703  * Fork - LGPL
33704  * <script type="text/javascript">
33705  */
33706  
33707
33708 /**
33709  * @class Roo.tree.ColumnTree
33710  * @extends Roo.data.TreePanel
33711  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33712  * @cfg {int} borderWidth  compined right/left border allowance
33713  * @constructor
33714  * @param {String/HTMLElement/Element} el The container element
33715  * @param {Object} config
33716  */
33717 Roo.tree.ColumnTree =  function(el, config)
33718 {
33719    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33720    this.addEvents({
33721         /**
33722         * @event resize
33723         * Fire this event on a container when it resizes
33724         * @param {int} w Width
33725         * @param {int} h Height
33726         */
33727        "resize" : true
33728     });
33729     this.on('resize', this.onResize, this);
33730 };
33731
33732 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33733     //lines:false,
33734     
33735     
33736     borderWidth: Roo.isBorderBox ? 0 : 2, 
33737     headEls : false,
33738     
33739     render : function(){
33740         // add the header.....
33741        
33742         Roo.tree.ColumnTree.superclass.render.apply(this);
33743         
33744         this.el.addClass('x-column-tree');
33745         
33746         this.headers = this.el.createChild(
33747             {cls:'x-tree-headers'},this.innerCt.dom);
33748    
33749         var cols = this.columns, c;
33750         var totalWidth = 0;
33751         this.headEls = [];
33752         var  len = cols.length;
33753         for(var i = 0; i < len; i++){
33754              c = cols[i];
33755              totalWidth += c.width;
33756             this.headEls.push(this.headers.createChild({
33757                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33758                  cn: {
33759                      cls:'x-tree-hd-text',
33760                      html: c.header
33761                  },
33762                  style:'width:'+(c.width-this.borderWidth)+'px;'
33763              }));
33764         }
33765         this.headers.createChild({cls:'x-clear'});
33766         // prevent floats from wrapping when clipped
33767         this.headers.setWidth(totalWidth);
33768         //this.innerCt.setWidth(totalWidth);
33769         this.innerCt.setStyle({ overflow: 'auto' });
33770         this.onResize(this.width, this.height);
33771              
33772         
33773     },
33774     onResize : function(w,h)
33775     {
33776         this.height = h;
33777         this.width = w;
33778         // resize cols..
33779         this.innerCt.setWidth(this.width);
33780         this.innerCt.setHeight(this.height-20);
33781         
33782         // headers...
33783         var cols = this.columns, c;
33784         var totalWidth = 0;
33785         var expEl = false;
33786         var len = cols.length;
33787         for(var i = 0; i < len; i++){
33788             c = cols[i];
33789             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33790                 // it's the expander..
33791                 expEl  = this.headEls[i];
33792                 continue;
33793             }
33794             totalWidth += c.width;
33795             
33796         }
33797         if (expEl) {
33798             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33799         }
33800         this.headers.setWidth(w-20);
33801
33802         
33803         
33804         
33805     }
33806 });
33807 /*
33808  * Based on:
33809  * Ext JS Library 1.1.1
33810  * Copyright(c) 2006-2007, Ext JS, LLC.
33811  *
33812  * Originally Released Under LGPL - original licence link has changed is not relivant.
33813  *
33814  * Fork - LGPL
33815  * <script type="text/javascript">
33816  */
33817  
33818 /**
33819  * @class Roo.menu.Menu
33820  * @extends Roo.util.Observable
33821  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33822  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33823  * @constructor
33824  * Creates a new Menu
33825  * @param {Object} config Configuration options
33826  */
33827 Roo.menu.Menu = function(config){
33828     Roo.apply(this, config);
33829     this.id = this.id || Roo.id();
33830     this.addEvents({
33831         /**
33832          * @event beforeshow
33833          * Fires before this menu is displayed
33834          * @param {Roo.menu.Menu} this
33835          */
33836         beforeshow : true,
33837         /**
33838          * @event beforehide
33839          * Fires before this menu is hidden
33840          * @param {Roo.menu.Menu} this
33841          */
33842         beforehide : true,
33843         /**
33844          * @event show
33845          * Fires after this menu is displayed
33846          * @param {Roo.menu.Menu} this
33847          */
33848         show : true,
33849         /**
33850          * @event hide
33851          * Fires after this menu is hidden
33852          * @param {Roo.menu.Menu} this
33853          */
33854         hide : true,
33855         /**
33856          * @event click
33857          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33858          * @param {Roo.menu.Menu} this
33859          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33860          * @param {Roo.EventObject} e
33861          */
33862         click : true,
33863         /**
33864          * @event mouseover
33865          * Fires when the mouse is hovering over this menu
33866          * @param {Roo.menu.Menu} this
33867          * @param {Roo.EventObject} e
33868          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33869          */
33870         mouseover : true,
33871         /**
33872          * @event mouseout
33873          * Fires when the mouse exits this menu
33874          * @param {Roo.menu.Menu} this
33875          * @param {Roo.EventObject} e
33876          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33877          */
33878         mouseout : true,
33879         /**
33880          * @event itemclick
33881          * Fires when a menu item contained in this menu is clicked
33882          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33883          * @param {Roo.EventObject} e
33884          */
33885         itemclick: true
33886     });
33887     if (this.registerMenu) {
33888         Roo.menu.MenuMgr.register(this);
33889     }
33890     
33891     var mis = this.items;
33892     this.items = new Roo.util.MixedCollection();
33893     if(mis){
33894         this.add.apply(this, mis);
33895     }
33896 };
33897
33898 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33899     /**
33900      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33901      */
33902     minWidth : 120,
33903     /**
33904      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33905      * for bottom-right shadow (defaults to "sides")
33906      */
33907     shadow : "sides",
33908     /**
33909      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33910      * this menu (defaults to "tl-tr?")
33911      */
33912     subMenuAlign : "tl-tr?",
33913     /**
33914      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33915      * relative to its element of origin (defaults to "tl-bl?")
33916      */
33917     defaultAlign : "tl-bl?",
33918     /**
33919      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33920      */
33921     allowOtherMenus : false,
33922     /**
33923      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33924      */
33925     registerMenu : true,
33926
33927     hidden:true,
33928
33929     // private
33930     render : function(){
33931         if(this.el){
33932             return;
33933         }
33934         var el = this.el = new Roo.Layer({
33935             cls: "x-menu",
33936             shadow:this.shadow,
33937             constrain: false,
33938             parentEl: this.parentEl || document.body,
33939             zindex:15000
33940         });
33941
33942         this.keyNav = new Roo.menu.MenuNav(this);
33943
33944         if(this.plain){
33945             el.addClass("x-menu-plain");
33946         }
33947         if(this.cls){
33948             el.addClass(this.cls);
33949         }
33950         // generic focus element
33951         this.focusEl = el.createChild({
33952             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33953         });
33954         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33955         ul.on("click", this.onClick, this);
33956         ul.on("mouseover", this.onMouseOver, this);
33957         ul.on("mouseout", this.onMouseOut, this);
33958         this.items.each(function(item){
33959             var li = document.createElement("li");
33960             li.className = "x-menu-list-item";
33961             ul.dom.appendChild(li);
33962             item.render(li, this);
33963         }, this);
33964         this.ul = ul;
33965         this.autoWidth();
33966     },
33967
33968     // private
33969     autoWidth : function(){
33970         var el = this.el, ul = this.ul;
33971         if(!el){
33972             return;
33973         }
33974         var w = this.width;
33975         if(w){
33976             el.setWidth(w);
33977         }else if(Roo.isIE){
33978             el.setWidth(this.minWidth);
33979             var t = el.dom.offsetWidth; // force recalc
33980             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33981         }
33982     },
33983
33984     // private
33985     delayAutoWidth : function(){
33986         if(this.rendered){
33987             if(!this.awTask){
33988                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33989             }
33990             this.awTask.delay(20);
33991         }
33992     },
33993
33994     // private
33995     findTargetItem : function(e){
33996         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33997         if(t && t.menuItemId){
33998             return this.items.get(t.menuItemId);
33999         }
34000     },
34001
34002     // private
34003     onClick : function(e){
34004         var t;
34005         if(t = this.findTargetItem(e)){
34006             t.onClick(e);
34007             this.fireEvent("click", this, t, e);
34008         }
34009     },
34010
34011     // private
34012     setActiveItem : function(item, autoExpand){
34013         if(item != this.activeItem){
34014             if(this.activeItem){
34015                 this.activeItem.deactivate();
34016             }
34017             this.activeItem = item;
34018             item.activate(autoExpand);
34019         }else if(autoExpand){
34020             item.expandMenu();
34021         }
34022     },
34023
34024     // private
34025     tryActivate : function(start, step){
34026         var items = this.items;
34027         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34028             var item = items.get(i);
34029             if(!item.disabled && item.canActivate){
34030                 this.setActiveItem(item, false);
34031                 return item;
34032             }
34033         }
34034         return false;
34035     },
34036
34037     // private
34038     onMouseOver : function(e){
34039         var t;
34040         if(t = this.findTargetItem(e)){
34041             if(t.canActivate && !t.disabled){
34042                 this.setActiveItem(t, true);
34043             }
34044         }
34045         this.fireEvent("mouseover", this, e, t);
34046     },
34047
34048     // private
34049     onMouseOut : function(e){
34050         var t;
34051         if(t = this.findTargetItem(e)){
34052             if(t == this.activeItem && t.shouldDeactivate(e)){
34053                 this.activeItem.deactivate();
34054                 delete this.activeItem;
34055             }
34056         }
34057         this.fireEvent("mouseout", this, e, t);
34058     },
34059
34060     /**
34061      * Read-only.  Returns true if the menu is currently displayed, else false.
34062      * @type Boolean
34063      */
34064     isVisible : function(){
34065         return this.el && !this.hidden;
34066     },
34067
34068     /**
34069      * Displays this menu relative to another element
34070      * @param {String/HTMLElement/Roo.Element} element The element to align to
34071      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34072      * the element (defaults to this.defaultAlign)
34073      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34074      */
34075     show : function(el, pos, parentMenu){
34076         this.parentMenu = parentMenu;
34077         if(!this.el){
34078             this.render();
34079         }
34080         this.fireEvent("beforeshow", this);
34081         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34082     },
34083
34084     /**
34085      * Displays this menu at a specific xy position
34086      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34087      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34088      */
34089     showAt : function(xy, parentMenu, /* private: */_e){
34090         this.parentMenu = parentMenu;
34091         if(!this.el){
34092             this.render();
34093         }
34094         if(_e !== false){
34095             this.fireEvent("beforeshow", this);
34096             xy = this.el.adjustForConstraints(xy);
34097         }
34098         this.el.setXY(xy);
34099         this.el.show();
34100         this.hidden = false;
34101         this.focus();
34102         this.fireEvent("show", this);
34103     },
34104
34105     focus : function(){
34106         if(!this.hidden){
34107             this.doFocus.defer(50, this);
34108         }
34109     },
34110
34111     doFocus : function(){
34112         if(!this.hidden){
34113             this.focusEl.focus();
34114         }
34115     },
34116
34117     /**
34118      * Hides this menu and optionally all parent menus
34119      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34120      */
34121     hide : function(deep){
34122         if(this.el && this.isVisible()){
34123             this.fireEvent("beforehide", this);
34124             if(this.activeItem){
34125                 this.activeItem.deactivate();
34126                 this.activeItem = null;
34127             }
34128             this.el.hide();
34129             this.hidden = true;
34130             this.fireEvent("hide", this);
34131         }
34132         if(deep === true && this.parentMenu){
34133             this.parentMenu.hide(true);
34134         }
34135     },
34136
34137     /**
34138      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34139      * Any of the following are valid:
34140      * <ul>
34141      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34142      * <li>An HTMLElement object which will be converted to a menu item</li>
34143      * <li>A menu item config object that will be created as a new menu item</li>
34144      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34145      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34146      * </ul>
34147      * Usage:
34148      * <pre><code>
34149 // Create the menu
34150 var menu = new Roo.menu.Menu();
34151
34152 // Create a menu item to add by reference
34153 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34154
34155 // Add a bunch of items at once using different methods.
34156 // Only the last item added will be returned.
34157 var item = menu.add(
34158     menuItem,                // add existing item by ref
34159     'Dynamic Item',          // new TextItem
34160     '-',                     // new separator
34161     { text: 'Config Item' }  // new item by config
34162 );
34163 </code></pre>
34164      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34165      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34166      */
34167     add : function(){
34168         var a = arguments, l = a.length, item;
34169         for(var i = 0; i < l; i++){
34170             var el = a[i];
34171             if ((typeof(el) == "object") && el.xtype && el.xns) {
34172                 el = Roo.factory(el, Roo.menu);
34173             }
34174             
34175             if(el.render){ // some kind of Item
34176                 item = this.addItem(el);
34177             }else if(typeof el == "string"){ // string
34178                 if(el == "separator" || el == "-"){
34179                     item = this.addSeparator();
34180                 }else{
34181                     item = this.addText(el);
34182                 }
34183             }else if(el.tagName || el.el){ // element
34184                 item = this.addElement(el);
34185             }else if(typeof el == "object"){ // must be menu item config?
34186                 item = this.addMenuItem(el);
34187             }
34188         }
34189         return item;
34190     },
34191
34192     /**
34193      * Returns this menu's underlying {@link Roo.Element} object
34194      * @return {Roo.Element} The element
34195      */
34196     getEl : function(){
34197         if(!this.el){
34198             this.render();
34199         }
34200         return this.el;
34201     },
34202
34203     /**
34204      * Adds a separator bar to the menu
34205      * @return {Roo.menu.Item} The menu item that was added
34206      */
34207     addSeparator : function(){
34208         return this.addItem(new Roo.menu.Separator());
34209     },
34210
34211     /**
34212      * Adds an {@link Roo.Element} object to the menu
34213      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34214      * @return {Roo.menu.Item} The menu item that was added
34215      */
34216     addElement : function(el){
34217         return this.addItem(new Roo.menu.BaseItem(el));
34218     },
34219
34220     /**
34221      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34222      * @param {Roo.menu.Item} item The menu item to add
34223      * @return {Roo.menu.Item} The menu item that was added
34224      */
34225     addItem : function(item){
34226         this.items.add(item);
34227         if(this.ul){
34228             var li = document.createElement("li");
34229             li.className = "x-menu-list-item";
34230             this.ul.dom.appendChild(li);
34231             item.render(li, this);
34232             this.delayAutoWidth();
34233         }
34234         return item;
34235     },
34236
34237     /**
34238      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34239      * @param {Object} config A MenuItem config object
34240      * @return {Roo.menu.Item} The menu item that was added
34241      */
34242     addMenuItem : function(config){
34243         if(!(config instanceof Roo.menu.Item)){
34244             if(typeof config.checked == "boolean"){ // must be check menu item config?
34245                 config = new Roo.menu.CheckItem(config);
34246             }else{
34247                 config = new Roo.menu.Item(config);
34248             }
34249         }
34250         return this.addItem(config);
34251     },
34252
34253     /**
34254      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34255      * @param {String} text The text to display in the menu item
34256      * @return {Roo.menu.Item} The menu item that was added
34257      */
34258     addText : function(text){
34259         return this.addItem(new Roo.menu.TextItem({ text : text }));
34260     },
34261
34262     /**
34263      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34264      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34265      * @param {Roo.menu.Item} item The menu item to add
34266      * @return {Roo.menu.Item} The menu item that was added
34267      */
34268     insert : function(index, item){
34269         this.items.insert(index, item);
34270         if(this.ul){
34271             var li = document.createElement("li");
34272             li.className = "x-menu-list-item";
34273             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34274             item.render(li, this);
34275             this.delayAutoWidth();
34276         }
34277         return item;
34278     },
34279
34280     /**
34281      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34282      * @param {Roo.menu.Item} item The menu item to remove
34283      */
34284     remove : function(item){
34285         this.items.removeKey(item.id);
34286         item.destroy();
34287     },
34288
34289     /**
34290      * Removes and destroys all items in the menu
34291      */
34292     removeAll : function(){
34293         var f;
34294         while(f = this.items.first()){
34295             this.remove(f);
34296         }
34297     }
34298 });
34299
34300 // MenuNav is a private utility class used internally by the Menu
34301 Roo.menu.MenuNav = function(menu){
34302     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34303     this.scope = this.menu = menu;
34304 };
34305
34306 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34307     doRelay : function(e, h){
34308         var k = e.getKey();
34309         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34310             this.menu.tryActivate(0, 1);
34311             return false;
34312         }
34313         return h.call(this.scope || this, e, this.menu);
34314     },
34315
34316     up : function(e, m){
34317         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34318             m.tryActivate(m.items.length-1, -1);
34319         }
34320     },
34321
34322     down : function(e, m){
34323         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34324             m.tryActivate(0, 1);
34325         }
34326     },
34327
34328     right : function(e, m){
34329         if(m.activeItem){
34330             m.activeItem.expandMenu(true);
34331         }
34332     },
34333
34334     left : function(e, m){
34335         m.hide();
34336         if(m.parentMenu && m.parentMenu.activeItem){
34337             m.parentMenu.activeItem.activate();
34338         }
34339     },
34340
34341     enter : function(e, m){
34342         if(m.activeItem){
34343             e.stopPropagation();
34344             m.activeItem.onClick(e);
34345             m.fireEvent("click", this, m.activeItem);
34346             return true;
34347         }
34348     }
34349 });/*
34350  * Based on:
34351  * Ext JS Library 1.1.1
34352  * Copyright(c) 2006-2007, Ext JS, LLC.
34353  *
34354  * Originally Released Under LGPL - original licence link has changed is not relivant.
34355  *
34356  * Fork - LGPL
34357  * <script type="text/javascript">
34358  */
34359  
34360 /**
34361  * @class Roo.menu.MenuMgr
34362  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34363  * @singleton
34364  */
34365 Roo.menu.MenuMgr = function(){
34366    var menus, active, groups = {}, attached = false, lastShow = new Date();
34367
34368    // private - called when first menu is created
34369    function init(){
34370        menus = {};
34371        active = new Roo.util.MixedCollection();
34372        Roo.get(document).addKeyListener(27, function(){
34373            if(active.length > 0){
34374                hideAll();
34375            }
34376        });
34377    }
34378
34379    // private
34380    function hideAll(){
34381        if(active && active.length > 0){
34382            var c = active.clone();
34383            c.each(function(m){
34384                m.hide();
34385            });
34386        }
34387    }
34388
34389    // private
34390    function onHide(m){
34391        active.remove(m);
34392        if(active.length < 1){
34393            Roo.get(document).un("mousedown", onMouseDown);
34394            attached = false;
34395        }
34396    }
34397
34398    // private
34399    function onShow(m){
34400        var last = active.last();
34401        lastShow = new Date();
34402        active.add(m);
34403        if(!attached){
34404            Roo.get(document).on("mousedown", onMouseDown);
34405            attached = true;
34406        }
34407        if(m.parentMenu){
34408           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34409           m.parentMenu.activeChild = m;
34410        }else if(last && last.isVisible()){
34411           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34412        }
34413    }
34414
34415    // private
34416    function onBeforeHide(m){
34417        if(m.activeChild){
34418            m.activeChild.hide();
34419        }
34420        if(m.autoHideTimer){
34421            clearTimeout(m.autoHideTimer);
34422            delete m.autoHideTimer;
34423        }
34424    }
34425
34426    // private
34427    function onBeforeShow(m){
34428        var pm = m.parentMenu;
34429        if(!pm && !m.allowOtherMenus){
34430            hideAll();
34431        }else if(pm && pm.activeChild && active != m){
34432            pm.activeChild.hide();
34433        }
34434    }
34435
34436    // private
34437    function onMouseDown(e){
34438        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34439            hideAll();
34440        }
34441    }
34442
34443    // private
34444    function onBeforeCheck(mi, state){
34445        if(state){
34446            var g = groups[mi.group];
34447            for(var i = 0, l = g.length; i < l; i++){
34448                if(g[i] != mi){
34449                    g[i].setChecked(false);
34450                }
34451            }
34452        }
34453    }
34454
34455    return {
34456
34457        /**
34458         * Hides all menus that are currently visible
34459         */
34460        hideAll : function(){
34461             hideAll();  
34462        },
34463
34464        // private
34465        register : function(menu){
34466            if(!menus){
34467                init();
34468            }
34469            menus[menu.id] = menu;
34470            menu.on("beforehide", onBeforeHide);
34471            menu.on("hide", onHide);
34472            menu.on("beforeshow", onBeforeShow);
34473            menu.on("show", onShow);
34474            var g = menu.group;
34475            if(g && menu.events["checkchange"]){
34476                if(!groups[g]){
34477                    groups[g] = [];
34478                }
34479                groups[g].push(menu);
34480                menu.on("checkchange", onCheck);
34481            }
34482        },
34483
34484         /**
34485          * Returns a {@link Roo.menu.Menu} object
34486          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34487          * be used to generate and return a new Menu instance.
34488          */
34489        get : function(menu){
34490            if(typeof menu == "string"){ // menu id
34491                return menus[menu];
34492            }else if(menu.events){  // menu instance
34493                return menu;
34494            }else if(typeof menu.length == 'number'){ // array of menu items?
34495                return new Roo.menu.Menu({items:menu});
34496            }else{ // otherwise, must be a config
34497                return new Roo.menu.Menu(menu);
34498            }
34499        },
34500
34501        // private
34502        unregister : function(menu){
34503            delete menus[menu.id];
34504            menu.un("beforehide", onBeforeHide);
34505            menu.un("hide", onHide);
34506            menu.un("beforeshow", onBeforeShow);
34507            menu.un("show", onShow);
34508            var g = menu.group;
34509            if(g && menu.events["checkchange"]){
34510                groups[g].remove(menu);
34511                menu.un("checkchange", onCheck);
34512            }
34513        },
34514
34515        // private
34516        registerCheckable : function(menuItem){
34517            var g = menuItem.group;
34518            if(g){
34519                if(!groups[g]){
34520                    groups[g] = [];
34521                }
34522                groups[g].push(menuItem);
34523                menuItem.on("beforecheckchange", onBeforeCheck);
34524            }
34525        },
34526
34527        // private
34528        unregisterCheckable : function(menuItem){
34529            var g = menuItem.group;
34530            if(g){
34531                groups[g].remove(menuItem);
34532                menuItem.un("beforecheckchange", onBeforeCheck);
34533            }
34534        }
34535    };
34536 }();/*
34537  * Based on:
34538  * Ext JS Library 1.1.1
34539  * Copyright(c) 2006-2007, Ext JS, LLC.
34540  *
34541  * Originally Released Under LGPL - original licence link has changed is not relivant.
34542  *
34543  * Fork - LGPL
34544  * <script type="text/javascript">
34545  */
34546  
34547
34548 /**
34549  * @class Roo.menu.BaseItem
34550  * @extends Roo.Component
34551  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34552  * management and base configuration options shared by all menu components.
34553  * @constructor
34554  * Creates a new BaseItem
34555  * @param {Object} config Configuration options
34556  */
34557 Roo.menu.BaseItem = function(config){
34558     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34559
34560     this.addEvents({
34561         /**
34562          * @event click
34563          * Fires when this item is clicked
34564          * @param {Roo.menu.BaseItem} this
34565          * @param {Roo.EventObject} e
34566          */
34567         click: true,
34568         /**
34569          * @event activate
34570          * Fires when this item is activated
34571          * @param {Roo.menu.BaseItem} this
34572          */
34573         activate : true,
34574         /**
34575          * @event deactivate
34576          * Fires when this item is deactivated
34577          * @param {Roo.menu.BaseItem} this
34578          */
34579         deactivate : true
34580     });
34581
34582     if(this.handler){
34583         this.on("click", this.handler, this.scope, true);
34584     }
34585 };
34586
34587 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34588     /**
34589      * @cfg {Function} handler
34590      * A function that will handle the click event of this menu item (defaults to undefined)
34591      */
34592     /**
34593      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34594      */
34595     canActivate : false,
34596     /**
34597      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34598      */
34599     activeClass : "x-menu-item-active",
34600     /**
34601      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34602      */
34603     hideOnClick : true,
34604     /**
34605      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34606      */
34607     hideDelay : 100,
34608
34609     // private
34610     ctype: "Roo.menu.BaseItem",
34611
34612     // private
34613     actionMode : "container",
34614
34615     // private
34616     render : function(container, parentMenu){
34617         this.parentMenu = parentMenu;
34618         Roo.menu.BaseItem.superclass.render.call(this, container);
34619         this.container.menuItemId = this.id;
34620     },
34621
34622     // private
34623     onRender : function(container, position){
34624         this.el = Roo.get(this.el);
34625         container.dom.appendChild(this.el.dom);
34626     },
34627
34628     // private
34629     onClick : function(e){
34630         if(!this.disabled && this.fireEvent("click", this, e) !== false
34631                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34632             this.handleClick(e);
34633         }else{
34634             e.stopEvent();
34635         }
34636     },
34637
34638     // private
34639     activate : function(){
34640         if(this.disabled){
34641             return false;
34642         }
34643         var li = this.container;
34644         li.addClass(this.activeClass);
34645         this.region = li.getRegion().adjust(2, 2, -2, -2);
34646         this.fireEvent("activate", this);
34647         return true;
34648     },
34649
34650     // private
34651     deactivate : function(){
34652         this.container.removeClass(this.activeClass);
34653         this.fireEvent("deactivate", this);
34654     },
34655
34656     // private
34657     shouldDeactivate : function(e){
34658         return !this.region || !this.region.contains(e.getPoint());
34659     },
34660
34661     // private
34662     handleClick : function(e){
34663         if(this.hideOnClick){
34664             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34665         }
34666     },
34667
34668     // private
34669     expandMenu : function(autoActivate){
34670         // do nothing
34671     },
34672
34673     // private
34674     hideMenu : function(){
34675         // do nothing
34676     }
34677 });/*
34678  * Based on:
34679  * Ext JS Library 1.1.1
34680  * Copyright(c) 2006-2007, Ext JS, LLC.
34681  *
34682  * Originally Released Under LGPL - original licence link has changed is not relivant.
34683  *
34684  * Fork - LGPL
34685  * <script type="text/javascript">
34686  */
34687  
34688 /**
34689  * @class Roo.menu.Adapter
34690  * @extends Roo.menu.BaseItem
34691  * 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.
34692  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34693  * @constructor
34694  * Creates a new Adapter
34695  * @param {Object} config Configuration options
34696  */
34697 Roo.menu.Adapter = function(component, config){
34698     Roo.menu.Adapter.superclass.constructor.call(this, config);
34699     this.component = component;
34700 };
34701 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34702     // private
34703     canActivate : true,
34704
34705     // private
34706     onRender : function(container, position){
34707         this.component.render(container);
34708         this.el = this.component.getEl();
34709     },
34710
34711     // private
34712     activate : function(){
34713         if(this.disabled){
34714             return false;
34715         }
34716         this.component.focus();
34717         this.fireEvent("activate", this);
34718         return true;
34719     },
34720
34721     // private
34722     deactivate : function(){
34723         this.fireEvent("deactivate", this);
34724     },
34725
34726     // private
34727     disable : function(){
34728         this.component.disable();
34729         Roo.menu.Adapter.superclass.disable.call(this);
34730     },
34731
34732     // private
34733     enable : function(){
34734         this.component.enable();
34735         Roo.menu.Adapter.superclass.enable.call(this);
34736     }
34737 });/*
34738  * Based on:
34739  * Ext JS Library 1.1.1
34740  * Copyright(c) 2006-2007, Ext JS, LLC.
34741  *
34742  * Originally Released Under LGPL - original licence link has changed is not relivant.
34743  *
34744  * Fork - LGPL
34745  * <script type="text/javascript">
34746  */
34747
34748 /**
34749  * @class Roo.menu.TextItem
34750  * @extends Roo.menu.BaseItem
34751  * Adds a static text string to a menu, usually used as either a heading or group separator.
34752  * Note: old style constructor with text is still supported.
34753  * 
34754  * @constructor
34755  * Creates a new TextItem
34756  * @param {Object} cfg Configuration
34757  */
34758 Roo.menu.TextItem = function(cfg){
34759     if (typeof(cfg) == 'string') {
34760         this.text = cfg;
34761     } else {
34762         Roo.apply(this,cfg);
34763     }
34764     
34765     Roo.menu.TextItem.superclass.constructor.call(this);
34766 };
34767
34768 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34769     /**
34770      * @cfg {Boolean} text Text to show on item.
34771      */
34772     text : '',
34773     
34774     /**
34775      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34776      */
34777     hideOnClick : false,
34778     /**
34779      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34780      */
34781     itemCls : "x-menu-text",
34782
34783     // private
34784     onRender : function(){
34785         var s = document.createElement("span");
34786         s.className = this.itemCls;
34787         s.innerHTML = this.text;
34788         this.el = s;
34789         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34790     }
34791 });/*
34792  * Based on:
34793  * Ext JS Library 1.1.1
34794  * Copyright(c) 2006-2007, Ext JS, LLC.
34795  *
34796  * Originally Released Under LGPL - original licence link has changed is not relivant.
34797  *
34798  * Fork - LGPL
34799  * <script type="text/javascript">
34800  */
34801
34802 /**
34803  * @class Roo.menu.Separator
34804  * @extends Roo.menu.BaseItem
34805  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34806  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34807  * @constructor
34808  * @param {Object} config Configuration options
34809  */
34810 Roo.menu.Separator = function(config){
34811     Roo.menu.Separator.superclass.constructor.call(this, config);
34812 };
34813
34814 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34815     /**
34816      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34817      */
34818     itemCls : "x-menu-sep",
34819     /**
34820      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34821      */
34822     hideOnClick : false,
34823
34824     // private
34825     onRender : function(li){
34826         var s = document.createElement("span");
34827         s.className = this.itemCls;
34828         s.innerHTML = "&#160;";
34829         this.el = s;
34830         li.addClass("x-menu-sep-li");
34831         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34832     }
34833 });/*
34834  * Based on:
34835  * Ext JS Library 1.1.1
34836  * Copyright(c) 2006-2007, Ext JS, LLC.
34837  *
34838  * Originally Released Under LGPL - original licence link has changed is not relivant.
34839  *
34840  * Fork - LGPL
34841  * <script type="text/javascript">
34842  */
34843 /**
34844  * @class Roo.menu.Item
34845  * @extends Roo.menu.BaseItem
34846  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34847  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34848  * activation and click handling.
34849  * @constructor
34850  * Creates a new Item
34851  * @param {Object} config Configuration options
34852  */
34853 Roo.menu.Item = function(config){
34854     Roo.menu.Item.superclass.constructor.call(this, config);
34855     if(this.menu){
34856         this.menu = Roo.menu.MenuMgr.get(this.menu);
34857     }
34858 };
34859 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34860     
34861     /**
34862      * @cfg {String} text
34863      * The text to show on the menu item.
34864      */
34865     text: '',
34866      /**
34867      * @cfg {String} HTML to render in menu
34868      * The text to show on the menu item (HTML version).
34869      */
34870     html: '',
34871     /**
34872      * @cfg {String} icon
34873      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34874      */
34875     icon: undefined,
34876     /**
34877      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34878      */
34879     itemCls : "x-menu-item",
34880     /**
34881      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34882      */
34883     canActivate : true,
34884     /**
34885      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34886      */
34887     showDelay: 200,
34888     // doc'd in BaseItem
34889     hideDelay: 200,
34890
34891     // private
34892     ctype: "Roo.menu.Item",
34893     
34894     // private
34895     onRender : function(container, position){
34896         var el = document.createElement("a");
34897         el.hideFocus = true;
34898         el.unselectable = "on";
34899         el.href = this.href || "#";
34900         if(this.hrefTarget){
34901             el.target = this.hrefTarget;
34902         }
34903         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34904         
34905         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34906         
34907         el.innerHTML = String.format(
34908                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34909                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34910         this.el = el;
34911         Roo.menu.Item.superclass.onRender.call(this, container, position);
34912     },
34913
34914     /**
34915      * Sets the text to display in this menu item
34916      * @param {String} text The text to display
34917      * @param {Boolean} isHTML true to indicate text is pure html.
34918      */
34919     setText : function(text, isHTML){
34920         if (isHTML) {
34921             this.html = text;
34922         } else {
34923             this.text = text;
34924             this.html = '';
34925         }
34926         if(this.rendered){
34927             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34928      
34929             this.el.update(String.format(
34930                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34931                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34932             this.parentMenu.autoWidth();
34933         }
34934     },
34935
34936     // private
34937     handleClick : function(e){
34938         if(!this.href){ // if no link defined, stop the event automatically
34939             e.stopEvent();
34940         }
34941         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34942     },
34943
34944     // private
34945     activate : function(autoExpand){
34946         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34947             this.focus();
34948             if(autoExpand){
34949                 this.expandMenu();
34950             }
34951         }
34952         return true;
34953     },
34954
34955     // private
34956     shouldDeactivate : function(e){
34957         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34958             if(this.menu && this.menu.isVisible()){
34959                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34960             }
34961             return true;
34962         }
34963         return false;
34964     },
34965
34966     // private
34967     deactivate : function(){
34968         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34969         this.hideMenu();
34970     },
34971
34972     // private
34973     expandMenu : function(autoActivate){
34974         if(!this.disabled && this.menu){
34975             clearTimeout(this.hideTimer);
34976             delete this.hideTimer;
34977             if(!this.menu.isVisible() && !this.showTimer){
34978                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34979             }else if (this.menu.isVisible() && autoActivate){
34980                 this.menu.tryActivate(0, 1);
34981             }
34982         }
34983     },
34984
34985     // private
34986     deferExpand : function(autoActivate){
34987         delete this.showTimer;
34988         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34989         if(autoActivate){
34990             this.menu.tryActivate(0, 1);
34991         }
34992     },
34993
34994     // private
34995     hideMenu : function(){
34996         clearTimeout(this.showTimer);
34997         delete this.showTimer;
34998         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34999             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35000         }
35001     },
35002
35003     // private
35004     deferHide : function(){
35005         delete this.hideTimer;
35006         this.menu.hide();
35007     }
35008 });/*
35009  * Based on:
35010  * Ext JS Library 1.1.1
35011  * Copyright(c) 2006-2007, Ext JS, LLC.
35012  *
35013  * Originally Released Under LGPL - original licence link has changed is not relivant.
35014  *
35015  * Fork - LGPL
35016  * <script type="text/javascript">
35017  */
35018  
35019 /**
35020  * @class Roo.menu.CheckItem
35021  * @extends Roo.menu.Item
35022  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35023  * @constructor
35024  * Creates a new CheckItem
35025  * @param {Object} config Configuration options
35026  */
35027 Roo.menu.CheckItem = function(config){
35028     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35029     this.addEvents({
35030         /**
35031          * @event beforecheckchange
35032          * Fires before the checked value is set, providing an opportunity to cancel if needed
35033          * @param {Roo.menu.CheckItem} this
35034          * @param {Boolean} checked The new checked value that will be set
35035          */
35036         "beforecheckchange" : true,
35037         /**
35038          * @event checkchange
35039          * Fires after the checked value has been set
35040          * @param {Roo.menu.CheckItem} this
35041          * @param {Boolean} checked The checked value that was set
35042          */
35043         "checkchange" : true
35044     });
35045     if(this.checkHandler){
35046         this.on('checkchange', this.checkHandler, this.scope);
35047     }
35048 };
35049 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35050     /**
35051      * @cfg {String} group
35052      * All check items with the same group name will automatically be grouped into a single-select
35053      * radio button group (defaults to '')
35054      */
35055     /**
35056      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35057      */
35058     itemCls : "x-menu-item x-menu-check-item",
35059     /**
35060      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35061      */
35062     groupClass : "x-menu-group-item",
35063
35064     /**
35065      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35066      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35067      * initialized with checked = true will be rendered as checked.
35068      */
35069     checked: false,
35070
35071     // private
35072     ctype: "Roo.menu.CheckItem",
35073
35074     // private
35075     onRender : function(c){
35076         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35077         if(this.group){
35078             this.el.addClass(this.groupClass);
35079         }
35080         Roo.menu.MenuMgr.registerCheckable(this);
35081         if(this.checked){
35082             this.checked = false;
35083             this.setChecked(true, true);
35084         }
35085     },
35086
35087     // private
35088     destroy : function(){
35089         if(this.rendered){
35090             Roo.menu.MenuMgr.unregisterCheckable(this);
35091         }
35092         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35093     },
35094
35095     /**
35096      * Set the checked state of this item
35097      * @param {Boolean} checked The new checked value
35098      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35099      */
35100     setChecked : function(state, suppressEvent){
35101         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35102             if(this.container){
35103                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35104             }
35105             this.checked = state;
35106             if(suppressEvent !== true){
35107                 this.fireEvent("checkchange", this, state);
35108             }
35109         }
35110     },
35111
35112     // private
35113     handleClick : function(e){
35114        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35115            this.setChecked(!this.checked);
35116        }
35117        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35118     }
35119 });/*
35120  * Based on:
35121  * Ext JS Library 1.1.1
35122  * Copyright(c) 2006-2007, Ext JS, LLC.
35123  *
35124  * Originally Released Under LGPL - original licence link has changed is not relivant.
35125  *
35126  * Fork - LGPL
35127  * <script type="text/javascript">
35128  */
35129  
35130 /**
35131  * @class Roo.menu.DateItem
35132  * @extends Roo.menu.Adapter
35133  * A menu item that wraps the {@link Roo.DatPicker} component.
35134  * @constructor
35135  * Creates a new DateItem
35136  * @param {Object} config Configuration options
35137  */
35138 Roo.menu.DateItem = function(config){
35139     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35140     /** The Roo.DatePicker object @type Roo.DatePicker */
35141     this.picker = this.component;
35142     this.addEvents({select: true});
35143     
35144     this.picker.on("render", function(picker){
35145         picker.getEl().swallowEvent("click");
35146         picker.container.addClass("x-menu-date-item");
35147     });
35148
35149     this.picker.on("select", this.onSelect, this);
35150 };
35151
35152 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35153     // private
35154     onSelect : function(picker, date){
35155         this.fireEvent("select", this, date, picker);
35156         Roo.menu.DateItem.superclass.handleClick.call(this);
35157     }
35158 });/*
35159  * Based on:
35160  * Ext JS Library 1.1.1
35161  * Copyright(c) 2006-2007, Ext JS, LLC.
35162  *
35163  * Originally Released Under LGPL - original licence link has changed is not relivant.
35164  *
35165  * Fork - LGPL
35166  * <script type="text/javascript">
35167  */
35168  
35169 /**
35170  * @class Roo.menu.ColorItem
35171  * @extends Roo.menu.Adapter
35172  * A menu item that wraps the {@link Roo.ColorPalette} component.
35173  * @constructor
35174  * Creates a new ColorItem
35175  * @param {Object} config Configuration options
35176  */
35177 Roo.menu.ColorItem = function(config){
35178     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35179     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35180     this.palette = this.component;
35181     this.relayEvents(this.palette, ["select"]);
35182     if(this.selectHandler){
35183         this.on('select', this.selectHandler, this.scope);
35184     }
35185 };
35186 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35187  * Based on:
35188  * Ext JS Library 1.1.1
35189  * Copyright(c) 2006-2007, Ext JS, LLC.
35190  *
35191  * Originally Released Under LGPL - original licence link has changed is not relivant.
35192  *
35193  * Fork - LGPL
35194  * <script type="text/javascript">
35195  */
35196  
35197
35198 /**
35199  * @class Roo.menu.DateMenu
35200  * @extends Roo.menu.Menu
35201  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35202  * @constructor
35203  * Creates a new DateMenu
35204  * @param {Object} config Configuration options
35205  */
35206 Roo.menu.DateMenu = function(config){
35207     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35208     this.plain = true;
35209     var di = new Roo.menu.DateItem(config);
35210     this.add(di);
35211     /**
35212      * The {@link Roo.DatePicker} instance for this DateMenu
35213      * @type DatePicker
35214      */
35215     this.picker = di.picker;
35216     /**
35217      * @event select
35218      * @param {DatePicker} picker
35219      * @param {Date} date
35220      */
35221     this.relayEvents(di, ["select"]);
35222
35223     this.on('beforeshow', function(){
35224         if(this.picker){
35225             this.picker.hideMonthPicker(true);
35226         }
35227     }, this);
35228 };
35229 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35230     cls:'x-date-menu'
35231 });/*
35232  * Based on:
35233  * Ext JS Library 1.1.1
35234  * Copyright(c) 2006-2007, Ext JS, LLC.
35235  *
35236  * Originally Released Under LGPL - original licence link has changed is not relivant.
35237  *
35238  * Fork - LGPL
35239  * <script type="text/javascript">
35240  */
35241  
35242
35243 /**
35244  * @class Roo.menu.ColorMenu
35245  * @extends Roo.menu.Menu
35246  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35247  * @constructor
35248  * Creates a new ColorMenu
35249  * @param {Object} config Configuration options
35250  */
35251 Roo.menu.ColorMenu = function(config){
35252     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35253     this.plain = true;
35254     var ci = new Roo.menu.ColorItem(config);
35255     this.add(ci);
35256     /**
35257      * The {@link Roo.ColorPalette} instance for this ColorMenu
35258      * @type ColorPalette
35259      */
35260     this.palette = ci.palette;
35261     /**
35262      * @event select
35263      * @param {ColorPalette} palette
35264      * @param {String} color
35265      */
35266     this.relayEvents(ci, ["select"]);
35267 };
35268 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35269  * Based on:
35270  * Ext JS Library 1.1.1
35271  * Copyright(c) 2006-2007, Ext JS, LLC.
35272  *
35273  * Originally Released Under LGPL - original licence link has changed is not relivant.
35274  *
35275  * Fork - LGPL
35276  * <script type="text/javascript">
35277  */
35278  
35279 /**
35280  * @class Roo.form.Field
35281  * @extends Roo.BoxComponent
35282  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35283  * @constructor
35284  * Creates a new Field
35285  * @param {Object} config Configuration options
35286  */
35287 Roo.form.Field = function(config){
35288     Roo.form.Field.superclass.constructor.call(this, config);
35289 };
35290
35291 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35292     /**
35293      * @cfg {String} fieldLabel Label to use when rendering a form.
35294      */
35295        /**
35296      * @cfg {String} qtip Mouse over tip
35297      */
35298      
35299     /**
35300      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35301      */
35302     invalidClass : "x-form-invalid",
35303     /**
35304      * @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")
35305      */
35306     invalidText : "The value in this field is invalid",
35307     /**
35308      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35309      */
35310     focusClass : "x-form-focus",
35311     /**
35312      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35313       automatic validation (defaults to "keyup").
35314      */
35315     validationEvent : "keyup",
35316     /**
35317      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35318      */
35319     validateOnBlur : true,
35320     /**
35321      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35322      */
35323     validationDelay : 250,
35324     /**
35325      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35326      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35327      */
35328     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35329     /**
35330      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35331      */
35332     fieldClass : "x-form-field",
35333     /**
35334      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35335      *<pre>
35336 Value         Description
35337 -----------   ----------------------------------------------------------------------
35338 qtip          Display a quick tip when the user hovers over the field
35339 title         Display a default browser title attribute popup
35340 under         Add a block div beneath the field containing the error text
35341 side          Add an error icon to the right of the field with a popup on hover
35342 [element id]  Add the error text directly to the innerHTML of the specified element
35343 </pre>
35344      */
35345     msgTarget : 'qtip',
35346     /**
35347      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35348      */
35349     msgFx : 'normal',
35350
35351     /**
35352      * @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.
35353      */
35354     readOnly : false,
35355
35356     /**
35357      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35358      */
35359     disabled : false,
35360
35361     /**
35362      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35363      */
35364     inputType : undefined,
35365     
35366     /**
35367      * @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).
35368          */
35369         tabIndex : undefined,
35370         
35371     // private
35372     isFormField : true,
35373
35374     // private
35375     hasFocus : false,
35376     /**
35377      * @property {Roo.Element} fieldEl
35378      * Element Containing the rendered Field (with label etc.)
35379      */
35380     /**
35381      * @cfg {Mixed} value A value to initialize this field with.
35382      */
35383     value : undefined,
35384
35385     /**
35386      * @cfg {String} name The field's HTML name attribute.
35387      */
35388     /**
35389      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35390      */
35391
35392         // private ??
35393         initComponent : function(){
35394         Roo.form.Field.superclass.initComponent.call(this);
35395         this.addEvents({
35396             /**
35397              * @event focus
35398              * Fires when this field receives input focus.
35399              * @param {Roo.form.Field} this
35400              */
35401             focus : true,
35402             /**
35403              * @event blur
35404              * Fires when this field loses input focus.
35405              * @param {Roo.form.Field} this
35406              */
35407             blur : true,
35408             /**
35409              * @event specialkey
35410              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35411              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35412              * @param {Roo.form.Field} this
35413              * @param {Roo.EventObject} e The event object
35414              */
35415             specialkey : true,
35416             /**
35417              * @event change
35418              * Fires just before the field blurs if the field value has changed.
35419              * @param {Roo.form.Field} this
35420              * @param {Mixed} newValue The new value
35421              * @param {Mixed} oldValue The original value
35422              */
35423             change : true,
35424             /**
35425              * @event invalid
35426              * Fires after the field has been marked as invalid.
35427              * @param {Roo.form.Field} this
35428              * @param {String} msg The validation message
35429              */
35430             invalid : true,
35431             /**
35432              * @event valid
35433              * Fires after the field has been validated with no errors.
35434              * @param {Roo.form.Field} this
35435              */
35436             valid : true,
35437              /**
35438              * @event keyup
35439              * Fires after the key up
35440              * @param {Roo.form.Field} this
35441              * @param {Roo.EventObject}  e The event Object
35442              */
35443             keyup : true
35444         });
35445     },
35446
35447     /**
35448      * Returns the name attribute of the field if available
35449      * @return {String} name The field name
35450      */
35451     getName: function(){
35452          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35453     },
35454
35455     // private
35456     onRender : function(ct, position){
35457         Roo.form.Field.superclass.onRender.call(this, ct, position);
35458         if(!this.el){
35459             var cfg = this.getAutoCreate();
35460             if(!cfg.name){
35461                 cfg.name = this.name || this.id;
35462             }
35463             if(this.inputType){
35464                 cfg.type = this.inputType;
35465             }
35466             this.el = ct.createChild(cfg, position);
35467         }
35468         var type = this.el.dom.type;
35469         if(type){
35470             if(type == 'password'){
35471                 type = 'text';
35472             }
35473             this.el.addClass('x-form-'+type);
35474         }
35475         if(this.readOnly){
35476             this.el.dom.readOnly = true;
35477         }
35478         if(this.tabIndex !== undefined){
35479             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35480         }
35481
35482         this.el.addClass([this.fieldClass, this.cls]);
35483         this.initValue();
35484     },
35485
35486     /**
35487      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35488      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35489      * @return {Roo.form.Field} this
35490      */
35491     applyTo : function(target){
35492         this.allowDomMove = false;
35493         this.el = Roo.get(target);
35494         this.render(this.el.dom.parentNode);
35495         return this;
35496     },
35497
35498     // private
35499     initValue : function(){
35500         if(this.value !== undefined){
35501             this.setValue(this.value);
35502         }else if(this.el.dom.value.length > 0){
35503             this.setValue(this.el.dom.value);
35504         }
35505     },
35506
35507     /**
35508      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35509      */
35510     isDirty : function() {
35511         if(this.disabled) {
35512             return false;
35513         }
35514         return String(this.getValue()) !== String(this.originalValue);
35515     },
35516
35517     // private
35518     afterRender : function(){
35519         Roo.form.Field.superclass.afterRender.call(this);
35520         this.initEvents();
35521     },
35522
35523     // private
35524     fireKey : function(e){
35525         //Roo.log('field ' + e.getKey());
35526         if(e.isNavKeyPress()){
35527             this.fireEvent("specialkey", this, e);
35528         }
35529     },
35530
35531     /**
35532      * Resets the current field value to the originally loaded value and clears any validation messages
35533      */
35534     reset : function(){
35535         this.setValue(this.originalValue);
35536         this.clearInvalid();
35537     },
35538
35539     // private
35540     initEvents : function(){
35541         // safari killled keypress - so keydown is now used..
35542         this.el.on("keydown" , this.fireKey,  this);
35543         this.el.on("focus", this.onFocus,  this);
35544         this.el.on("blur", this.onBlur,  this);
35545         this.el.relayEvent('keyup', this);
35546
35547         // reference to original value for reset
35548         this.originalValue = this.getValue();
35549     },
35550
35551     // private
35552     onFocus : function(){
35553         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35554             this.el.addClass(this.focusClass);
35555         }
35556         if(!this.hasFocus){
35557             this.hasFocus = true;
35558             this.startValue = this.getValue();
35559             this.fireEvent("focus", this);
35560         }
35561     },
35562
35563     beforeBlur : Roo.emptyFn,
35564
35565     // private
35566     onBlur : function(){
35567         this.beforeBlur();
35568         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35569             this.el.removeClass(this.focusClass);
35570         }
35571         this.hasFocus = false;
35572         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35573             this.validate();
35574         }
35575         var v = this.getValue();
35576         if(String(v) !== String(this.startValue)){
35577             this.fireEvent('change', this, v, this.startValue);
35578         }
35579         this.fireEvent("blur", this);
35580     },
35581
35582     /**
35583      * Returns whether or not the field value is currently valid
35584      * @param {Boolean} preventMark True to disable marking the field invalid
35585      * @return {Boolean} True if the value is valid, else false
35586      */
35587     isValid : function(preventMark){
35588         if(this.disabled){
35589             return true;
35590         }
35591         var restore = this.preventMark;
35592         this.preventMark = preventMark === true;
35593         var v = this.validateValue(this.processValue(this.getRawValue()));
35594         this.preventMark = restore;
35595         return v;
35596     },
35597
35598     /**
35599      * Validates the field value
35600      * @return {Boolean} True if the value is valid, else false
35601      */
35602     validate : function(){
35603         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35604             this.clearInvalid();
35605             return true;
35606         }
35607         return false;
35608     },
35609
35610     processValue : function(value){
35611         return value;
35612     },
35613
35614     // private
35615     // Subclasses should provide the validation implementation by overriding this
35616     validateValue : function(value){
35617         return true;
35618     },
35619
35620     /**
35621      * Mark this field as invalid
35622      * @param {String} msg The validation message
35623      */
35624     markInvalid : function(msg){
35625         if(!this.rendered || this.preventMark){ // not rendered
35626             return;
35627         }
35628         this.el.addClass(this.invalidClass);
35629         msg = msg || this.invalidText;
35630         switch(this.msgTarget){
35631             case 'qtip':
35632                 this.el.dom.qtip = msg;
35633                 this.el.dom.qclass = 'x-form-invalid-tip';
35634                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35635                     Roo.QuickTips.enable();
35636                 }
35637                 break;
35638             case 'title':
35639                 this.el.dom.title = msg;
35640                 break;
35641             case 'under':
35642                 if(!this.errorEl){
35643                     var elp = this.el.findParent('.x-form-element', 5, true);
35644                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35645                     this.errorEl.setWidth(elp.getWidth(true)-20);
35646                 }
35647                 this.errorEl.update(msg);
35648                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35649                 break;
35650             case 'side':
35651                 if(!this.errorIcon){
35652                     var elp = this.el.findParent('.x-form-element', 5, true);
35653                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35654                 }
35655                 this.alignErrorIcon();
35656                 this.errorIcon.dom.qtip = msg;
35657                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35658                 this.errorIcon.show();
35659                 this.on('resize', this.alignErrorIcon, this);
35660                 break;
35661             default:
35662                 var t = Roo.getDom(this.msgTarget);
35663                 t.innerHTML = msg;
35664                 t.style.display = this.msgDisplay;
35665                 break;
35666         }
35667         this.fireEvent('invalid', this, msg);
35668     },
35669
35670     // private
35671     alignErrorIcon : function(){
35672         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35673     },
35674
35675     /**
35676      * Clear any invalid styles/messages for this field
35677      */
35678     clearInvalid : function(){
35679         if(!this.rendered || this.preventMark){ // not rendered
35680             return;
35681         }
35682         this.el.removeClass(this.invalidClass);
35683         switch(this.msgTarget){
35684             case 'qtip':
35685                 this.el.dom.qtip = '';
35686                 break;
35687             case 'title':
35688                 this.el.dom.title = '';
35689                 break;
35690             case 'under':
35691                 if(this.errorEl){
35692                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35693                 }
35694                 break;
35695             case 'side':
35696                 if(this.errorIcon){
35697                     this.errorIcon.dom.qtip = '';
35698                     this.errorIcon.hide();
35699                     this.un('resize', this.alignErrorIcon, this);
35700                 }
35701                 break;
35702             default:
35703                 var t = Roo.getDom(this.msgTarget);
35704                 t.innerHTML = '';
35705                 t.style.display = 'none';
35706                 break;
35707         }
35708         this.fireEvent('valid', this);
35709     },
35710
35711     /**
35712      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35713      * @return {Mixed} value The field value
35714      */
35715     getRawValue : function(){
35716         var v = this.el.getValue();
35717         if(v === this.emptyText){
35718             v = '';
35719         }
35720         return v;
35721     },
35722
35723     /**
35724      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35725      * @return {Mixed} value The field value
35726      */
35727     getValue : function(){
35728         var v = this.el.getValue();
35729         if(v === this.emptyText || v === undefined){
35730             v = '';
35731         }
35732         return v;
35733     },
35734
35735     /**
35736      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35737      * @param {Mixed} value The value to set
35738      */
35739     setRawValue : function(v){
35740         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35741     },
35742
35743     /**
35744      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35745      * @param {Mixed} value The value to set
35746      */
35747     setValue : function(v){
35748         this.value = v;
35749         if(this.rendered){
35750             this.el.dom.value = (v === null || v === undefined ? '' : v);
35751             this.validate();
35752         }
35753     },
35754
35755     adjustSize : function(w, h){
35756         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35757         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35758         return s;
35759     },
35760
35761     adjustWidth : function(tag, w){
35762         tag = tag.toLowerCase();
35763         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35764             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35765                 if(tag == 'input'){
35766                     return w + 2;
35767                 }
35768                 if(tag = 'textarea'){
35769                     return w-2;
35770                 }
35771             }else if(Roo.isOpera){
35772                 if(tag == 'input'){
35773                     return w + 2;
35774                 }
35775                 if(tag = 'textarea'){
35776                     return w-2;
35777                 }
35778             }
35779         }
35780         return w;
35781     }
35782 });
35783
35784
35785 // anything other than normal should be considered experimental
35786 Roo.form.Field.msgFx = {
35787     normal : {
35788         show: function(msgEl, f){
35789             msgEl.setDisplayed('block');
35790         },
35791
35792         hide : function(msgEl, f){
35793             msgEl.setDisplayed(false).update('');
35794         }
35795     },
35796
35797     slide : {
35798         show: function(msgEl, f){
35799             msgEl.slideIn('t', {stopFx:true});
35800         },
35801
35802         hide : function(msgEl, f){
35803             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35804         }
35805     },
35806
35807     slideRight : {
35808         show: function(msgEl, f){
35809             msgEl.fixDisplay();
35810             msgEl.alignTo(f.el, 'tl-tr');
35811             msgEl.slideIn('l', {stopFx:true});
35812         },
35813
35814         hide : function(msgEl, f){
35815             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35816         }
35817     }
35818 };/*
35819  * Based on:
35820  * Ext JS Library 1.1.1
35821  * Copyright(c) 2006-2007, Ext JS, LLC.
35822  *
35823  * Originally Released Under LGPL - original licence link has changed is not relivant.
35824  *
35825  * Fork - LGPL
35826  * <script type="text/javascript">
35827  */
35828  
35829
35830 /**
35831  * @class Roo.form.TextField
35832  * @extends Roo.form.Field
35833  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35834  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35835  * @constructor
35836  * Creates a new TextField
35837  * @param {Object} config Configuration options
35838  */
35839 Roo.form.TextField = function(config){
35840     Roo.form.TextField.superclass.constructor.call(this, config);
35841     this.addEvents({
35842         /**
35843          * @event autosize
35844          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35845          * according to the default logic, but this event provides a hook for the developer to apply additional
35846          * logic at runtime to resize the field if needed.
35847              * @param {Roo.form.Field} this This text field
35848              * @param {Number} width The new field width
35849              */
35850         autosize : true
35851     });
35852 };
35853
35854 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35855     /**
35856      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35857      */
35858     grow : false,
35859     /**
35860      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35861      */
35862     growMin : 30,
35863     /**
35864      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35865      */
35866     growMax : 800,
35867     /**
35868      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35869      */
35870     vtype : null,
35871     /**
35872      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35873      */
35874     maskRe : null,
35875     /**
35876      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35877      */
35878     disableKeyFilter : false,
35879     /**
35880      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35881      */
35882     allowBlank : true,
35883     /**
35884      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35885      */
35886     minLength : 0,
35887     /**
35888      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35889      */
35890     maxLength : Number.MAX_VALUE,
35891     /**
35892      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35893      */
35894     minLengthText : "The minimum length for this field is {0}",
35895     /**
35896      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35897      */
35898     maxLengthText : "The maximum length for this field is {0}",
35899     /**
35900      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35901      */
35902     selectOnFocus : false,
35903     /**
35904      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35905      */
35906     blankText : "This field is required",
35907     /**
35908      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35909      * If available, this function will be called only after the basic validators all return true, and will be passed the
35910      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35911      */
35912     validator : null,
35913     /**
35914      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35915      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35916      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35917      */
35918     regex : null,
35919     /**
35920      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35921      */
35922     regexText : "",
35923     /**
35924      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35925      */
35926     emptyText : null,
35927     /**
35928      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35929      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35930      */
35931     emptyClass : 'x-form-empty-field',
35932
35933     // private
35934     initEvents : function(){
35935         Roo.form.TextField.superclass.initEvents.call(this);
35936         if(this.validationEvent == 'keyup'){
35937             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35938             this.el.on('keyup', this.filterValidation, this);
35939         }
35940         else if(this.validationEvent !== false){
35941             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35942         }
35943         if(this.selectOnFocus || this.emptyText){
35944             this.on("focus", this.preFocus, this);
35945             if(this.emptyText){
35946                 this.on('blur', this.postBlur, this);
35947                 this.applyEmptyText();
35948             }
35949         }
35950         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35951             this.el.on("keypress", this.filterKeys, this);
35952         }
35953         if(this.grow){
35954             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35955             this.el.on("click", this.autoSize,  this);
35956         }
35957     },
35958
35959     processValue : function(value){
35960         if(this.stripCharsRe){
35961             var newValue = value.replace(this.stripCharsRe, '');
35962             if(newValue !== value){
35963                 this.setRawValue(newValue);
35964                 return newValue;
35965             }
35966         }
35967         return value;
35968     },
35969
35970     filterValidation : function(e){
35971         if(!e.isNavKeyPress()){
35972             this.validationTask.delay(this.validationDelay);
35973         }
35974     },
35975
35976     // private
35977     onKeyUp : function(e){
35978         if(!e.isNavKeyPress()){
35979             this.autoSize();
35980         }
35981     },
35982
35983     /**
35984      * Resets the current field value to the originally-loaded value and clears any validation messages.
35985      * Also adds emptyText and emptyClass if the original value was blank.
35986      */
35987     reset : function(){
35988         Roo.form.TextField.superclass.reset.call(this);
35989         this.applyEmptyText();
35990     },
35991
35992     applyEmptyText : function(){
35993         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35994             this.setRawValue(this.emptyText);
35995             this.el.addClass(this.emptyClass);
35996         }
35997     },
35998
35999     // private
36000     preFocus : function(){
36001         if(this.emptyText){
36002             if(this.el.dom.value == this.emptyText){
36003                 this.setRawValue('');
36004             }
36005             this.el.removeClass(this.emptyClass);
36006         }
36007         if(this.selectOnFocus){
36008             this.el.dom.select();
36009         }
36010     },
36011
36012     // private
36013     postBlur : function(){
36014         this.applyEmptyText();
36015     },
36016
36017     // private
36018     filterKeys : function(e){
36019         var k = e.getKey();
36020         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36021             return;
36022         }
36023         var c = e.getCharCode(), cc = String.fromCharCode(c);
36024         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36025             return;
36026         }
36027         if(!this.maskRe.test(cc)){
36028             e.stopEvent();
36029         }
36030     },
36031
36032     setValue : function(v){
36033         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36034             this.el.removeClass(this.emptyClass);
36035         }
36036         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36037         this.applyEmptyText();
36038         this.autoSize();
36039     },
36040
36041     /**
36042      * Validates a value according to the field's validation rules and marks the field as invalid
36043      * if the validation fails
36044      * @param {Mixed} value The value to validate
36045      * @return {Boolean} True if the value is valid, else false
36046      */
36047     validateValue : function(value){
36048         if(value.length < 1 || value === this.emptyText){ // if it's blank
36049              if(this.allowBlank){
36050                 this.clearInvalid();
36051                 return true;
36052              }else{
36053                 this.markInvalid(this.blankText);
36054                 return false;
36055              }
36056         }
36057         if(value.length < this.minLength){
36058             this.markInvalid(String.format(this.minLengthText, this.minLength));
36059             return false;
36060         }
36061         if(value.length > this.maxLength){
36062             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36063             return false;
36064         }
36065         if(this.vtype){
36066             var vt = Roo.form.VTypes;
36067             if(!vt[this.vtype](value, this)){
36068                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36069                 return false;
36070             }
36071         }
36072         if(typeof this.validator == "function"){
36073             var msg = this.validator(value);
36074             if(msg !== true){
36075                 this.markInvalid(msg);
36076                 return false;
36077             }
36078         }
36079         if(this.regex && !this.regex.test(value)){
36080             this.markInvalid(this.regexText);
36081             return false;
36082         }
36083         return true;
36084     },
36085
36086     /**
36087      * Selects text in this field
36088      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36089      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36090      */
36091     selectText : function(start, end){
36092         var v = this.getRawValue();
36093         if(v.length > 0){
36094             start = start === undefined ? 0 : start;
36095             end = end === undefined ? v.length : end;
36096             var d = this.el.dom;
36097             if(d.setSelectionRange){
36098                 d.setSelectionRange(start, end);
36099             }else if(d.createTextRange){
36100                 var range = d.createTextRange();
36101                 range.moveStart("character", start);
36102                 range.moveEnd("character", v.length-end);
36103                 range.select();
36104             }
36105         }
36106     },
36107
36108     /**
36109      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36110      * This only takes effect if grow = true, and fires the autosize event.
36111      */
36112     autoSize : function(){
36113         if(!this.grow || !this.rendered){
36114             return;
36115         }
36116         if(!this.metrics){
36117             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36118         }
36119         var el = this.el;
36120         var v = el.dom.value;
36121         var d = document.createElement('div');
36122         d.appendChild(document.createTextNode(v));
36123         v = d.innerHTML;
36124         d = null;
36125         v += "&#160;";
36126         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36127         this.el.setWidth(w);
36128         this.fireEvent("autosize", this, w);
36129     }
36130 });/*
36131  * Based on:
36132  * Ext JS Library 1.1.1
36133  * Copyright(c) 2006-2007, Ext JS, LLC.
36134  *
36135  * Originally Released Under LGPL - original licence link has changed is not relivant.
36136  *
36137  * Fork - LGPL
36138  * <script type="text/javascript">
36139  */
36140  
36141 /**
36142  * @class Roo.form.Hidden
36143  * @extends Roo.form.TextField
36144  * Simple Hidden element used on forms 
36145  * 
36146  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36147  * 
36148  * @constructor
36149  * Creates a new Hidden form element.
36150  * @param {Object} config Configuration options
36151  */
36152
36153
36154
36155 // easy hidden field...
36156 Roo.form.Hidden = function(config){
36157     Roo.form.Hidden.superclass.constructor.call(this, config);
36158 };
36159   
36160 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36161     fieldLabel:      '',
36162     inputType:      'hidden',
36163     width:          50,
36164     allowBlank:     true,
36165     labelSeparator: '',
36166     hidden:         true,
36167     itemCls :       'x-form-item-display-none'
36168
36169
36170 });
36171
36172
36173 /*
36174  * Based on:
36175  * Ext JS Library 1.1.1
36176  * Copyright(c) 2006-2007, Ext JS, LLC.
36177  *
36178  * Originally Released Under LGPL - original licence link has changed is not relivant.
36179  *
36180  * Fork - LGPL
36181  * <script type="text/javascript">
36182  */
36183  
36184 /**
36185  * @class Roo.form.TriggerField
36186  * @extends Roo.form.TextField
36187  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36188  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36189  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36190  * for which you can provide a custom implementation.  For example:
36191  * <pre><code>
36192 var trigger = new Roo.form.TriggerField();
36193 trigger.onTriggerClick = myTriggerFn;
36194 trigger.applyTo('my-field');
36195 </code></pre>
36196  *
36197  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36198  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36199  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36200  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36201  * @constructor
36202  * Create a new TriggerField.
36203  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36204  * to the base TextField)
36205  */
36206 Roo.form.TriggerField = function(config){
36207     this.mimicing = false;
36208     Roo.form.TriggerField.superclass.constructor.call(this, config);
36209 };
36210
36211 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36212     /**
36213      * @cfg {String} triggerClass A CSS class to apply to the trigger
36214      */
36215     /**
36216      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36217      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36218      */
36219     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36220     /**
36221      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36222      */
36223     hideTrigger:false,
36224
36225     /** @cfg {Boolean} grow @hide */
36226     /** @cfg {Number} growMin @hide */
36227     /** @cfg {Number} growMax @hide */
36228
36229     /**
36230      * @hide 
36231      * @method
36232      */
36233     autoSize: Roo.emptyFn,
36234     // private
36235     monitorTab : true,
36236     // private
36237     deferHeight : true,
36238
36239     
36240     actionMode : 'wrap',
36241     // private
36242     onResize : function(w, h){
36243         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36244         if(typeof w == 'number'){
36245             var x = w - this.trigger.getWidth();
36246             this.el.setWidth(this.adjustWidth('input', x));
36247             this.trigger.setStyle('left', x+'px');
36248         }
36249     },
36250
36251     // private
36252     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36253
36254     // private
36255     getResizeEl : function(){
36256         return this.wrap;
36257     },
36258
36259     // private
36260     getPositionEl : function(){
36261         return this.wrap;
36262     },
36263
36264     // private
36265     alignErrorIcon : function(){
36266         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36267     },
36268
36269     // private
36270     onRender : function(ct, position){
36271         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36272         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36273         this.trigger = this.wrap.createChild(this.triggerConfig ||
36274                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36275         if(this.hideTrigger){
36276             this.trigger.setDisplayed(false);
36277         }
36278         this.initTrigger();
36279         if(!this.width){
36280             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36281         }
36282     },
36283
36284     // private
36285     initTrigger : function(){
36286         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36287         this.trigger.addClassOnOver('x-form-trigger-over');
36288         this.trigger.addClassOnClick('x-form-trigger-click');
36289     },
36290
36291     // private
36292     onDestroy : function(){
36293         if(this.trigger){
36294             this.trigger.removeAllListeners();
36295             this.trigger.remove();
36296         }
36297         if(this.wrap){
36298             this.wrap.remove();
36299         }
36300         Roo.form.TriggerField.superclass.onDestroy.call(this);
36301     },
36302
36303     // private
36304     onFocus : function(){
36305         Roo.form.TriggerField.superclass.onFocus.call(this);
36306         if(!this.mimicing){
36307             this.wrap.addClass('x-trigger-wrap-focus');
36308             this.mimicing = true;
36309             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36310             if(this.monitorTab){
36311                 this.el.on("keydown", this.checkTab, this);
36312             }
36313         }
36314     },
36315
36316     // private
36317     checkTab : function(e){
36318         if(e.getKey() == e.TAB){
36319             this.triggerBlur();
36320         }
36321     },
36322
36323     // private
36324     onBlur : function(){
36325         // do nothing
36326     },
36327
36328     // private
36329     mimicBlur : function(e, t){
36330         if(!this.wrap.contains(t) && this.validateBlur()){
36331             this.triggerBlur();
36332         }
36333     },
36334
36335     // private
36336     triggerBlur : function(){
36337         this.mimicing = false;
36338         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36339         if(this.monitorTab){
36340             this.el.un("keydown", this.checkTab, this);
36341         }
36342         this.wrap.removeClass('x-trigger-wrap-focus');
36343         Roo.form.TriggerField.superclass.onBlur.call(this);
36344     },
36345
36346     // private
36347     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36348     validateBlur : function(e, t){
36349         return true;
36350     },
36351
36352     // private
36353     onDisable : function(){
36354         Roo.form.TriggerField.superclass.onDisable.call(this);
36355         if(this.wrap){
36356             this.wrap.addClass('x-item-disabled');
36357         }
36358     },
36359
36360     // private
36361     onEnable : function(){
36362         Roo.form.TriggerField.superclass.onEnable.call(this);
36363         if(this.wrap){
36364             this.wrap.removeClass('x-item-disabled');
36365         }
36366     },
36367
36368     // private
36369     onShow : function(){
36370         var ae = this.getActionEl();
36371         
36372         if(ae){
36373             ae.dom.style.display = '';
36374             ae.dom.style.visibility = 'visible';
36375         }
36376     },
36377
36378     // private
36379     
36380     onHide : function(){
36381         var ae = this.getActionEl();
36382         ae.dom.style.display = 'none';
36383     },
36384
36385     /**
36386      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36387      * by an implementing function.
36388      * @method
36389      * @param {EventObject} e
36390      */
36391     onTriggerClick : Roo.emptyFn
36392 });
36393
36394 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36395 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36396 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36397 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36398     initComponent : function(){
36399         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36400
36401         this.triggerConfig = {
36402             tag:'span', cls:'x-form-twin-triggers', cn:[
36403             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36404             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36405         ]};
36406     },
36407
36408     getTrigger : function(index){
36409         return this.triggers[index];
36410     },
36411
36412     initTrigger : function(){
36413         var ts = this.trigger.select('.x-form-trigger', true);
36414         this.wrap.setStyle('overflow', 'hidden');
36415         var triggerField = this;
36416         ts.each(function(t, all, index){
36417             t.hide = function(){
36418                 var w = triggerField.wrap.getWidth();
36419                 this.dom.style.display = 'none';
36420                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36421             };
36422             t.show = function(){
36423                 var w = triggerField.wrap.getWidth();
36424                 this.dom.style.display = '';
36425                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36426             };
36427             var triggerIndex = 'Trigger'+(index+1);
36428
36429             if(this['hide'+triggerIndex]){
36430                 t.dom.style.display = 'none';
36431             }
36432             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36433             t.addClassOnOver('x-form-trigger-over');
36434             t.addClassOnClick('x-form-trigger-click');
36435         }, this);
36436         this.triggers = ts.elements;
36437     },
36438
36439     onTrigger1Click : Roo.emptyFn,
36440     onTrigger2Click : Roo.emptyFn
36441 });/*
36442  * Based on:
36443  * Ext JS Library 1.1.1
36444  * Copyright(c) 2006-2007, Ext JS, LLC.
36445  *
36446  * Originally Released Under LGPL - original licence link has changed is not relivant.
36447  *
36448  * Fork - LGPL
36449  * <script type="text/javascript">
36450  */
36451  
36452 /**
36453  * @class Roo.form.TextArea
36454  * @extends Roo.form.TextField
36455  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36456  * support for auto-sizing.
36457  * @constructor
36458  * Creates a new TextArea
36459  * @param {Object} config Configuration options
36460  */
36461 Roo.form.TextArea = function(config){
36462     Roo.form.TextArea.superclass.constructor.call(this, config);
36463     // these are provided exchanges for backwards compat
36464     // minHeight/maxHeight were replaced by growMin/growMax to be
36465     // compatible with TextField growing config values
36466     if(this.minHeight !== undefined){
36467         this.growMin = this.minHeight;
36468     }
36469     if(this.maxHeight !== undefined){
36470         this.growMax = this.maxHeight;
36471     }
36472 };
36473
36474 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36475     /**
36476      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36477      */
36478     growMin : 60,
36479     /**
36480      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36481      */
36482     growMax: 1000,
36483     /**
36484      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36485      * in the field (equivalent to setting overflow: hidden, defaults to false)
36486      */
36487     preventScrollbars: false,
36488     /**
36489      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36490      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36491      */
36492
36493     // private
36494     onRender : function(ct, position){
36495         if(!this.el){
36496             this.defaultAutoCreate = {
36497                 tag: "textarea",
36498                 style:"width:300px;height:60px;",
36499                 autocomplete: "off"
36500             };
36501         }
36502         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36503         if(this.grow){
36504             this.textSizeEl = Roo.DomHelper.append(document.body, {
36505                 tag: "pre", cls: "x-form-grow-sizer"
36506             });
36507             if(this.preventScrollbars){
36508                 this.el.setStyle("overflow", "hidden");
36509             }
36510             this.el.setHeight(this.growMin);
36511         }
36512     },
36513
36514     onDestroy : function(){
36515         if(this.textSizeEl){
36516             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36517         }
36518         Roo.form.TextArea.superclass.onDestroy.call(this);
36519     },
36520
36521     // private
36522     onKeyUp : function(e){
36523         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36524             this.autoSize();
36525         }
36526     },
36527
36528     /**
36529      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36530      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36531      */
36532     autoSize : function(){
36533         if(!this.grow || !this.textSizeEl){
36534             return;
36535         }
36536         var el = this.el;
36537         var v = el.dom.value;
36538         var ts = this.textSizeEl;
36539
36540         ts.innerHTML = '';
36541         ts.appendChild(document.createTextNode(v));
36542         v = ts.innerHTML;
36543
36544         Roo.fly(ts).setWidth(this.el.getWidth());
36545         if(v.length < 1){
36546             v = "&#160;&#160;";
36547         }else{
36548             if(Roo.isIE){
36549                 v = v.replace(/\n/g, '<p>&#160;</p>');
36550             }
36551             v += "&#160;\n&#160;";
36552         }
36553         ts.innerHTML = v;
36554         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36555         if(h != this.lastHeight){
36556             this.lastHeight = h;
36557             this.el.setHeight(h);
36558             this.fireEvent("autosize", this, h);
36559         }
36560     }
36561 });/*
36562  * Based on:
36563  * Ext JS Library 1.1.1
36564  * Copyright(c) 2006-2007, Ext JS, LLC.
36565  *
36566  * Originally Released Under LGPL - original licence link has changed is not relivant.
36567  *
36568  * Fork - LGPL
36569  * <script type="text/javascript">
36570  */
36571  
36572
36573 /**
36574  * @class Roo.form.NumberField
36575  * @extends Roo.form.TextField
36576  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36577  * @constructor
36578  * Creates a new NumberField
36579  * @param {Object} config Configuration options
36580  */
36581 Roo.form.NumberField = function(config){
36582     Roo.form.NumberField.superclass.constructor.call(this, config);
36583 };
36584
36585 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36586     /**
36587      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36588      */
36589     fieldClass: "x-form-field x-form-num-field",
36590     /**
36591      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36592      */
36593     allowDecimals : true,
36594     /**
36595      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36596      */
36597     decimalSeparator : ".",
36598     /**
36599      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36600      */
36601     decimalPrecision : 2,
36602     /**
36603      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36604      */
36605     allowNegative : true,
36606     /**
36607      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36608      */
36609     minValue : Number.NEGATIVE_INFINITY,
36610     /**
36611      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36612      */
36613     maxValue : Number.MAX_VALUE,
36614     /**
36615      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36616      */
36617     minText : "The minimum value for this field is {0}",
36618     /**
36619      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36620      */
36621     maxText : "The maximum value for this field is {0}",
36622     /**
36623      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36624      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36625      */
36626     nanText : "{0} is not a valid number",
36627
36628     // private
36629     initEvents : function(){
36630         Roo.form.NumberField.superclass.initEvents.call(this);
36631         var allowed = "0123456789";
36632         if(this.allowDecimals){
36633             allowed += this.decimalSeparator;
36634         }
36635         if(this.allowNegative){
36636             allowed += "-";
36637         }
36638         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36639         var keyPress = function(e){
36640             var k = e.getKey();
36641             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36642                 return;
36643             }
36644             var c = e.getCharCode();
36645             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36646                 e.stopEvent();
36647             }
36648         };
36649         this.el.on("keypress", keyPress, this);
36650     },
36651
36652     // private
36653     validateValue : function(value){
36654         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36655             return false;
36656         }
36657         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36658              return true;
36659         }
36660         var num = this.parseValue(value);
36661         if(isNaN(num)){
36662             this.markInvalid(String.format(this.nanText, value));
36663             return false;
36664         }
36665         if(num < this.minValue){
36666             this.markInvalid(String.format(this.minText, this.minValue));
36667             return false;
36668         }
36669         if(num > this.maxValue){
36670             this.markInvalid(String.format(this.maxText, this.maxValue));
36671             return false;
36672         }
36673         return true;
36674     },
36675
36676     getValue : function(){
36677         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36678     },
36679
36680     // private
36681     parseValue : function(value){
36682         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36683         return isNaN(value) ? '' : value;
36684     },
36685
36686     // private
36687     fixPrecision : function(value){
36688         var nan = isNaN(value);
36689         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36690             return nan ? '' : value;
36691         }
36692         return parseFloat(value).toFixed(this.decimalPrecision);
36693     },
36694
36695     setValue : function(v){
36696         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36697     },
36698
36699     // private
36700     decimalPrecisionFcn : function(v){
36701         return Math.floor(v);
36702     },
36703
36704     beforeBlur : function(){
36705         var v = this.parseValue(this.getRawValue());
36706         if(v){
36707             this.setValue(this.fixPrecision(v));
36708         }
36709     }
36710 });/*
36711  * Based on:
36712  * Ext JS Library 1.1.1
36713  * Copyright(c) 2006-2007, Ext JS, LLC.
36714  *
36715  * Originally Released Under LGPL - original licence link has changed is not relivant.
36716  *
36717  * Fork - LGPL
36718  * <script type="text/javascript">
36719  */
36720  
36721 /**
36722  * @class Roo.form.DateField
36723  * @extends Roo.form.TriggerField
36724  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36725 * @constructor
36726 * Create a new DateField
36727 * @param {Object} config
36728  */
36729 Roo.form.DateField = function(config){
36730     Roo.form.DateField.superclass.constructor.call(this, config);
36731     
36732       this.addEvents({
36733          
36734         /**
36735          * @event select
36736          * Fires when a date is selected
36737              * @param {Roo.form.DateField} combo This combo box
36738              * @param {Date} date The date selected
36739              */
36740         'select' : true
36741          
36742     });
36743     
36744     
36745     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36746     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36747     this.ddMatch = null;
36748     if(this.disabledDates){
36749         var dd = this.disabledDates;
36750         var re = "(?:";
36751         for(var i = 0; i < dd.length; i++){
36752             re += dd[i];
36753             if(i != dd.length-1) re += "|";
36754         }
36755         this.ddMatch = new RegExp(re + ")");
36756     }
36757 };
36758
36759 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36760     /**
36761      * @cfg {String} format
36762      * The default date format string which can be overriden for localization support.  The format must be
36763      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36764      */
36765     format : "m/d/y",
36766     /**
36767      * @cfg {String} altFormats
36768      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36769      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36770      */
36771     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36772     /**
36773      * @cfg {Array} disabledDays
36774      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36775      */
36776     disabledDays : null,
36777     /**
36778      * @cfg {String} disabledDaysText
36779      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36780      */
36781     disabledDaysText : "Disabled",
36782     /**
36783      * @cfg {Array} disabledDates
36784      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36785      * expression so they are very powerful. Some examples:
36786      * <ul>
36787      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36788      * <li>["03/08", "09/16"] would disable those days for every year</li>
36789      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36790      * <li>["03/../2006"] would disable every day in March 2006</li>
36791      * <li>["^03"] would disable every day in every March</li>
36792      * </ul>
36793      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36794      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36795      */
36796     disabledDates : null,
36797     /**
36798      * @cfg {String} disabledDatesText
36799      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36800      */
36801     disabledDatesText : "Disabled",
36802     /**
36803      * @cfg {Date/String} minValue
36804      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36805      * valid format (defaults to null).
36806      */
36807     minValue : null,
36808     /**
36809      * @cfg {Date/String} maxValue
36810      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36811      * valid format (defaults to null).
36812      */
36813     maxValue : null,
36814     /**
36815      * @cfg {String} minText
36816      * The error text to display when the date in the cell is before minValue (defaults to
36817      * 'The date in this field must be after {minValue}').
36818      */
36819     minText : "The date in this field must be equal to or after {0}",
36820     /**
36821      * @cfg {String} maxText
36822      * The error text to display when the date in the cell is after maxValue (defaults to
36823      * 'The date in this field must be before {maxValue}').
36824      */
36825     maxText : "The date in this field must be equal to or before {0}",
36826     /**
36827      * @cfg {String} invalidText
36828      * The error text to display when the date in the field is invalid (defaults to
36829      * '{value} is not a valid date - it must be in the format {format}').
36830      */
36831     invalidText : "{0} is not a valid date - it must be in the format {1}",
36832     /**
36833      * @cfg {String} triggerClass
36834      * An additional CSS class used to style the trigger button.  The trigger will always get the
36835      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36836      * which displays a calendar icon).
36837      */
36838     triggerClass : 'x-form-date-trigger',
36839     
36840
36841     /**
36842      * @cfg {bool} useIso
36843      * if enabled, then the date field will use a hidden field to store the 
36844      * real value as iso formated date. default (false)
36845      */ 
36846     useIso : false,
36847     /**
36848      * @cfg {String/Object} autoCreate
36849      * A DomHelper element spec, or true for a default element spec (defaults to
36850      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36851      */ 
36852     // private
36853     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36854     
36855     // private
36856     hiddenField: false,
36857     
36858     onRender : function(ct, position)
36859     {
36860         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36861         if (this.useIso) {
36862             this.el.dom.removeAttribute('name'); 
36863             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36864                     'before', true);
36865             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36866             // prevent input submission
36867             this.hiddenName = this.name;
36868         }
36869             
36870             
36871     },
36872     
36873     // private
36874     validateValue : function(value)
36875     {
36876         value = this.formatDate(value);
36877         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36878             return false;
36879         }
36880         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36881              return true;
36882         }
36883         var svalue = value;
36884         value = this.parseDate(value);
36885         if(!value){
36886             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36887             return false;
36888         }
36889         var time = value.getTime();
36890         if(this.minValue && time < this.minValue.getTime()){
36891             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36892             return false;
36893         }
36894         if(this.maxValue && time > this.maxValue.getTime()){
36895             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36896             return false;
36897         }
36898         if(this.disabledDays){
36899             var day = value.getDay();
36900             for(var i = 0; i < this.disabledDays.length; i++) {
36901                 if(day === this.disabledDays[i]){
36902                     this.markInvalid(this.disabledDaysText);
36903                     return false;
36904                 }
36905             }
36906         }
36907         var fvalue = this.formatDate(value);
36908         if(this.ddMatch && this.ddMatch.test(fvalue)){
36909             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36910             return false;
36911         }
36912         return true;
36913     },
36914
36915     // private
36916     // Provides logic to override the default TriggerField.validateBlur which just returns true
36917     validateBlur : function(){
36918         return !this.menu || !this.menu.isVisible();
36919     },
36920
36921     /**
36922      * Returns the current date value of the date field.
36923      * @return {Date} The date value
36924      */
36925     getValue : function(){
36926         
36927         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36928     },
36929
36930     /**
36931      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36932      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36933      * (the default format used is "m/d/y").
36934      * <br />Usage:
36935      * <pre><code>
36936 //All of these calls set the same date value (May 4, 2006)
36937
36938 //Pass a date object:
36939 var dt = new Date('5/4/06');
36940 dateField.setValue(dt);
36941
36942 //Pass a date string (default format):
36943 dateField.setValue('5/4/06');
36944
36945 //Pass a date string (custom format):
36946 dateField.format = 'Y-m-d';
36947 dateField.setValue('2006-5-4');
36948 </code></pre>
36949      * @param {String/Date} date The date or valid date string
36950      */
36951     setValue : function(date){
36952         if (this.hiddenField) {
36953             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36954         }
36955         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36956     },
36957
36958     // private
36959     parseDate : function(value){
36960         if(!value || value instanceof Date){
36961             return value;
36962         }
36963         var v = Date.parseDate(value, this.format);
36964         if(!v && this.altFormats){
36965             if(!this.altFormatsArray){
36966                 this.altFormatsArray = this.altFormats.split("|");
36967             }
36968             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36969                 v = Date.parseDate(value, this.altFormatsArray[i]);
36970             }
36971         }
36972         return v;
36973     },
36974
36975     // private
36976     formatDate : function(date, fmt){
36977         return (!date || !(date instanceof Date)) ?
36978                date : date.dateFormat(fmt || this.format);
36979     },
36980
36981     // private
36982     menuListeners : {
36983         select: function(m, d){
36984             this.setValue(d);
36985             this.fireEvent('select', this, d);
36986         },
36987         show : function(){ // retain focus styling
36988             this.onFocus();
36989         },
36990         hide : function(){
36991             this.focus.defer(10, this);
36992             var ml = this.menuListeners;
36993             this.menu.un("select", ml.select,  this);
36994             this.menu.un("show", ml.show,  this);
36995             this.menu.un("hide", ml.hide,  this);
36996         }
36997     },
36998
36999     // private
37000     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37001     onTriggerClick : function(){
37002         if(this.disabled){
37003             return;
37004         }
37005         if(this.menu == null){
37006             this.menu = new Roo.menu.DateMenu();
37007         }
37008         Roo.apply(this.menu.picker,  {
37009             showClear: this.allowBlank,
37010             minDate : this.minValue,
37011             maxDate : this.maxValue,
37012             disabledDatesRE : this.ddMatch,
37013             disabledDatesText : this.disabledDatesText,
37014             disabledDays : this.disabledDays,
37015             disabledDaysText : this.disabledDaysText,
37016             format : this.format,
37017             minText : String.format(this.minText, this.formatDate(this.minValue)),
37018             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37019         });
37020         this.menu.on(Roo.apply({}, this.menuListeners, {
37021             scope:this
37022         }));
37023         this.menu.picker.setValue(this.getValue() || new Date());
37024         this.menu.show(this.el, "tl-bl?");
37025     },
37026
37027     beforeBlur : function(){
37028         var v = this.parseDate(this.getRawValue());
37029         if(v){
37030             this.setValue(v);
37031         }
37032     }
37033
37034     /** @cfg {Boolean} grow @hide */
37035     /** @cfg {Number} growMin @hide */
37036     /** @cfg {Number} growMax @hide */
37037     /**
37038      * @hide
37039      * @method autoSize
37040      */
37041 });/*
37042  * Based on:
37043  * Ext JS Library 1.1.1
37044  * Copyright(c) 2006-2007, Ext JS, LLC.
37045  *
37046  * Originally Released Under LGPL - original licence link has changed is not relivant.
37047  *
37048  * Fork - LGPL
37049  * <script type="text/javascript">
37050  */
37051  
37052
37053 /**
37054  * @class Roo.form.ComboBox
37055  * @extends Roo.form.TriggerField
37056  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37057  * @constructor
37058  * Create a new ComboBox.
37059  * @param {Object} config Configuration options
37060  */
37061 Roo.form.ComboBox = function(config){
37062     Roo.form.ComboBox.superclass.constructor.call(this, config);
37063     this.addEvents({
37064         /**
37065          * @event expand
37066          * Fires when the dropdown list is expanded
37067              * @param {Roo.form.ComboBox} combo This combo box
37068              */
37069         'expand' : true,
37070         /**
37071          * @event collapse
37072          * Fires when the dropdown list is collapsed
37073              * @param {Roo.form.ComboBox} combo This combo box
37074              */
37075         'collapse' : true,
37076         /**
37077          * @event beforeselect
37078          * Fires before a list item is selected. Return false to cancel the selection.
37079              * @param {Roo.form.ComboBox} combo This combo box
37080              * @param {Roo.data.Record} record The data record returned from the underlying store
37081              * @param {Number} index The index of the selected item in the dropdown list
37082              */
37083         'beforeselect' : true,
37084         /**
37085          * @event select
37086          * Fires when a list item is selected
37087              * @param {Roo.form.ComboBox} combo This combo box
37088              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37089              * @param {Number} index The index of the selected item in the dropdown list
37090              */
37091         'select' : true,
37092         /**
37093          * @event beforequery
37094          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37095          * The event object passed has these properties:
37096              * @param {Roo.form.ComboBox} combo This combo box
37097              * @param {String} query The query
37098              * @param {Boolean} forceAll true to force "all" query
37099              * @param {Boolean} cancel true to cancel the query
37100              * @param {Object} e The query event object
37101              */
37102         'beforequery': true,
37103          /**
37104          * @event add
37105          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37106              * @param {Roo.form.ComboBox} combo This combo box
37107              */
37108         'add' : true,
37109         /**
37110          * @event edit
37111          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37112              * @param {Roo.form.ComboBox} combo This combo box
37113              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37114              */
37115         'edit' : true
37116         
37117         
37118     });
37119     if(this.transform){
37120         this.allowDomMove = false;
37121         var s = Roo.getDom(this.transform);
37122         if(!this.hiddenName){
37123             this.hiddenName = s.name;
37124         }
37125         if(!this.store){
37126             this.mode = 'local';
37127             var d = [], opts = s.options;
37128             for(var i = 0, len = opts.length;i < len; i++){
37129                 var o = opts[i];
37130                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37131                 if(o.selected) {
37132                     this.value = value;
37133                 }
37134                 d.push([value, o.text]);
37135             }
37136             this.store = new Roo.data.SimpleStore({
37137                 'id': 0,
37138                 fields: ['value', 'text'],
37139                 data : d
37140             });
37141             this.valueField = 'value';
37142             this.displayField = 'text';
37143         }
37144         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37145         if(!this.lazyRender){
37146             this.target = true;
37147             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37148             s.parentNode.removeChild(s); // remove it
37149             this.render(this.el.parentNode);
37150         }else{
37151             s.parentNode.removeChild(s); // remove it
37152         }
37153
37154     }
37155     if (this.store) {
37156         this.store = Roo.factory(this.store, Roo.data);
37157     }
37158     
37159     this.selectedIndex = -1;
37160     if(this.mode == 'local'){
37161         if(config.queryDelay === undefined){
37162             this.queryDelay = 10;
37163         }
37164         if(config.minChars === undefined){
37165             this.minChars = 0;
37166         }
37167     }
37168 };
37169
37170 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37171     /**
37172      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37173      */
37174     /**
37175      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37176      * rendering into an Roo.Editor, defaults to false)
37177      */
37178     /**
37179      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37180      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37181      */
37182     /**
37183      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37184      */
37185     /**
37186      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37187      * the dropdown list (defaults to undefined, with no header element)
37188      */
37189
37190      /**
37191      * @cfg {String/Roo.Template} tpl The template to use to render the output
37192      */
37193      
37194     // private
37195     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37196     /**
37197      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37198      */
37199     listWidth: undefined,
37200     /**
37201      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37202      * mode = 'remote' or 'text' if mode = 'local')
37203      */
37204     displayField: undefined,
37205     /**
37206      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37207      * mode = 'remote' or 'value' if mode = 'local'). 
37208      * Note: use of a valueField requires the user make a selection
37209      * in order for a value to be mapped.
37210      */
37211     valueField: undefined,
37212     /**
37213      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37214      * field's data value (defaults to the underlying DOM element's name)
37215      */
37216     hiddenName: undefined,
37217     /**
37218      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37219      */
37220     listClass: '',
37221     /**
37222      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37223      */
37224     selectedClass: 'x-combo-selected',
37225     /**
37226      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37227      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37228      * which displays a downward arrow icon).
37229      */
37230     triggerClass : 'x-form-arrow-trigger',
37231     /**
37232      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37233      */
37234     shadow:'sides',
37235     /**
37236      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37237      * anchor positions (defaults to 'tl-bl')
37238      */
37239     listAlign: 'tl-bl?',
37240     /**
37241      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37242      */
37243     maxHeight: 300,
37244     /**
37245      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37246      * query specified by the allQuery config option (defaults to 'query')
37247      */
37248     triggerAction: 'query',
37249     /**
37250      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37251      * (defaults to 4, does not apply if editable = false)
37252      */
37253     minChars : 4,
37254     /**
37255      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37256      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37257      */
37258     typeAhead: false,
37259     /**
37260      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37261      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37262      */
37263     queryDelay: 500,
37264     /**
37265      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37266      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37267      */
37268     pageSize: 0,
37269     /**
37270      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37271      * when editable = true (defaults to false)
37272      */
37273     selectOnFocus:false,
37274     /**
37275      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37276      */
37277     queryParam: 'query',
37278     /**
37279      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37280      * when mode = 'remote' (defaults to 'Loading...')
37281      */
37282     loadingText: 'Loading...',
37283     /**
37284      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37285      */
37286     resizable: false,
37287     /**
37288      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37289      */
37290     handleHeight : 8,
37291     /**
37292      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37293      * traditional select (defaults to true)
37294      */
37295     editable: true,
37296     /**
37297      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37298      */
37299     allQuery: '',
37300     /**
37301      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37302      */
37303     mode: 'remote',
37304     /**
37305      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37306      * listWidth has a higher value)
37307      */
37308     minListWidth : 70,
37309     /**
37310      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37311      * allow the user to set arbitrary text into the field (defaults to false)
37312      */
37313     forceSelection:false,
37314     /**
37315      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37316      * if typeAhead = true (defaults to 250)
37317      */
37318     typeAheadDelay : 250,
37319     /**
37320      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37321      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37322      */
37323     valueNotFoundText : undefined,
37324     /**
37325      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37326      */
37327     blockFocus : false,
37328     
37329     /**
37330      * @cfg {Boolean} disableClear Disable showing of clear button.
37331      */
37332     disableClear : false,
37333     /**
37334      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37335      */
37336     alwaysQuery : false,
37337     
37338     //private
37339     addicon : false,
37340     editicon: false,
37341     
37342     
37343     // private
37344     onRender : function(ct, position){
37345         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37346         if(this.hiddenName){
37347             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37348                     'before', true);
37349             this.hiddenField.value =
37350                 this.hiddenValue !== undefined ? this.hiddenValue :
37351                 this.value !== undefined ? this.value : '';
37352
37353             // prevent input submission
37354             this.el.dom.removeAttribute('name');
37355         }
37356         if(Roo.isGecko){
37357             this.el.dom.setAttribute('autocomplete', 'off');
37358         }
37359
37360         var cls = 'x-combo-list';
37361
37362         this.list = new Roo.Layer({
37363             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37364         });
37365
37366         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37367         this.list.setWidth(lw);
37368         this.list.swallowEvent('mousewheel');
37369         this.assetHeight = 0;
37370
37371         if(this.title){
37372             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37373             this.assetHeight += this.header.getHeight();
37374         }
37375
37376         this.innerList = this.list.createChild({cls:cls+'-inner'});
37377         this.innerList.on('mouseover', this.onViewOver, this);
37378         this.innerList.on('mousemove', this.onViewMove, this);
37379         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37380         
37381         if(this.allowBlank && !this.pageSize && !this.disableClear){
37382             this.footer = this.list.createChild({cls:cls+'-ft'});
37383             this.pageTb = new Roo.Toolbar(this.footer);
37384            
37385         }
37386         if(this.pageSize){
37387             this.footer = this.list.createChild({cls:cls+'-ft'});
37388             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37389                     {pageSize: this.pageSize});
37390             
37391         }
37392         
37393         if (this.pageTb && this.allowBlank && !this.disableClear) {
37394             var _this = this;
37395             this.pageTb.add(new Roo.Toolbar.Fill(), {
37396                 cls: 'x-btn-icon x-btn-clear',
37397                 text: '&#160;',
37398                 handler: function()
37399                 {
37400                     _this.collapse();
37401                     _this.clearValue();
37402                     _this.onSelect(false, -1);
37403                 }
37404             });
37405         }
37406         if (this.footer) {
37407             this.assetHeight += this.footer.getHeight();
37408         }
37409         
37410
37411         if(!this.tpl){
37412             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37413         }
37414
37415         this.view = new Roo.View(this.innerList, this.tpl, {
37416             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37417         });
37418
37419         this.view.on('click', this.onViewClick, this);
37420
37421         this.store.on('beforeload', this.onBeforeLoad, this);
37422         this.store.on('load', this.onLoad, this);
37423         this.store.on('loadexception', this.collapse, this);
37424
37425         if(this.resizable){
37426             this.resizer = new Roo.Resizable(this.list,  {
37427                pinned:true, handles:'se'
37428             });
37429             this.resizer.on('resize', function(r, w, h){
37430                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37431                 this.listWidth = w;
37432                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37433                 this.restrictHeight();
37434             }, this);
37435             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37436         }
37437         if(!this.editable){
37438             this.editable = true;
37439             this.setEditable(false);
37440         }  
37441         
37442         
37443         if (typeof(this.events.add.listeners) != 'undefined') {
37444             
37445             this.addicon = this.wrap.createChild(
37446                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37447        
37448             this.addicon.on('click', function(e) {
37449                 this.fireEvent('add', this);
37450             }, this);
37451         }
37452         if (typeof(this.events.edit.listeners) != 'undefined') {
37453             
37454             this.editicon = this.wrap.createChild(
37455                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37456             if (this.addicon) {
37457                 this.editicon.setStyle('margin-left', '40px');
37458             }
37459             this.editicon.on('click', function(e) {
37460                 
37461                 // we fire even  if inothing is selected..
37462                 this.fireEvent('edit', this, this.lastData );
37463                 
37464             }, this);
37465         }
37466         
37467         
37468         
37469     },
37470
37471     // private
37472     initEvents : function(){
37473         Roo.form.ComboBox.superclass.initEvents.call(this);
37474
37475         this.keyNav = new Roo.KeyNav(this.el, {
37476             "up" : function(e){
37477                 this.inKeyMode = true;
37478                 this.selectPrev();
37479             },
37480
37481             "down" : function(e){
37482                 if(!this.isExpanded()){
37483                     this.onTriggerClick();
37484                 }else{
37485                     this.inKeyMode = true;
37486                     this.selectNext();
37487                 }
37488             },
37489
37490             "enter" : function(e){
37491                 this.onViewClick();
37492                 //return true;
37493             },
37494
37495             "esc" : function(e){
37496                 this.collapse();
37497             },
37498
37499             "tab" : function(e){
37500                 this.onViewClick(false);
37501                 return true;
37502             },
37503
37504             scope : this,
37505
37506             doRelay : function(foo, bar, hname){
37507                 if(hname == 'down' || this.scope.isExpanded()){
37508                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37509                 }
37510                 return true;
37511             },
37512
37513             forceKeyDown: true
37514         });
37515         this.queryDelay = Math.max(this.queryDelay || 10,
37516                 this.mode == 'local' ? 10 : 250);
37517         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37518         if(this.typeAhead){
37519             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37520         }
37521         if(this.editable !== false){
37522             this.el.on("keyup", this.onKeyUp, this);
37523         }
37524         if(this.forceSelection){
37525             this.on('blur', this.doForce, this);
37526         }
37527     },
37528
37529     onDestroy : function(){
37530         if(this.view){
37531             this.view.setStore(null);
37532             this.view.el.removeAllListeners();
37533             this.view.el.remove();
37534             this.view.purgeListeners();
37535         }
37536         if(this.list){
37537             this.list.destroy();
37538         }
37539         if(this.store){
37540             this.store.un('beforeload', this.onBeforeLoad, this);
37541             this.store.un('load', this.onLoad, this);
37542             this.store.un('loadexception', this.collapse, this);
37543         }
37544         Roo.form.ComboBox.superclass.onDestroy.call(this);
37545     },
37546
37547     // private
37548     fireKey : function(e){
37549         if(e.isNavKeyPress() && !this.list.isVisible()){
37550             this.fireEvent("specialkey", this, e);
37551         }
37552     },
37553
37554     // private
37555     onResize: function(w, h){
37556         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37557         
37558         if(typeof w != 'number'){
37559             // we do not handle it!?!?
37560             return;
37561         }
37562         var tw = this.trigger.getWidth();
37563         tw += this.addicon ? this.addicon.getWidth() : 0;
37564         tw += this.editicon ? this.editicon.getWidth() : 0;
37565         var x = w - tw;
37566         this.el.setWidth( this.adjustWidth('input', x));
37567             
37568         this.trigger.setStyle('left', x+'px');
37569         
37570         if(this.list && this.listWidth === undefined){
37571             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37572             this.list.setWidth(lw);
37573             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37574         }
37575         
37576     
37577         
37578     },
37579
37580     /**
37581      * Allow or prevent the user from directly editing the field text.  If false is passed,
37582      * the user will only be able to select from the items defined in the dropdown list.  This method
37583      * is the runtime equivalent of setting the 'editable' config option at config time.
37584      * @param {Boolean} value True to allow the user to directly edit the field text
37585      */
37586     setEditable : function(value){
37587         if(value == this.editable){
37588             return;
37589         }
37590         this.editable = value;
37591         if(!value){
37592             this.el.dom.setAttribute('readOnly', true);
37593             this.el.on('mousedown', this.onTriggerClick,  this);
37594             this.el.addClass('x-combo-noedit');
37595         }else{
37596             this.el.dom.setAttribute('readOnly', false);
37597             this.el.un('mousedown', this.onTriggerClick,  this);
37598             this.el.removeClass('x-combo-noedit');
37599         }
37600     },
37601
37602     // private
37603     onBeforeLoad : function(){
37604         if(!this.hasFocus){
37605             return;
37606         }
37607         this.innerList.update(this.loadingText ?
37608                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37609         this.restrictHeight();
37610         this.selectedIndex = -1;
37611     },
37612
37613     // private
37614     onLoad : function(){
37615         if(!this.hasFocus){
37616             return;
37617         }
37618         if(this.store.getCount() > 0){
37619             this.expand();
37620             this.restrictHeight();
37621             if(this.lastQuery == this.allQuery){
37622                 if(this.editable){
37623                     this.el.dom.select();
37624                 }
37625                 if(!this.selectByValue(this.value, true)){
37626                     this.select(0, true);
37627                 }
37628             }else{
37629                 this.selectNext();
37630                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37631                     this.taTask.delay(this.typeAheadDelay);
37632                 }
37633             }
37634         }else{
37635             this.onEmptyResults();
37636         }
37637         //this.el.focus();
37638     },
37639
37640     // private
37641     onTypeAhead : function(){
37642         if(this.store.getCount() > 0){
37643             var r = this.store.getAt(0);
37644             var newValue = r.data[this.displayField];
37645             var len = newValue.length;
37646             var selStart = this.getRawValue().length;
37647             if(selStart != len){
37648                 this.setRawValue(newValue);
37649                 this.selectText(selStart, newValue.length);
37650             }
37651         }
37652     },
37653
37654     // private
37655     onSelect : function(record, index){
37656         if(this.fireEvent('beforeselect', this, record, index) !== false){
37657             this.setFromData(index > -1 ? record.data : false);
37658             this.collapse();
37659             this.fireEvent('select', this, record, index);
37660         }
37661     },
37662
37663     /**
37664      * Returns the currently selected field value or empty string if no value is set.
37665      * @return {String} value The selected value
37666      */
37667     getValue : function(){
37668         if(this.valueField){
37669             return typeof this.value != 'undefined' ? this.value : '';
37670         }else{
37671             return Roo.form.ComboBox.superclass.getValue.call(this);
37672         }
37673     },
37674
37675     /**
37676      * Clears any text/value currently set in the field
37677      */
37678     clearValue : function(){
37679         if(this.hiddenField){
37680             this.hiddenField.value = '';
37681         }
37682         this.value = '';
37683         this.setRawValue('');
37684         this.lastSelectionText = '';
37685         this.applyEmptyText();
37686     },
37687
37688     /**
37689      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37690      * will be displayed in the field.  If the value does not match the data value of an existing item,
37691      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37692      * Otherwise the field will be blank (although the value will still be set).
37693      * @param {String} value The value to match
37694      */
37695     setValue : function(v){
37696         var text = v;
37697         if(this.valueField){
37698             var r = this.findRecord(this.valueField, v);
37699             if(r){
37700                 text = r.data[this.displayField];
37701             }else if(this.valueNotFoundText !== undefined){
37702                 text = this.valueNotFoundText;
37703             }
37704         }
37705         this.lastSelectionText = text;
37706         if(this.hiddenField){
37707             this.hiddenField.value = v;
37708         }
37709         Roo.form.ComboBox.superclass.setValue.call(this, text);
37710         this.value = v;
37711     },
37712     /**
37713      * @property {Object} the last set data for the element
37714      */
37715     
37716     lastData : false,
37717     /**
37718      * Sets the value of the field based on a object which is related to the record format for the store.
37719      * @param {Object} value the value to set as. or false on reset?
37720      */
37721     setFromData : function(o){
37722         var dv = ''; // display value
37723         var vv = ''; // value value..
37724         this.lastData = o;
37725         if (this.displayField) {
37726             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37727         } else {
37728             // this is an error condition!!!
37729             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37730         }
37731         
37732         if(this.valueField){
37733             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37734         }
37735         if(this.hiddenField){
37736             this.hiddenField.value = vv;
37737             
37738             this.lastSelectionText = dv;
37739             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37740             this.value = vv;
37741             return;
37742         }
37743         // no hidden field.. - we store the value in 'value', but still display
37744         // display field!!!!
37745         this.lastSelectionText = dv;
37746         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37747         this.value = vv;
37748         
37749         
37750     },
37751     // private
37752     reset : function(){
37753         // overridden so that last data is reset..
37754         this.setValue(this.originalValue);
37755         this.clearInvalid();
37756         this.lastData = false;
37757     },
37758     // private
37759     findRecord : function(prop, value){
37760         var record;
37761         if(this.store.getCount() > 0){
37762             this.store.each(function(r){
37763                 if(r.data[prop] == value){
37764                     record = r;
37765                     return false;
37766                 }
37767             });
37768         }
37769         return record;
37770     },
37771
37772     // private
37773     onViewMove : function(e, t){
37774         this.inKeyMode = false;
37775     },
37776
37777     // private
37778     onViewOver : function(e, t){
37779         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37780             return;
37781         }
37782         var item = this.view.findItemFromChild(t);
37783         if(item){
37784             var index = this.view.indexOf(item);
37785             this.select(index, false);
37786         }
37787     },
37788
37789     // private
37790     onViewClick : function(doFocus){
37791         var index = this.view.getSelectedIndexes()[0];
37792         var r = this.store.getAt(index);
37793         if(r){
37794             this.onSelect(r, index);
37795         }
37796         if(doFocus !== false && !this.blockFocus){
37797             this.el.focus();
37798         }
37799     },
37800
37801     // private
37802     restrictHeight : function(){
37803         this.innerList.dom.style.height = '';
37804         var inner = this.innerList.dom;
37805         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37806         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37807         this.list.beginUpdate();
37808         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37809         this.list.alignTo(this.el, this.listAlign);
37810         this.list.endUpdate();
37811     },
37812
37813     // private
37814     onEmptyResults : function(){
37815         this.collapse();
37816     },
37817
37818     /**
37819      * Returns true if the dropdown list is expanded, else false.
37820      */
37821     isExpanded : function(){
37822         return this.list.isVisible();
37823     },
37824
37825     /**
37826      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37827      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37828      * @param {String} value The data value of the item to select
37829      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37830      * selected item if it is not currently in view (defaults to true)
37831      * @return {Boolean} True if the value matched an item in the list, else false
37832      */
37833     selectByValue : function(v, scrollIntoView){
37834         if(v !== undefined && v !== null){
37835             var r = this.findRecord(this.valueField || this.displayField, v);
37836             if(r){
37837                 this.select(this.store.indexOf(r), scrollIntoView);
37838                 return true;
37839             }
37840         }
37841         return false;
37842     },
37843
37844     /**
37845      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37846      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37847      * @param {Number} index The zero-based index of the list item to select
37848      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37849      * selected item if it is not currently in view (defaults to true)
37850      */
37851     select : function(index, scrollIntoView){
37852         this.selectedIndex = index;
37853         this.view.select(index);
37854         if(scrollIntoView !== false){
37855             var el = this.view.getNode(index);
37856             if(el){
37857                 this.innerList.scrollChildIntoView(el, false);
37858             }
37859         }
37860     },
37861
37862     // private
37863     selectNext : function(){
37864         var ct = this.store.getCount();
37865         if(ct > 0){
37866             if(this.selectedIndex == -1){
37867                 this.select(0);
37868             }else if(this.selectedIndex < ct-1){
37869                 this.select(this.selectedIndex+1);
37870             }
37871         }
37872     },
37873
37874     // private
37875     selectPrev : function(){
37876         var ct = this.store.getCount();
37877         if(ct > 0){
37878             if(this.selectedIndex == -1){
37879                 this.select(0);
37880             }else if(this.selectedIndex != 0){
37881                 this.select(this.selectedIndex-1);
37882             }
37883         }
37884     },
37885
37886     // private
37887     onKeyUp : function(e){
37888         if(this.editable !== false && !e.isSpecialKey()){
37889             this.lastKey = e.getKey();
37890             this.dqTask.delay(this.queryDelay);
37891         }
37892     },
37893
37894     // private
37895     validateBlur : function(){
37896         return !this.list || !this.list.isVisible();   
37897     },
37898
37899     // private
37900     initQuery : function(){
37901         this.doQuery(this.getRawValue());
37902     },
37903
37904     // private
37905     doForce : function(){
37906         if(this.el.dom.value.length > 0){
37907             this.el.dom.value =
37908                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37909             this.applyEmptyText();
37910         }
37911     },
37912
37913     /**
37914      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37915      * query allowing the query action to be canceled if needed.
37916      * @param {String} query The SQL query to execute
37917      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37918      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37919      * saved in the current store (defaults to false)
37920      */
37921     doQuery : function(q, forceAll){
37922         if(q === undefined || q === null){
37923             q = '';
37924         }
37925         var qe = {
37926             query: q,
37927             forceAll: forceAll,
37928             combo: this,
37929             cancel:false
37930         };
37931         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37932             return false;
37933         }
37934         q = qe.query;
37935         forceAll = qe.forceAll;
37936         if(forceAll === true || (q.length >= this.minChars)){
37937             if(this.lastQuery != q || this.alwaysQuery){
37938                 this.lastQuery = q;
37939                 if(this.mode == 'local'){
37940                     this.selectedIndex = -1;
37941                     if(forceAll){
37942                         this.store.clearFilter();
37943                     }else{
37944                         this.store.filter(this.displayField, q);
37945                     }
37946                     this.onLoad();
37947                 }else{
37948                     this.store.baseParams[this.queryParam] = q;
37949                     this.store.load({
37950                         params: this.getParams(q)
37951                     });
37952                     this.expand();
37953                 }
37954             }else{
37955                 this.selectedIndex = -1;
37956                 this.onLoad();   
37957             }
37958         }
37959     },
37960
37961     // private
37962     getParams : function(q){
37963         var p = {};
37964         //p[this.queryParam] = q;
37965         if(this.pageSize){
37966             p.start = 0;
37967             p.limit = this.pageSize;
37968         }
37969         return p;
37970     },
37971
37972     /**
37973      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37974      */
37975     collapse : function(){
37976         if(!this.isExpanded()){
37977             return;
37978         }
37979         this.list.hide();
37980         Roo.get(document).un('mousedown', this.collapseIf, this);
37981         Roo.get(document).un('mousewheel', this.collapseIf, this);
37982         if (!this.editable) {
37983             Roo.get(document).un('keydown', this.listKeyPress, this);
37984         }
37985         this.fireEvent('collapse', this);
37986     },
37987
37988     // private
37989     collapseIf : function(e){
37990         if(!e.within(this.wrap) && !e.within(this.list)){
37991             this.collapse();
37992         }
37993     },
37994
37995     /**
37996      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37997      */
37998     expand : function(){
37999         if(this.isExpanded() || !this.hasFocus){
38000             return;
38001         }
38002         this.list.alignTo(this.el, this.listAlign);
38003         this.list.show();
38004         Roo.get(document).on('mousedown', this.collapseIf, this);
38005         Roo.get(document).on('mousewheel', this.collapseIf, this);
38006         if (!this.editable) {
38007             Roo.get(document).on('keydown', this.listKeyPress, this);
38008         }
38009         
38010         this.fireEvent('expand', this);
38011     },
38012
38013     // private
38014     // Implements the default empty TriggerField.onTriggerClick function
38015     onTriggerClick : function(){
38016         if(this.disabled){
38017             return;
38018         }
38019         if(this.isExpanded()){
38020             this.collapse();
38021             if (!this.blockFocus) {
38022                 this.el.focus();
38023             }
38024             
38025         }else {
38026             this.hasFocus = true;
38027             if(this.triggerAction == 'all') {
38028                 this.doQuery(this.allQuery, true);
38029             } else {
38030                 this.doQuery(this.getRawValue());
38031             }
38032             if (!this.blockFocus) {
38033                 this.el.focus();
38034             }
38035         }
38036     },
38037     listKeyPress : function(e)
38038     {
38039         //Roo.log('listkeypress');
38040         // scroll to first matching element based on key pres..
38041         if (e.isSpecialKey()) {
38042             return false;
38043         }
38044         var k = String.fromCharCode(e.getKey()).toUpperCase();
38045         //Roo.log(k);
38046         var match  = false;
38047         var csel = this.view.getSelectedNodes();
38048         var cselitem = false;
38049         if (csel.length) {
38050             var ix = this.view.indexOf(csel[0]);
38051             cselitem  = this.store.getAt(ix);
38052             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38053                 cselitem = false;
38054             }
38055             
38056         }
38057         
38058         this.store.each(function(v) { 
38059             if (cselitem) {
38060                 // start at existing selection.
38061                 if (cselitem.id == v.id) {
38062                     cselitem = false;
38063                 }
38064                 return;
38065             }
38066                 
38067             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38068                 match = this.store.indexOf(v);
38069                 return false;
38070             }
38071         }, this);
38072         
38073         if (match === false) {
38074             return true; // no more action?
38075         }
38076         // scroll to?
38077         this.view.select(match);
38078         var sn = Roo.get(this.view.getSelectedNodes()[0])
38079         sn.scrollIntoView(sn.dom.parentNode, false);
38080     }
38081
38082     /** 
38083     * @cfg {Boolean} grow 
38084     * @hide 
38085     */
38086     /** 
38087     * @cfg {Number} growMin 
38088     * @hide 
38089     */
38090     /** 
38091     * @cfg {Number} growMax 
38092     * @hide 
38093     */
38094     /**
38095      * @hide
38096      * @method autoSize
38097      */
38098 });/*
38099  * Based on:
38100  * Ext JS Library 1.1.1
38101  * Copyright(c) 2006-2007, Ext JS, LLC.
38102  *
38103  * Originally Released Under LGPL - original licence link has changed is not relivant.
38104  *
38105  * Fork - LGPL
38106  * <script type="text/javascript">
38107  */
38108 /**
38109  * @class Roo.form.Checkbox
38110  * @extends Roo.form.Field
38111  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38112  * @constructor
38113  * Creates a new Checkbox
38114  * @param {Object} config Configuration options
38115  */
38116 Roo.form.Checkbox = function(config){
38117     Roo.form.Checkbox.superclass.constructor.call(this, config);
38118     this.addEvents({
38119         /**
38120          * @event check
38121          * Fires when the checkbox is checked or unchecked.
38122              * @param {Roo.form.Checkbox} this This checkbox
38123              * @param {Boolean} checked The new checked value
38124              */
38125         check : true
38126     });
38127 };
38128
38129 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38130     /**
38131      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38132      */
38133     focusClass : undefined,
38134     /**
38135      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38136      */
38137     fieldClass: "x-form-field",
38138     /**
38139      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38140      */
38141     checked: false,
38142     /**
38143      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38144      * {tag: "input", type: "checkbox", autocomplete: "off"})
38145      */
38146     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38147     /**
38148      * @cfg {String} boxLabel The text that appears beside the checkbox
38149      */
38150     boxLabel : "",
38151     /**
38152      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38153      */  
38154     inputValue : '1',
38155     /**
38156      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38157      */
38158      valueOff: '0', // value when not checked..
38159
38160     actionMode : 'viewEl', 
38161     //
38162     // private
38163     itemCls : 'x-menu-check-item x-form-item',
38164     groupClass : 'x-menu-group-item',
38165     inputType : 'hidden',
38166     
38167     
38168     inSetChecked: false, // check that we are not calling self...
38169     
38170     inputElement: false, // real input element?
38171     basedOn: false, // ????
38172     
38173     isFormField: true, // not sure where this is needed!!!!
38174
38175     onResize : function(){
38176         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38177         if(!this.boxLabel){
38178             this.el.alignTo(this.wrap, 'c-c');
38179         }
38180     },
38181
38182     initEvents : function(){
38183         Roo.form.Checkbox.superclass.initEvents.call(this);
38184         this.el.on("click", this.onClick,  this);
38185         this.el.on("change", this.onClick,  this);
38186     },
38187
38188
38189     getResizeEl : function(){
38190         return this.wrap;
38191     },
38192
38193     getPositionEl : function(){
38194         return this.wrap;
38195     },
38196
38197     // private
38198     onRender : function(ct, position){
38199         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38200         /*
38201         if(this.inputValue !== undefined){
38202             this.el.dom.value = this.inputValue;
38203         }
38204         */
38205         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38206         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38207         var viewEl = this.wrap.createChild({ 
38208             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38209         this.viewEl = viewEl;   
38210         this.wrap.on('click', this.onClick,  this); 
38211         
38212         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38213         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38214         
38215         
38216         
38217         if(this.boxLabel){
38218             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38219         //    viewEl.on('click', this.onClick,  this); 
38220         }
38221         //if(this.checked){
38222             this.setChecked(this.checked);
38223         //}else{
38224             //this.checked = this.el.dom;
38225         //}
38226
38227     },
38228
38229     // private
38230     initValue : Roo.emptyFn,
38231
38232     /**
38233      * Returns the checked state of the checkbox.
38234      * @return {Boolean} True if checked, else false
38235      */
38236     getValue : function(){
38237         if(this.el){
38238             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38239         }
38240         return this.valueOff;
38241         
38242     },
38243
38244         // private
38245     onClick : function(){ 
38246         this.setChecked(!this.checked);
38247
38248         //if(this.el.dom.checked != this.checked){
38249         //    this.setValue(this.el.dom.checked);
38250        // }
38251     },
38252
38253     /**
38254      * Sets the checked state of the checkbox.
38255      * On is always based on a string comparison between inputValue and the param.
38256      * @param {Boolean/String} value - the value to set 
38257      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38258      */
38259     setValue : function(v,suppressEvent){
38260         
38261         
38262         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38263         //if(this.el && this.el.dom){
38264         //    this.el.dom.checked = this.checked;
38265         //    this.el.dom.defaultChecked = this.checked;
38266         //}
38267         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38268         //this.fireEvent("check", this, this.checked);
38269     },
38270     // private..
38271     setChecked : function(state,suppressEvent)
38272     {
38273         if (this.inSetChecked) {
38274             this.checked = state;
38275             return;
38276         }
38277         
38278     
38279         if(this.wrap){
38280             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38281         }
38282         this.checked = state;
38283         if(suppressEvent !== true){
38284             this.fireEvent('check', this, state);
38285         }
38286         this.inSetChecked = true;
38287         this.el.dom.value = state ? this.inputValue : this.valueOff;
38288         this.inSetChecked = false;
38289         
38290     },
38291     // handle setting of hidden value by some other method!!?!?
38292     setFromHidden: function()
38293     {
38294         if(!this.el){
38295             return;
38296         }
38297         //console.log("SET FROM HIDDEN");
38298         //alert('setFrom hidden');
38299         this.setValue(this.el.dom.value);
38300     },
38301     
38302     onDestroy : function()
38303     {
38304         if(this.viewEl){
38305             Roo.get(this.viewEl).remove();
38306         }
38307          
38308         Roo.form.Checkbox.superclass.onDestroy.call(this);
38309     }
38310
38311 });/*
38312  * Based on:
38313  * Ext JS Library 1.1.1
38314  * Copyright(c) 2006-2007, Ext JS, LLC.
38315  *
38316  * Originally Released Under LGPL - original licence link has changed is not relivant.
38317  *
38318  * Fork - LGPL
38319  * <script type="text/javascript">
38320  */
38321  
38322 /**
38323  * @class Roo.form.Radio
38324  * @extends Roo.form.Checkbox
38325  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38326  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38327  * @constructor
38328  * Creates a new Radio
38329  * @param {Object} config Configuration options
38330  */
38331 Roo.form.Radio = function(){
38332     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38333 };
38334 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38335     inputType: 'radio',
38336
38337     /**
38338      * If this radio is part of a group, it will return the selected value
38339      * @return {String}
38340      */
38341     getGroupValue : function(){
38342         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38343     }
38344 });//<script type="text/javascript">
38345
38346 /*
38347  * Ext JS Library 1.1.1
38348  * Copyright(c) 2006-2007, Ext JS, LLC.
38349  * licensing@extjs.com
38350  * 
38351  * http://www.extjs.com/license
38352  */
38353  
38354  /*
38355   * 
38356   * Known bugs:
38357   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38358   * - IE ? - no idea how much works there.
38359   * 
38360   * 
38361   * 
38362   */
38363  
38364
38365 /**
38366  * @class Ext.form.HtmlEditor
38367  * @extends Ext.form.Field
38368  * Provides a lightweight HTML Editor component.
38369  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38370  * 
38371  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38372  * supported by this editor.</b><br/><br/>
38373  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38374  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38375  */
38376 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38377       /**
38378      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38379      */
38380     toolbars : false,
38381     /**
38382      * @cfg {String} createLinkText The default text for the create link prompt
38383      */
38384     createLinkText : 'Please enter the URL for the link:',
38385     /**
38386      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38387      */
38388     defaultLinkValue : 'http:/'+'/',
38389    
38390     
38391     // id of frame..
38392     frameId: false,
38393     
38394     // private properties
38395     validationEvent : false,
38396     deferHeight: true,
38397     initialized : false,
38398     activated : false,
38399     sourceEditMode : false,
38400     onFocus : Roo.emptyFn,
38401     iframePad:3,
38402     hideMode:'offsets',
38403     defaultAutoCreate : {
38404         tag: "textarea",
38405         style:"width:500px;height:300px;",
38406         autocomplete: "off"
38407     },
38408
38409     // private
38410     initComponent : function(){
38411         this.addEvents({
38412             /**
38413              * @event initialize
38414              * Fires when the editor is fully initialized (including the iframe)
38415              * @param {HtmlEditor} this
38416              */
38417             initialize: true,
38418             /**
38419              * @event activate
38420              * Fires when the editor is first receives the focus. Any insertion must wait
38421              * until after this event.
38422              * @param {HtmlEditor} this
38423              */
38424             activate: true,
38425              /**
38426              * @event beforesync
38427              * Fires before the textarea is updated with content from the editor iframe. Return false
38428              * to cancel the sync.
38429              * @param {HtmlEditor} this
38430              * @param {String} html
38431              */
38432             beforesync: true,
38433              /**
38434              * @event beforepush
38435              * Fires before the iframe editor is updated with content from the textarea. Return false
38436              * to cancel the push.
38437              * @param {HtmlEditor} this
38438              * @param {String} html
38439              */
38440             beforepush: true,
38441              /**
38442              * @event sync
38443              * Fires when the textarea is updated with content from the editor iframe.
38444              * @param {HtmlEditor} this
38445              * @param {String} html
38446              */
38447             sync: true,
38448              /**
38449              * @event push
38450              * Fires when the iframe editor is updated with content from the textarea.
38451              * @param {HtmlEditor} this
38452              * @param {String} html
38453              */
38454             push: true,
38455              /**
38456              * @event editmodechange
38457              * Fires when the editor switches edit modes
38458              * @param {HtmlEditor} this
38459              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38460              */
38461             editmodechange: true,
38462             /**
38463              * @event editorevent
38464              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38465              * @param {HtmlEditor} this
38466              */
38467             editorevent: true
38468         })
38469     },
38470
38471     /**
38472      * Protected method that will not generally be called directly. It
38473      * is called when the editor creates its toolbar. Override this method if you need to
38474      * add custom toolbar buttons.
38475      * @param {HtmlEditor} editor
38476      */
38477     createToolbar : function(editor){
38478         if (!editor.toolbars || !editor.toolbars.length) {
38479             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38480         }
38481         
38482         for (var i =0 ; i < editor.toolbars.length;i++) {
38483             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38484             editor.toolbars[i].init(editor);
38485         }
38486          
38487         
38488     },
38489
38490     /**
38491      * Protected method that will not generally be called directly. It
38492      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38493      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38494      */
38495     getDocMarkup : function(){
38496         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38497     },
38498
38499     // private
38500     onRender : function(ct, position){
38501         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38502         this.el.dom.style.border = '0 none';
38503         this.el.dom.setAttribute('tabIndex', -1);
38504         this.el.addClass('x-hidden');
38505         if(Roo.isIE){ // fix IE 1px bogus margin
38506             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38507         }
38508         this.wrap = this.el.wrap({
38509             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38510         });
38511
38512         this.frameId = Roo.id();
38513         this.createToolbar(this);
38514         
38515         
38516         
38517         
38518       
38519         
38520         var iframe = this.wrap.createChild({
38521             tag: 'iframe',
38522             id: this.frameId,
38523             name: this.frameId,
38524             frameBorder : 'no',
38525             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38526         });
38527         
38528        // console.log(iframe);
38529         //this.wrap.dom.appendChild(iframe);
38530
38531         this.iframe = iframe.dom;
38532
38533          this.assignDocWin();
38534         
38535         this.doc.designMode = 'on';
38536        
38537         this.doc.open();
38538         this.doc.write(this.getDocMarkup());
38539         this.doc.close();
38540
38541         
38542         var task = { // must defer to wait for browser to be ready
38543             run : function(){
38544                 //console.log("run task?" + this.doc.readyState);
38545                 this.assignDocWin();
38546                 if(this.doc.body || this.doc.readyState == 'complete'){
38547                     try {
38548                         this.doc.designMode="on";
38549                     } catch (e) {
38550                         return;
38551                     }
38552                     Roo.TaskMgr.stop(task);
38553                     this.initEditor.defer(10, this);
38554                 }
38555             },
38556             interval : 10,
38557             duration:10000,
38558             scope: this
38559         };
38560         Roo.TaskMgr.start(task);
38561
38562         if(!this.width){
38563             this.setSize(this.el.getSize());
38564         }
38565     },
38566
38567     // private
38568     onResize : function(w, h){
38569         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38570         if(this.el && this.iframe){
38571             if(typeof w == 'number'){
38572                 var aw = w - this.wrap.getFrameWidth('lr');
38573                 this.el.setWidth(this.adjustWidth('textarea', aw));
38574                 this.iframe.style.width = aw + 'px';
38575             }
38576             if(typeof h == 'number'){
38577                 var tbh = 0;
38578                 for (var i =0; i < this.toolbars.length;i++) {
38579                     // fixme - ask toolbars for heights?
38580                     tbh += this.toolbars[i].tb.el.getHeight();
38581                 }
38582                 
38583                 
38584                 
38585                 
38586                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38587                 this.el.setHeight(this.adjustWidth('textarea', ah));
38588                 this.iframe.style.height = ah + 'px';
38589                 if(this.doc){
38590                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38591                 }
38592             }
38593         }
38594     },
38595
38596     /**
38597      * Toggles the editor between standard and source edit mode.
38598      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38599      */
38600     toggleSourceEdit : function(sourceEditMode){
38601         
38602         this.sourceEditMode = sourceEditMode === true;
38603         
38604         if(this.sourceEditMode){
38605           
38606             this.syncValue();
38607             this.iframe.className = 'x-hidden';
38608             this.el.removeClass('x-hidden');
38609             this.el.dom.removeAttribute('tabIndex');
38610             this.el.focus();
38611         }else{
38612              
38613             this.pushValue();
38614             this.iframe.className = '';
38615             this.el.addClass('x-hidden');
38616             this.el.dom.setAttribute('tabIndex', -1);
38617             this.deferFocus();
38618         }
38619         this.setSize(this.wrap.getSize());
38620         this.fireEvent('editmodechange', this, this.sourceEditMode);
38621     },
38622
38623     // private used internally
38624     createLink : function(){
38625         var url = prompt(this.createLinkText, this.defaultLinkValue);
38626         if(url && url != 'http:/'+'/'){
38627             this.relayCmd('createlink', url);
38628         }
38629     },
38630
38631     // private (for BoxComponent)
38632     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38633
38634     // private (for BoxComponent)
38635     getResizeEl : function(){
38636         return this.wrap;
38637     },
38638
38639     // private (for BoxComponent)
38640     getPositionEl : function(){
38641         return this.wrap;
38642     },
38643
38644     // private
38645     initEvents : function(){
38646         this.originalValue = this.getValue();
38647     },
38648
38649     /**
38650      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38651      * @method
38652      */
38653     markInvalid : Roo.emptyFn,
38654     /**
38655      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38656      * @method
38657      */
38658     clearInvalid : Roo.emptyFn,
38659
38660     setValue : function(v){
38661         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38662         this.pushValue();
38663     },
38664
38665     /**
38666      * Protected method that will not generally be called directly. If you need/want
38667      * custom HTML cleanup, this is the method you should override.
38668      * @param {String} html The HTML to be cleaned
38669      * return {String} The cleaned HTML
38670      */
38671     cleanHtml : function(html){
38672         html = String(html);
38673         if(html.length > 5){
38674             if(Roo.isSafari){ // strip safari nonsense
38675                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38676             }
38677         }
38678         if(html == '&nbsp;'){
38679             html = '';
38680         }
38681         return html;
38682     },
38683
38684     /**
38685      * Protected method that will not generally be called directly. Syncs the contents
38686      * of the editor iframe with the textarea.
38687      */
38688     syncValue : function(){
38689         if(this.initialized){
38690             var bd = (this.doc.body || this.doc.documentElement);
38691             this.cleanUpPaste();
38692             var html = bd.innerHTML;
38693             if(Roo.isSafari){
38694                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38695                 var m = bs.match(/text-align:(.*?);/i);
38696                 if(m && m[1]){
38697                     html = '<div style="'+m[0]+'">' + html + '</div>';
38698                 }
38699             }
38700             html = this.cleanHtml(html);
38701             if(this.fireEvent('beforesync', this, html) !== false){
38702                 this.el.dom.value = html;
38703                 this.fireEvent('sync', this, html);
38704             }
38705         }
38706     },
38707
38708     /**
38709      * Protected method that will not generally be called directly. Pushes the value of the textarea
38710      * into the iframe editor.
38711      */
38712     pushValue : function(){
38713         if(this.initialized){
38714             var v = this.el.dom.value;
38715             if(v.length < 1){
38716                 v = '&#160;';
38717             }
38718             
38719             if(this.fireEvent('beforepush', this, v) !== false){
38720                 var d = (this.doc.body || this.doc.documentElement);
38721                 d.innerHTML = v;
38722                 this.cleanUpPaste();
38723                 this.el.dom.value = d.innerHTML;
38724                 this.fireEvent('push', this, v);
38725             }
38726         }
38727     },
38728
38729     // private
38730     deferFocus : function(){
38731         this.focus.defer(10, this);
38732     },
38733
38734     // doc'ed in Field
38735     focus : function(){
38736         if(this.win && !this.sourceEditMode){
38737             this.win.focus();
38738         }else{
38739             this.el.focus();
38740         }
38741     },
38742     
38743     assignDocWin: function()
38744     {
38745         var iframe = this.iframe;
38746         
38747          if(Roo.isIE){
38748             this.doc = iframe.contentWindow.document;
38749             this.win = iframe.contentWindow;
38750         } else {
38751             if (!Roo.get(this.frameId)) {
38752                 return;
38753             }
38754             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38755             this.win = Roo.get(this.frameId).dom.contentWindow;
38756         }
38757     },
38758     
38759     // private
38760     initEditor : function(){
38761         //console.log("INIT EDITOR");
38762         this.assignDocWin();
38763         
38764         
38765         
38766         this.doc.designMode="on";
38767         this.doc.open();
38768         this.doc.write(this.getDocMarkup());
38769         this.doc.close();
38770         
38771         var dbody = (this.doc.body || this.doc.documentElement);
38772         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38773         // this copies styles from the containing element into thsi one..
38774         // not sure why we need all of this..
38775         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38776         ss['background-attachment'] = 'fixed'; // w3c
38777         dbody.bgProperties = 'fixed'; // ie
38778         Roo.DomHelper.applyStyles(dbody, ss);
38779         Roo.EventManager.on(this.doc, {
38780             'mousedown': this.onEditorEvent,
38781             'dblclick': this.onEditorEvent,
38782             'click': this.onEditorEvent,
38783             'keyup': this.onEditorEvent,
38784             buffer:100,
38785             scope: this
38786         });
38787         if(Roo.isGecko){
38788             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38789         }
38790         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38791             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38792         }
38793         this.initialized = true;
38794
38795         this.fireEvent('initialize', this);
38796         this.pushValue();
38797     },
38798
38799     // private
38800     onDestroy : function(){
38801         
38802         
38803         
38804         if(this.rendered){
38805             
38806             for (var i =0; i < this.toolbars.length;i++) {
38807                 // fixme - ask toolbars for heights?
38808                 this.toolbars[i].onDestroy();
38809             }
38810             
38811             this.wrap.dom.innerHTML = '';
38812             this.wrap.remove();
38813         }
38814     },
38815
38816     // private
38817     onFirstFocus : function(){
38818         
38819         this.assignDocWin();
38820         
38821         
38822         this.activated = true;
38823         for (var i =0; i < this.toolbars.length;i++) {
38824             this.toolbars[i].onFirstFocus();
38825         }
38826        
38827         if(Roo.isGecko){ // prevent silly gecko errors
38828             this.win.focus();
38829             var s = this.win.getSelection();
38830             if(!s.focusNode || s.focusNode.nodeType != 3){
38831                 var r = s.getRangeAt(0);
38832                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38833                 r.collapse(true);
38834                 this.deferFocus();
38835             }
38836             try{
38837                 this.execCmd('useCSS', true);
38838                 this.execCmd('styleWithCSS', false);
38839             }catch(e){}
38840         }
38841         this.fireEvent('activate', this);
38842     },
38843
38844     // private
38845     adjustFont: function(btn){
38846         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38847         //if(Roo.isSafari){ // safari
38848         //    adjust *= 2;
38849        // }
38850         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38851         if(Roo.isSafari){ // safari
38852             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38853             v =  (v < 10) ? 10 : v;
38854             v =  (v > 48) ? 48 : v;
38855             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38856             
38857         }
38858         
38859         
38860         v = Math.max(1, v+adjust);
38861         
38862         this.execCmd('FontSize', v  );
38863     },
38864
38865     onEditorEvent : function(e){
38866         this.fireEvent('editorevent', this, e);
38867       //  this.updateToolbar();
38868         this.syncValue();
38869     },
38870
38871     insertTag : function(tg)
38872     {
38873         // could be a bit smarter... -> wrap the current selected tRoo..
38874         
38875         this.execCmd("formatblock",   tg);
38876         
38877     },
38878     
38879     insertText : function(txt)
38880     {
38881         
38882         
38883         range = this.createRange();
38884         range.deleteContents();
38885                //alert(Sender.getAttribute('label'));
38886                
38887         range.insertNode(this.doc.createTextNode(txt));
38888     } ,
38889     
38890     // private
38891     relayBtnCmd : function(btn){
38892         this.relayCmd(btn.cmd);
38893     },
38894
38895     /**
38896      * Executes a Midas editor command on the editor document and performs necessary focus and
38897      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38898      * @param {String} cmd The Midas command
38899      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38900      */
38901     relayCmd : function(cmd, value){
38902         this.win.focus();
38903         this.execCmd(cmd, value);
38904         this.fireEvent('editorevent', this);
38905         //this.updateToolbar();
38906         this.deferFocus();
38907     },
38908
38909     /**
38910      * Executes a Midas editor command directly on the editor document.
38911      * For visual commands, you should use {@link #relayCmd} instead.
38912      * <b>This should only be called after the editor is initialized.</b>
38913      * @param {String} cmd The Midas command
38914      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38915      */
38916     execCmd : function(cmd, value){
38917         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38918         this.syncValue();
38919     },
38920
38921    
38922     /**
38923      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38924      * to insert tRoo.
38925      * @param {String} text
38926      */
38927     insertAtCursor : function(text){
38928         if(!this.activated){
38929             return;
38930         }
38931         if(Roo.isIE){
38932             this.win.focus();
38933             var r = this.doc.selection.createRange();
38934             if(r){
38935                 r.collapse(true);
38936                 r.pasteHTML(text);
38937                 this.syncValue();
38938                 this.deferFocus();
38939             }
38940         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38941             this.win.focus();
38942             this.execCmd('InsertHTML', text);
38943             this.deferFocus();
38944         }
38945     },
38946  // private
38947     mozKeyPress : function(e){
38948         if(e.ctrlKey){
38949             var c = e.getCharCode(), cmd;
38950           
38951             if(c > 0){
38952                 c = String.fromCharCode(c).toLowerCase();
38953                 switch(c){
38954                     case 'b':
38955                         cmd = 'bold';
38956                     break;
38957                     case 'i':
38958                         cmd = 'italic';
38959                     break;
38960                     case 'u':
38961                         cmd = 'underline';
38962                     case 'v':
38963                         this.cleanUpPaste.defer(100, this);
38964                         return;
38965                     break;
38966                 }
38967                 if(cmd){
38968                     this.win.focus();
38969                     this.execCmd(cmd);
38970                     this.deferFocus();
38971                     e.preventDefault();
38972                 }
38973                 
38974             }
38975         }
38976     },
38977
38978     // private
38979     fixKeys : function(){ // load time branching for fastest keydown performance
38980         if(Roo.isIE){
38981             return function(e){
38982                 var k = e.getKey(), r;
38983                 if(k == e.TAB){
38984                     e.stopEvent();
38985                     r = this.doc.selection.createRange();
38986                     if(r){
38987                         r.collapse(true);
38988                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38989                         this.deferFocus();
38990                     }
38991                     return;
38992                 }
38993                 
38994                 if(k == e.ENTER){
38995                     r = this.doc.selection.createRange();
38996                     if(r){
38997                         var target = r.parentElement();
38998                         if(!target || target.tagName.toLowerCase() != 'li'){
38999                             e.stopEvent();
39000                             r.pasteHTML('<br />');
39001                             r.collapse(false);
39002                             r.select();
39003                         }
39004                     }
39005                 }
39006                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39007                     this.cleanUpPaste.defer(100, this);
39008                     return;
39009                 }
39010                 
39011                 
39012             };
39013         }else if(Roo.isOpera){
39014             return function(e){
39015                 var k = e.getKey();
39016                 if(k == e.TAB){
39017                     e.stopEvent();
39018                     this.win.focus();
39019                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39020                     this.deferFocus();
39021                 }
39022                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39023                     this.cleanUpPaste.defer(100, this);
39024                     return;
39025                 }
39026                 
39027             };
39028         }else if(Roo.isSafari){
39029             return function(e){
39030                 var k = e.getKey();
39031                 
39032                 if(k == e.TAB){
39033                     e.stopEvent();
39034                     this.execCmd('InsertText','\t');
39035                     this.deferFocus();
39036                     return;
39037                 }
39038                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39039                     this.cleanUpPaste.defer(100, this);
39040                     return;
39041                 }
39042                 
39043              };
39044         }
39045     }(),
39046     
39047     getAllAncestors: function()
39048     {
39049         var p = this.getSelectedNode();
39050         var a = [];
39051         if (!p) {
39052             a.push(p); // push blank onto stack..
39053             p = this.getParentElement();
39054         }
39055         
39056         
39057         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39058             a.push(p);
39059             p = p.parentNode;
39060         }
39061         a.push(this.doc.body);
39062         return a;
39063     },
39064     lastSel : false,
39065     lastSelNode : false,
39066     
39067     
39068     getSelection : function() 
39069     {
39070         this.assignDocWin();
39071         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39072     },
39073     
39074     getSelectedNode: function() 
39075     {
39076         // this may only work on Gecko!!!
39077         
39078         // should we cache this!!!!
39079         
39080         
39081         
39082          
39083         var range = this.createRange(this.getSelection());
39084         
39085         if (Roo.isIE) {
39086             var parent = range.parentElement();
39087             while (true) {
39088                 var testRange = range.duplicate();
39089                 testRange.moveToElementText(parent);
39090                 if (testRange.inRange(range)) {
39091                     break;
39092                 }
39093                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39094                     break;
39095                 }
39096                 parent = parent.parentElement;
39097             }
39098             return parent;
39099         }
39100         
39101         
39102         var ar = range.endContainer.childNodes;
39103         if (!ar.length) {
39104             ar = range.commonAncestorContainer.childNodes;
39105             //alert(ar.length);
39106         }
39107         var nodes = [];
39108         var other_nodes = [];
39109         var has_other_nodes = false;
39110         for (var i=0;i<ar.length;i++) {
39111             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39112                 continue;
39113             }
39114             // fullly contained node.
39115             
39116             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39117                 nodes.push(ar[i]);
39118                 continue;
39119             }
39120             
39121             // probably selected..
39122             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39123                 other_nodes.push(ar[i]);
39124                 continue;
39125             }
39126             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39127                 continue;
39128             }
39129             
39130             
39131             has_other_nodes = true;
39132         }
39133         if (!nodes.length && other_nodes.length) {
39134             nodes= other_nodes;
39135         }
39136         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39137             return false;
39138         }
39139         
39140         return nodes[0];
39141     },
39142     createRange: function(sel)
39143     {
39144         // this has strange effects when using with 
39145         // top toolbar - not sure if it's a great idea.
39146         //this.editor.contentWindow.focus();
39147         if (typeof sel != "undefined") {
39148             try {
39149                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39150             } catch(e) {
39151                 return this.doc.createRange();
39152             }
39153         } else {
39154             return this.doc.createRange();
39155         }
39156     },
39157     getParentElement: function()
39158     {
39159         
39160         this.assignDocWin();
39161         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39162         
39163         var range = this.createRange(sel);
39164          
39165         try {
39166             var p = range.commonAncestorContainer;
39167             while (p.nodeType == 3) { // text node
39168                 p = p.parentNode;
39169             }
39170             return p;
39171         } catch (e) {
39172             return null;
39173         }
39174     
39175     },
39176     
39177     
39178     
39179     // BC Hacks - cause I cant work out what i was trying to do..
39180     rangeIntersectsNode : function(range, node)
39181     {
39182         var nodeRange = node.ownerDocument.createRange();
39183         try {
39184             nodeRange.selectNode(node);
39185         }
39186         catch (e) {
39187             nodeRange.selectNodeContents(node);
39188         }
39189
39190         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39191                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39192     },
39193     rangeCompareNode : function(range, node) {
39194         var nodeRange = node.ownerDocument.createRange();
39195         try {
39196             nodeRange.selectNode(node);
39197         } catch (e) {
39198             nodeRange.selectNodeContents(node);
39199         }
39200         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39201         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39202
39203         if (nodeIsBefore && !nodeIsAfter)
39204             return 0;
39205         if (!nodeIsBefore && nodeIsAfter)
39206             return 1;
39207         if (nodeIsBefore && nodeIsAfter)
39208             return 2;
39209
39210         return 3;
39211     },
39212
39213     // private? - in a new class?
39214     cleanUpPaste :  function()
39215     {
39216         // cleans up the whole document..
39217       //  console.log('cleanuppaste');
39218         this.cleanUpChildren(this.doc.body);
39219         
39220         
39221     },
39222     cleanUpChildren : function (n)
39223     {
39224         if (!n.childNodes.length) {
39225             return;
39226         }
39227         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39228            this.cleanUpChild(n.childNodes[i]);
39229         }
39230     },
39231     
39232     
39233         
39234     
39235     cleanUpChild : function (node)
39236     {
39237         //console.log(node);
39238         if (node.nodeName == "#text") {
39239             // clean up silly Windows -- stuff?
39240             return; 
39241         }
39242         if (node.nodeName == "#comment") {
39243             node.parentNode.removeChild(node);
39244             // clean up silly Windows -- stuff?
39245             return; 
39246         }
39247         
39248         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39249             // remove node.
39250             node.parentNode.removeChild(node);
39251             return;
39252             
39253         }
39254         if (!node.attributes || !node.attributes.length) {
39255             this.cleanUpChildren(node);
39256             return;
39257         }
39258         
39259         function cleanAttr(n,v)
39260         {
39261             
39262             if (v.match(/^\./) || v.match(/^\//)) {
39263                 return;
39264             }
39265             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39266                 return;
39267             }
39268             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39269             node.removeAttribute(n);
39270             
39271         }
39272         
39273         function cleanStyle(n,v)
39274         {
39275             if (v.match(/expression/)) { //XSS?? should we even bother..
39276                 node.removeAttribute(n);
39277                 return;
39278             }
39279             
39280             
39281             var parts = v.split(/;/);
39282             Roo.each(parts, function(p) {
39283                 p = p.replace(/\s+/g,'');
39284                 if (!p.length) {
39285                     return;
39286                 }
39287                 var l = p.split(':').shift().replace(/\s+/g,'');
39288                 
39289                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39290                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39291                     node.removeAttribute(n);
39292                     return false;
39293                 }
39294             });
39295             
39296             
39297         }
39298         
39299         
39300         for (var i = node.attributes.length-1; i > -1 ; i--) {
39301             var a = node.attributes[i];
39302             //console.log(a);
39303             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39304                 node.removeAttribute(a.name);
39305                 return;
39306             }
39307             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39308                 cleanAttr(a.name,a.value); // fixme..
39309                 return;
39310             }
39311             if (a.name == 'style') {
39312                 cleanStyle(a.name,a.value);
39313             }
39314             /// clean up MS crap..
39315             if (a.name == 'class') {
39316                 if (a.value.match(/^Mso/)) {
39317                     node.className = '';
39318                 }
39319             }
39320             
39321             // style cleanup!?
39322             // class cleanup?
39323             
39324         }
39325         
39326         
39327         this.cleanUpChildren(node);
39328         
39329         
39330     }
39331     
39332     
39333     // hide stuff that is not compatible
39334     /**
39335      * @event blur
39336      * @hide
39337      */
39338     /**
39339      * @event change
39340      * @hide
39341      */
39342     /**
39343      * @event focus
39344      * @hide
39345      */
39346     /**
39347      * @event specialkey
39348      * @hide
39349      */
39350     /**
39351      * @cfg {String} fieldClass @hide
39352      */
39353     /**
39354      * @cfg {String} focusClass @hide
39355      */
39356     /**
39357      * @cfg {String} autoCreate @hide
39358      */
39359     /**
39360      * @cfg {String} inputType @hide
39361      */
39362     /**
39363      * @cfg {String} invalidClass @hide
39364      */
39365     /**
39366      * @cfg {String} invalidText @hide
39367      */
39368     /**
39369      * @cfg {String} msgFx @hide
39370      */
39371     /**
39372      * @cfg {String} validateOnBlur @hide
39373      */
39374 });
39375
39376 Roo.form.HtmlEditor.white = [
39377         'area', 'br', 'img', 'input', 'hr', 'wbr',
39378         
39379        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39380        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39381        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39382        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39383        'table',   'ul',         'xmp', 
39384        
39385        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39386       'thead',   'tr', 
39387      
39388       'dir', 'menu', 'ol', 'ul', 'dl',
39389        
39390       'embed',  'object'
39391 ];
39392
39393
39394 Roo.form.HtmlEditor.black = [
39395     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39396         'applet', // 
39397         'base',   'basefont', 'bgsound', 'blink',  'body', 
39398         'frame',  'frameset', 'head',    'html',   'ilayer', 
39399         'iframe', 'layer',  'link',     'meta',    'object',   
39400         'script', 'style' ,'title',  'xml' // clean later..
39401 ];
39402 Roo.form.HtmlEditor.clean = [
39403     'script', 'style', 'title', 'xml'
39404 ];
39405
39406 // attributes..
39407
39408 Roo.form.HtmlEditor.ablack = [
39409     'on'
39410 ];
39411     
39412 Roo.form.HtmlEditor.aclean = [ 
39413     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39414 ];
39415
39416 // protocols..
39417 Roo.form.HtmlEditor.pwhite= [
39418         'http',  'https',  'mailto'
39419 ];
39420
39421 Roo.form.HtmlEditor.cwhite= [
39422         'text-align',
39423         'font-size'
39424 ];
39425
39426 // <script type="text/javascript">
39427 /*
39428  * Based on
39429  * Ext JS Library 1.1.1
39430  * Copyright(c) 2006-2007, Ext JS, LLC.
39431  *  
39432  
39433  */
39434
39435 /**
39436  * @class Roo.form.HtmlEditorToolbar1
39437  * Basic Toolbar
39438  * 
39439  * Usage:
39440  *
39441  new Roo.form.HtmlEditor({
39442     ....
39443     toolbars : [
39444         new Roo.form.HtmlEditorToolbar1({
39445             disable : { fonts: 1 , format: 1, ..., ... , ...],
39446             btns : [ .... ]
39447         })
39448     }
39449      
39450  * 
39451  * @cfg {Object} disable List of elements to disable..
39452  * @cfg {Array} btns List of additional buttons.
39453  * 
39454  * 
39455  * NEEDS Extra CSS? 
39456  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39457  */
39458  
39459 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39460 {
39461     
39462     Roo.apply(this, config);
39463     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39464     // dont call parent... till later.
39465 }
39466
39467 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39468     
39469     tb: false,
39470     
39471     rendered: false,
39472     
39473     editor : false,
39474     /**
39475      * @cfg {Object} disable  List of toolbar elements to disable
39476          
39477      */
39478     disable : false,
39479       /**
39480      * @cfg {Array} fontFamilies An array of available font families
39481      */
39482     fontFamilies : [
39483         'Arial',
39484         'Courier New',
39485         'Tahoma',
39486         'Times New Roman',
39487         'Verdana'
39488     ],
39489     
39490     specialChars : [
39491            "&#169;",
39492           "&#174;",     
39493           "&#8482;",    
39494           "&#163;" ,    
39495          // "&#8212;",    
39496           "&#8230;",    
39497           "&#247;" ,    
39498         //  "&#225;" ,     ?? a acute?
39499            "&#8364;"    , //Euro
39500        //   "&#8220;"    ,
39501         //  "&#8221;"    ,
39502         //  "&#8226;"    ,
39503           "&#176;"  //   , // degrees
39504
39505          // "&#233;"     , // e ecute
39506          // "&#250;"     , // u ecute?
39507     ],
39508     inputElements : [ 
39509             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39510             "input:submit", "input:button", "select", "textarea", "label" ],
39511     formats : [
39512         ["p"] ,  
39513         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39514         ["pre"],[ "code"], 
39515         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39516     ],
39517      /**
39518      * @cfg {String} defaultFont default font to use.
39519      */
39520     defaultFont: 'tahoma',
39521    
39522     fontSelect : false,
39523     
39524     
39525     formatCombo : false,
39526     
39527     init : function(editor)
39528     {
39529         this.editor = editor;
39530         
39531         
39532         var fid = editor.frameId;
39533         var etb = this;
39534         function btn(id, toggle, handler){
39535             var xid = fid + '-'+ id ;
39536             return {
39537                 id : xid,
39538                 cmd : id,
39539                 cls : 'x-btn-icon x-edit-'+id,
39540                 enableToggle:toggle !== false,
39541                 scope: editor, // was editor...
39542                 handler:handler||editor.relayBtnCmd,
39543                 clickEvent:'mousedown',
39544                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39545                 tabIndex:-1
39546             };
39547         }
39548         
39549         
39550         
39551         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39552         this.tb = tb;
39553          // stop form submits
39554         tb.el.on('click', function(e){
39555             e.preventDefault(); // what does this do?
39556         });
39557
39558         if(!this.disable.font && !Roo.isSafari){
39559             /* why no safari for fonts
39560             editor.fontSelect = tb.el.createChild({
39561                 tag:'select',
39562                 tabIndex: -1,
39563                 cls:'x-font-select',
39564                 html: editor.createFontOptions()
39565             });
39566             editor.fontSelect.on('change', function(){
39567                 var font = editor.fontSelect.dom.value;
39568                 editor.relayCmd('fontname', font);
39569                 editor.deferFocus();
39570             }, editor);
39571             tb.add(
39572                 editor.fontSelect.dom,
39573                 '-'
39574             );
39575             */
39576         };
39577         if(!this.disable.formats){
39578             this.formatCombo = new Roo.form.ComboBox({
39579                 store: new Roo.data.SimpleStore({
39580                     id : 'tag',
39581                     fields: ['tag'],
39582                     data : this.formats // from states.js
39583                 }),
39584                 blockFocus : true,
39585                 //autoCreate : {tag: "div",  size: "20"},
39586                 displayField:'tag',
39587                 typeAhead: false,
39588                 mode: 'local',
39589                 editable : false,
39590                 triggerAction: 'all',
39591                 emptyText:'Add tag',
39592                 selectOnFocus:true,
39593                 width:135,
39594                 listeners : {
39595                     'select': function(c, r, i) {
39596                         editor.insertTag(r.get('tag'));
39597                         editor.focus();
39598                     }
39599                 }
39600
39601             });
39602             tb.addField(this.formatCombo);
39603             
39604         }
39605         
39606         if(!this.disable.format){
39607             tb.add(
39608                 btn('bold'),
39609                 btn('italic'),
39610                 btn('underline')
39611             );
39612         };
39613         if(!this.disable.fontSize){
39614             tb.add(
39615                 '-',
39616                 
39617                 
39618                 btn('increasefontsize', false, editor.adjustFont),
39619                 btn('decreasefontsize', false, editor.adjustFont)
39620             );
39621         };
39622         
39623         
39624         if(this.disable.colors){
39625             tb.add(
39626                 '-', {
39627                     id:editor.frameId +'-forecolor',
39628                     cls:'x-btn-icon x-edit-forecolor',
39629                     clickEvent:'mousedown',
39630                     tooltip: this.buttonTips['forecolor'] || undefined,
39631                     tabIndex:-1,
39632                     menu : new Roo.menu.ColorMenu({
39633                         allowReselect: true,
39634                         focus: Roo.emptyFn,
39635                         value:'000000',
39636                         plain:true,
39637                         selectHandler: function(cp, color){
39638                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39639                             editor.deferFocus();
39640                         },
39641                         scope: editor,
39642                         clickEvent:'mousedown'
39643                     })
39644                 }, {
39645                     id:editor.frameId +'backcolor',
39646                     cls:'x-btn-icon x-edit-backcolor',
39647                     clickEvent:'mousedown',
39648                     tooltip: this.buttonTips['backcolor'] || undefined,
39649                     tabIndex:-1,
39650                     menu : new Roo.menu.ColorMenu({
39651                         focus: Roo.emptyFn,
39652                         value:'FFFFFF',
39653                         plain:true,
39654                         allowReselect: true,
39655                         selectHandler: function(cp, color){
39656                             if(Roo.isGecko){
39657                                 editor.execCmd('useCSS', false);
39658                                 editor.execCmd('hilitecolor', color);
39659                                 editor.execCmd('useCSS', true);
39660                                 editor.deferFocus();
39661                             }else{
39662                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39663                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39664                                 editor.deferFocus();
39665                             }
39666                         },
39667                         scope:editor,
39668                         clickEvent:'mousedown'
39669                     })
39670                 }
39671             );
39672         };
39673         // now add all the items...
39674         
39675
39676         if(!this.disable.alignments){
39677             tb.add(
39678                 '-',
39679                 btn('justifyleft'),
39680                 btn('justifycenter'),
39681                 btn('justifyright')
39682             );
39683         };
39684
39685         //if(!Roo.isSafari){
39686             if(!this.disable.links){
39687                 tb.add(
39688                     '-',
39689                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39690                 );
39691             };
39692
39693             if(!this.disable.lists){
39694                 tb.add(
39695                     '-',
39696                     btn('insertorderedlist'),
39697                     btn('insertunorderedlist')
39698                 );
39699             }
39700             if(!this.disable.sourceEdit){
39701                 tb.add(
39702                     '-',
39703                     btn('sourceedit', true, function(btn){
39704                         this.toggleSourceEdit(btn.pressed);
39705                     })
39706                 );
39707             }
39708         //}
39709         
39710         var smenu = { };
39711         // special menu.. - needs to be tidied up..
39712         if (!this.disable.special) {
39713             smenu = {
39714                 text: "&#169;",
39715                 cls: 'x-edit-none',
39716                 menu : {
39717                     items : []
39718                    }
39719             };
39720             for (var i =0; i < this.specialChars.length; i++) {
39721                 smenu.menu.items.push({
39722                     
39723                     html: this.specialChars[i],
39724                     handler: function(a,b) {
39725                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39726                         
39727                     },
39728                     tabIndex:-1
39729                 });
39730             }
39731             
39732             
39733             tb.add(smenu);
39734             
39735             
39736         }
39737         if (this.btns) {
39738             for(var i =0; i< this.btns.length;i++) {
39739                 var b = this.btns[i];
39740                 b.cls =  'x-edit-none';
39741                 b.scope = editor;
39742                 tb.add(b);
39743             }
39744         
39745         }
39746         
39747         
39748         
39749         // disable everything...
39750         
39751         this.tb.items.each(function(item){
39752            if(item.id != editor.frameId+ '-sourceedit'){
39753                 item.disable();
39754             }
39755         });
39756         this.rendered = true;
39757         
39758         // the all the btns;
39759         editor.on('editorevent', this.updateToolbar, this);
39760         // other toolbars need to implement this..
39761         //editor.on('editmodechange', this.updateToolbar, this);
39762     },
39763     
39764     
39765     
39766     /**
39767      * Protected method that will not generally be called directly. It triggers
39768      * a toolbar update by reading the markup state of the current selection in the editor.
39769      */
39770     updateToolbar: function(){
39771
39772         if(!this.editor.activated){
39773             this.editor.onFirstFocus();
39774             return;
39775         }
39776
39777         var btns = this.tb.items.map, 
39778             doc = this.editor.doc,
39779             frameId = this.editor.frameId;
39780
39781         if(!this.disable.font && !Roo.isSafari){
39782             /*
39783             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39784             if(name != this.fontSelect.dom.value){
39785                 this.fontSelect.dom.value = name;
39786             }
39787             */
39788         }
39789         if(!this.disable.format){
39790             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39791             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39792             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39793         }
39794         if(!this.disable.alignments){
39795             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39796             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39797             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39798         }
39799         if(!Roo.isSafari && !this.disable.lists){
39800             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39801             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39802         }
39803         
39804         var ans = this.editor.getAllAncestors();
39805         if (this.formatCombo) {
39806             
39807             
39808             var store = this.formatCombo.store;
39809             this.formatCombo.setValue("");
39810             for (var i =0; i < ans.length;i++) {
39811                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39812                     // select it..
39813                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39814                     break;
39815                 }
39816             }
39817         }
39818         
39819         
39820         
39821         // hides menus... - so this cant be on a menu...
39822         Roo.menu.MenuMgr.hideAll();
39823
39824         //this.editorsyncValue();
39825     },
39826    
39827     
39828     createFontOptions : function(){
39829         var buf = [], fs = this.fontFamilies, ff, lc;
39830         for(var i = 0, len = fs.length; i< len; i++){
39831             ff = fs[i];
39832             lc = ff.toLowerCase();
39833             buf.push(
39834                 '<option value="',lc,'" style="font-family:',ff,';"',
39835                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39836                     ff,
39837                 '</option>'
39838             );
39839         }
39840         return buf.join('');
39841     },
39842     
39843     toggleSourceEdit : function(sourceEditMode){
39844         if(sourceEditMode === undefined){
39845             sourceEditMode = !this.sourceEditMode;
39846         }
39847         this.sourceEditMode = sourceEditMode === true;
39848         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39849         // just toggle the button?
39850         if(btn.pressed !== this.editor.sourceEditMode){
39851             btn.toggle(this.editor.sourceEditMode);
39852             return;
39853         }
39854         
39855         if(this.sourceEditMode){
39856             this.tb.items.each(function(item){
39857                 if(item.cmd != 'sourceedit'){
39858                     item.disable();
39859                 }
39860             });
39861           
39862         }else{
39863             if(this.initialized){
39864                 this.tb.items.each(function(item){
39865                     item.enable();
39866                 });
39867             }
39868             
39869         }
39870         // tell the editor that it's been pressed..
39871         this.editor.toggleSourceEdit(sourceEditMode);
39872        
39873     },
39874      /**
39875      * Object collection of toolbar tooltips for the buttons in the editor. The key
39876      * is the command id associated with that button and the value is a valid QuickTips object.
39877      * For example:
39878 <pre><code>
39879 {
39880     bold : {
39881         title: 'Bold (Ctrl+B)',
39882         text: 'Make the selected text bold.',
39883         cls: 'x-html-editor-tip'
39884     },
39885     italic : {
39886         title: 'Italic (Ctrl+I)',
39887         text: 'Make the selected text italic.',
39888         cls: 'x-html-editor-tip'
39889     },
39890     ...
39891 </code></pre>
39892     * @type Object
39893      */
39894     buttonTips : {
39895         bold : {
39896             title: 'Bold (Ctrl+B)',
39897             text: 'Make the selected text bold.',
39898             cls: 'x-html-editor-tip'
39899         },
39900         italic : {
39901             title: 'Italic (Ctrl+I)',
39902             text: 'Make the selected text italic.',
39903             cls: 'x-html-editor-tip'
39904         },
39905         underline : {
39906             title: 'Underline (Ctrl+U)',
39907             text: 'Underline the selected text.',
39908             cls: 'x-html-editor-tip'
39909         },
39910         increasefontsize : {
39911             title: 'Grow Text',
39912             text: 'Increase the font size.',
39913             cls: 'x-html-editor-tip'
39914         },
39915         decreasefontsize : {
39916             title: 'Shrink Text',
39917             text: 'Decrease the font size.',
39918             cls: 'x-html-editor-tip'
39919         },
39920         backcolor : {
39921             title: 'Text Highlight Color',
39922             text: 'Change the background color of the selected text.',
39923             cls: 'x-html-editor-tip'
39924         },
39925         forecolor : {
39926             title: 'Font Color',
39927             text: 'Change the color of the selected text.',
39928             cls: 'x-html-editor-tip'
39929         },
39930         justifyleft : {
39931             title: 'Align Text Left',
39932             text: 'Align text to the left.',
39933             cls: 'x-html-editor-tip'
39934         },
39935         justifycenter : {
39936             title: 'Center Text',
39937             text: 'Center text in the editor.',
39938             cls: 'x-html-editor-tip'
39939         },
39940         justifyright : {
39941             title: 'Align Text Right',
39942             text: 'Align text to the right.',
39943             cls: 'x-html-editor-tip'
39944         },
39945         insertunorderedlist : {
39946             title: 'Bullet List',
39947             text: 'Start a bulleted list.',
39948             cls: 'x-html-editor-tip'
39949         },
39950         insertorderedlist : {
39951             title: 'Numbered List',
39952             text: 'Start a numbered list.',
39953             cls: 'x-html-editor-tip'
39954         },
39955         createlink : {
39956             title: 'Hyperlink',
39957             text: 'Make the selected text a hyperlink.',
39958             cls: 'x-html-editor-tip'
39959         },
39960         sourceedit : {
39961             title: 'Source Edit',
39962             text: 'Switch to source editing mode.',
39963             cls: 'x-html-editor-tip'
39964         }
39965     },
39966     // private
39967     onDestroy : function(){
39968         if(this.rendered){
39969             
39970             this.tb.items.each(function(item){
39971                 if(item.menu){
39972                     item.menu.removeAll();
39973                     if(item.menu.el){
39974                         item.menu.el.destroy();
39975                     }
39976                 }
39977                 item.destroy();
39978             });
39979              
39980         }
39981     },
39982     onFirstFocus: function() {
39983         this.tb.items.each(function(item){
39984            item.enable();
39985         });
39986     }
39987 });
39988
39989
39990
39991
39992 // <script type="text/javascript">
39993 /*
39994  * Based on
39995  * Ext JS Library 1.1.1
39996  * Copyright(c) 2006-2007, Ext JS, LLC.
39997  *  
39998  
39999  */
40000
40001  
40002 /**
40003  * @class Roo.form.HtmlEditor.ToolbarContext
40004  * Context Toolbar
40005  * 
40006  * Usage:
40007  *
40008  new Roo.form.HtmlEditor({
40009     ....
40010     toolbars : [
40011         new Roo.form.HtmlEditor.ToolbarStandard(),
40012         new Roo.form.HtmlEditor.ToolbarContext()
40013         })
40014     }
40015      
40016  * 
40017  * @config : {Object} disable List of elements to disable.. (not done yet.)
40018  * 
40019  * 
40020  */
40021
40022 Roo.form.HtmlEditor.ToolbarContext = function(config)
40023 {
40024     
40025     Roo.apply(this, config);
40026     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40027     // dont call parent... till later.
40028 }
40029 Roo.form.HtmlEditor.ToolbarContext.types = {
40030     'IMG' : {
40031         width : {
40032             title: "Width",
40033             width: 40
40034         },
40035         height:  {
40036             title: "Height",
40037             width: 40
40038         },
40039         align: {
40040             title: "Align",
40041             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40042             width : 80
40043             
40044         },
40045         border: {
40046             title: "Border",
40047             width: 40
40048         },
40049         alt: {
40050             title: "Alt",
40051             width: 120
40052         },
40053         src : {
40054             title: "Src",
40055             width: 220
40056         }
40057         
40058     },
40059     'A' : {
40060         name : {
40061             title: "Name",
40062             width: 50
40063         },
40064         href:  {
40065             title: "Href",
40066             width: 220
40067         } // border?
40068         
40069     },
40070     'TABLE' : {
40071         rows : {
40072             title: "Rows",
40073             width: 20
40074         },
40075         cols : {
40076             title: "Cols",
40077             width: 20
40078         },
40079         width : {
40080             title: "Width",
40081             width: 40
40082         },
40083         height : {
40084             title: "Height",
40085             width: 40
40086         },
40087         border : {
40088             title: "Border",
40089             width: 20
40090         }
40091     },
40092     'TD' : {
40093         width : {
40094             title: "Width",
40095             width: 40
40096         },
40097         height : {
40098             title: "Height",
40099             width: 40
40100         },   
40101         align: {
40102             title: "Align",
40103             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40104             width: 40
40105         },
40106         valign: {
40107             title: "Valign",
40108             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40109             width: 40
40110         },
40111         colspan: {
40112             title: "Colspan",
40113             width: 20
40114             
40115         }
40116     },
40117     'INPUT' : {
40118         name : {
40119             title: "name",
40120             width: 120
40121         },
40122         value : {
40123             title: "Value",
40124             width: 120
40125         },
40126         width : {
40127             title: "Width",
40128             width: 40
40129         }
40130     },
40131     'LABEL' : {
40132         'for' : {
40133             title: "For",
40134             width: 120
40135         }
40136     },
40137     'TEXTAREA' : {
40138           name : {
40139             title: "name",
40140             width: 120
40141         },
40142         rows : {
40143             title: "Rows",
40144             width: 20
40145         },
40146         cols : {
40147             title: "Cols",
40148             width: 20
40149         }
40150     },
40151     'SELECT' : {
40152         name : {
40153             title: "name",
40154             width: 120
40155         },
40156         selectoptions : {
40157             title: "Options",
40158             width: 200
40159         }
40160     },
40161     'BODY' : {
40162         title : {
40163             title: "title",
40164             width: 120,
40165             disabled : true
40166         }
40167     }
40168 };
40169
40170
40171
40172 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40173     
40174     tb: false,
40175     
40176     rendered: false,
40177     
40178     editor : false,
40179     /**
40180      * @cfg {Object} disable  List of toolbar elements to disable
40181          
40182      */
40183     disable : false,
40184     
40185     
40186     
40187     toolbars : false,
40188     
40189     init : function(editor)
40190     {
40191         this.editor = editor;
40192         
40193         
40194         var fid = editor.frameId;
40195         var etb = this;
40196         function btn(id, toggle, handler){
40197             var xid = fid + '-'+ id ;
40198             return {
40199                 id : xid,
40200                 cmd : id,
40201                 cls : 'x-btn-icon x-edit-'+id,
40202                 enableToggle:toggle !== false,
40203                 scope: editor, // was editor...
40204                 handler:handler||editor.relayBtnCmd,
40205                 clickEvent:'mousedown',
40206                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40207                 tabIndex:-1
40208             };
40209         }
40210         // create a new element.
40211         var wdiv = editor.wrap.createChild({
40212                 tag: 'div'
40213             }, editor.wrap.dom.firstChild.nextSibling, true);
40214         
40215         // can we do this more than once??
40216         
40217          // stop form submits
40218       
40219  
40220         // disable everything...
40221         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40222         this.toolbars = {};
40223            
40224         for (var i in  ty) {
40225           
40226             this.toolbars[i] = this.buildToolbar(ty[i],i);
40227         }
40228         this.tb = this.toolbars.BODY;
40229         this.tb.el.show();
40230         
40231          
40232         this.rendered = true;
40233         
40234         // the all the btns;
40235         editor.on('editorevent', this.updateToolbar, this);
40236         // other toolbars need to implement this..
40237         //editor.on('editmodechange', this.updateToolbar, this);
40238     },
40239     
40240     
40241     
40242     /**
40243      * Protected method that will not generally be called directly. It triggers
40244      * a toolbar update by reading the markup state of the current selection in the editor.
40245      */
40246     updateToolbar: function(){
40247
40248         if(!this.editor.activated){
40249             this.editor.onFirstFocus();
40250             return;
40251         }
40252
40253         
40254         var ans = this.editor.getAllAncestors();
40255         
40256         // pick
40257         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40258         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40259         sel = sel ? sel : this.editor.doc.body;
40260         sel = sel.tagName.length ? sel : this.editor.doc.body;
40261         var tn = sel.tagName.toUpperCase();
40262         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40263         tn = sel.tagName.toUpperCase();
40264         if (this.tb.name  == tn) {
40265             return; // no change
40266         }
40267         this.tb.el.hide();
40268         ///console.log("show: " + tn);
40269         this.tb =  this.toolbars[tn];
40270         this.tb.el.show();
40271         this.tb.fields.each(function(e) {
40272             e.setValue(sel.getAttribute(e.name));
40273         });
40274         this.tb.selectedNode = sel;
40275         
40276         
40277         Roo.menu.MenuMgr.hideAll();
40278
40279         //this.editorsyncValue();
40280     },
40281    
40282        
40283     // private
40284     onDestroy : function(){
40285         if(this.rendered){
40286             
40287             this.tb.items.each(function(item){
40288                 if(item.menu){
40289                     item.menu.removeAll();
40290                     if(item.menu.el){
40291                         item.menu.el.destroy();
40292                     }
40293                 }
40294                 item.destroy();
40295             });
40296              
40297         }
40298     },
40299     onFirstFocus: function() {
40300         // need to do this for all the toolbars..
40301         this.tb.items.each(function(item){
40302            item.enable();
40303         });
40304     },
40305     buildToolbar: function(tlist, nm)
40306     {
40307         var editor = this.editor;
40308          // create a new element.
40309         var wdiv = editor.wrap.createChild({
40310                 tag: 'div'
40311             }, editor.wrap.dom.firstChild.nextSibling, true);
40312         
40313        
40314         var tb = new Roo.Toolbar(wdiv);
40315         tb.add(nm+ ":&nbsp;");
40316         for (var i in tlist) {
40317             var item = tlist[i];
40318             tb.add(item.title + ":&nbsp;");
40319             if (item.opts) {
40320                 // fixme
40321                 
40322               
40323                 tb.addField( new Roo.form.ComboBox({
40324                     store: new Roo.data.SimpleStore({
40325                         id : 'val',
40326                         fields: ['val'],
40327                         data : item.opts // from states.js
40328                     }),
40329                     name : i,
40330                     displayField:'val',
40331                     typeAhead: false,
40332                     mode: 'local',
40333                     editable : false,
40334                     triggerAction: 'all',
40335                     emptyText:'Select',
40336                     selectOnFocus:true,
40337                     width: item.width ? item.width  : 130,
40338                     listeners : {
40339                         'select': function(c, r, i) {
40340                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40341                         }
40342                     }
40343
40344                 }));
40345                 continue;
40346                     
40347                 
40348                 
40349                 
40350                 
40351                 tb.addField( new Roo.form.TextField({
40352                     name: i,
40353                     width: 100,
40354                     //allowBlank:false,
40355                     value: ''
40356                 }));
40357                 continue;
40358             }
40359             tb.addField( new Roo.form.TextField({
40360                 name: i,
40361                 width: item.width,
40362                 //allowBlank:true,
40363                 value: '',
40364                 listeners: {
40365                     'change' : function(f, nv, ov) {
40366                         tb.selectedNode.setAttribute(f.name, nv);
40367                     }
40368                 }
40369             }));
40370              
40371         }
40372         tb.el.on('click', function(e){
40373             e.preventDefault(); // what does this do?
40374         });
40375         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40376         tb.el.hide();
40377         tb.name = nm;
40378         // dont need to disable them... as they will get hidden
40379         return tb;
40380          
40381         
40382     }
40383     
40384     
40385     
40386     
40387 });
40388
40389
40390
40391
40392
40393 /*
40394  * Based on:
40395  * Ext JS Library 1.1.1
40396  * Copyright(c) 2006-2007, Ext JS, LLC.
40397  *
40398  * Originally Released Under LGPL - original licence link has changed is not relivant.
40399  *
40400  * Fork - LGPL
40401  * <script type="text/javascript">
40402  */
40403  
40404 /**
40405  * @class Roo.form.BasicForm
40406  * @extends Roo.util.Observable
40407  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40408  * @constructor
40409  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40410  * @param {Object} config Configuration options
40411  */
40412 Roo.form.BasicForm = function(el, config){
40413     this.allItems = [];
40414     this.childForms = [];
40415     Roo.apply(this, config);
40416     /*
40417      * The Roo.form.Field items in this form.
40418      * @type MixedCollection
40419      */
40420      
40421      
40422     this.items = new Roo.util.MixedCollection(false, function(o){
40423         return o.id || (o.id = Roo.id());
40424     });
40425     this.addEvents({
40426         /**
40427          * @event beforeaction
40428          * Fires before any action is performed. Return false to cancel the action.
40429          * @param {Form} this
40430          * @param {Action} action The action to be performed
40431          */
40432         beforeaction: true,
40433         /**
40434          * @event actionfailed
40435          * Fires when an action fails.
40436          * @param {Form} this
40437          * @param {Action} action The action that failed
40438          */
40439         actionfailed : true,
40440         /**
40441          * @event actioncomplete
40442          * Fires when an action is completed.
40443          * @param {Form} this
40444          * @param {Action} action The action that completed
40445          */
40446         actioncomplete : true
40447     });
40448     if(el){
40449         this.initEl(el);
40450     }
40451     Roo.form.BasicForm.superclass.constructor.call(this);
40452 };
40453
40454 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40455     /**
40456      * @cfg {String} method
40457      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40458      */
40459     /**
40460      * @cfg {DataReader} reader
40461      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40462      * This is optional as there is built-in support for processing JSON.
40463      */
40464     /**
40465      * @cfg {DataReader} errorReader
40466      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40467      * This is completely optional as there is built-in support for processing JSON.
40468      */
40469     /**
40470      * @cfg {String} url
40471      * The URL to use for form actions if one isn't supplied in the action options.
40472      */
40473     /**
40474      * @cfg {Boolean} fileUpload
40475      * Set to true if this form is a file upload.
40476      */
40477      
40478     /**
40479      * @cfg {Object} baseParams
40480      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40481      */
40482      /**
40483      
40484     /**
40485      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40486      */
40487     timeout: 30,
40488
40489     // private
40490     activeAction : null,
40491
40492     /**
40493      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40494      * or setValues() data instead of when the form was first created.
40495      */
40496     trackResetOnLoad : false,
40497     
40498     
40499     /**
40500      * childForms - used for multi-tab forms
40501      * @type {Array}
40502      */
40503     childForms : false,
40504     
40505     /**
40506      * allItems - full list of fields.
40507      * @type {Array}
40508      */
40509     allItems : false,
40510     
40511     /**
40512      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40513      * element by passing it or its id or mask the form itself by passing in true.
40514      * @type Mixed
40515      */
40516     waitMsgTarget : false,
40517
40518     // private
40519     initEl : function(el){
40520         this.el = Roo.get(el);
40521         this.id = this.el.id || Roo.id();
40522         this.el.on('submit', this.onSubmit, this);
40523         this.el.addClass('x-form');
40524     },
40525
40526     // private
40527     onSubmit : function(e){
40528         e.stopEvent();
40529     },
40530
40531     /**
40532      * Returns true if client-side validation on the form is successful.
40533      * @return Boolean
40534      */
40535     isValid : function(){
40536         var valid = true;
40537         this.items.each(function(f){
40538            if(!f.validate()){
40539                valid = false;
40540            }
40541         });
40542         return valid;
40543     },
40544
40545     /**
40546      * Returns true if any fields in this form have changed since their original load.
40547      * @return Boolean
40548      */
40549     isDirty : function(){
40550         var dirty = false;
40551         this.items.each(function(f){
40552            if(f.isDirty()){
40553                dirty = true;
40554                return false;
40555            }
40556         });
40557         return dirty;
40558     },
40559
40560     /**
40561      * Performs a predefined action (submit or load) or custom actions you define on this form.
40562      * @param {String} actionName The name of the action type
40563      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40564      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40565      * accept other config options):
40566      * <pre>
40567 Property          Type             Description
40568 ----------------  ---------------  ----------------------------------------------------------------------------------
40569 url               String           The url for the action (defaults to the form's url)
40570 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40571 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40572 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40573                                    validate the form on the client (defaults to false)
40574      * </pre>
40575      * @return {BasicForm} this
40576      */
40577     doAction : function(action, options){
40578         if(typeof action == 'string'){
40579             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40580         }
40581         if(this.fireEvent('beforeaction', this, action) !== false){
40582             this.beforeAction(action);
40583             action.run.defer(100, action);
40584         }
40585         return this;
40586     },
40587
40588     /**
40589      * Shortcut to do a submit action.
40590      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40591      * @return {BasicForm} this
40592      */
40593     submit : function(options){
40594         this.doAction('submit', options);
40595         return this;
40596     },
40597
40598     /**
40599      * Shortcut to do a load action.
40600      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40601      * @return {BasicForm} this
40602      */
40603     load : function(options){
40604         this.doAction('load', options);
40605         return this;
40606     },
40607
40608     /**
40609      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40610      * @param {Record} record The record to edit
40611      * @return {BasicForm} this
40612      */
40613     updateRecord : function(record){
40614         record.beginEdit();
40615         var fs = record.fields;
40616         fs.each(function(f){
40617             var field = this.findField(f.name);
40618             if(field){
40619                 record.set(f.name, field.getValue());
40620             }
40621         }, this);
40622         record.endEdit();
40623         return this;
40624     },
40625
40626     /**
40627      * Loads an Roo.data.Record into this form.
40628      * @param {Record} record The record to load
40629      * @return {BasicForm} this
40630      */
40631     loadRecord : function(record){
40632         this.setValues(record.data);
40633         return this;
40634     },
40635
40636     // private
40637     beforeAction : function(action){
40638         var o = action.options;
40639         
40640        
40641         if(this.waitMsgTarget === true){
40642             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40643         }else if(this.waitMsgTarget){
40644             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40645             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40646         }else {
40647             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40648         }
40649          
40650     },
40651
40652     // private
40653     afterAction : function(action, success){
40654         this.activeAction = null;
40655         var o = action.options;
40656         
40657         if(this.waitMsgTarget === true){
40658             this.el.unmask();
40659         }else if(this.waitMsgTarget){
40660             this.waitMsgTarget.unmask();
40661         }else{
40662             Roo.MessageBox.updateProgress(1);
40663             Roo.MessageBox.hide();
40664         }
40665          
40666         if(success){
40667             if(o.reset){
40668                 this.reset();
40669             }
40670             Roo.callback(o.success, o.scope, [this, action]);
40671             this.fireEvent('actioncomplete', this, action);
40672             
40673         }else{
40674             Roo.callback(o.failure, o.scope, [this, action]);
40675             // show an error message if no failed handler is set..
40676             if (!this.hasListener('actionfailed')) {
40677                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40678             }
40679             
40680             this.fireEvent('actionfailed', this, action);
40681         }
40682         
40683     },
40684
40685     /**
40686      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40687      * @param {String} id The value to search for
40688      * @return Field
40689      */
40690     findField : function(id){
40691         var field = this.items.get(id);
40692         if(!field){
40693             this.items.each(function(f){
40694                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40695                     field = f;
40696                     return false;
40697                 }
40698             });
40699         }
40700         return field || null;
40701     },
40702
40703     /**
40704      * Add a secondary form to this one, 
40705      * Used to provide tabbed forms. One form is primary, with hidden values 
40706      * which mirror the elements from the other forms.
40707      * 
40708      * @param {Roo.form.Form} form to add.
40709      * 
40710      */
40711     addForm : function(form)
40712     {
40713        
40714         if (this.childForms.indexOf(form) > -1) {
40715             // already added..
40716             return;
40717         }
40718         this.childForms.push(form);
40719         var n = '';
40720         Roo.each(form.allItems, function (fe) {
40721             
40722             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40723             if (this.findField(n)) { // already added..
40724                 return;
40725             }
40726             var add = new Roo.form.Hidden({
40727                 name : n
40728             });
40729             add.render(this.el);
40730             
40731             this.add( add );
40732         }, this);
40733         
40734     },
40735     /**
40736      * Mark fields in this form invalid in bulk.
40737      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40738      * @return {BasicForm} this
40739      */
40740     markInvalid : function(errors){
40741         if(errors instanceof Array){
40742             for(var i = 0, len = errors.length; i < len; i++){
40743                 var fieldError = errors[i];
40744                 var f = this.findField(fieldError.id);
40745                 if(f){
40746                     f.markInvalid(fieldError.msg);
40747                 }
40748             }
40749         }else{
40750             var field, id;
40751             for(id in errors){
40752                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40753                     field.markInvalid(errors[id]);
40754                 }
40755             }
40756         }
40757         Roo.each(this.childForms || [], function (f) {
40758             f.markInvalid(errors);
40759         });
40760         
40761         return this;
40762     },
40763
40764     /**
40765      * Set values for fields in this form in bulk.
40766      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40767      * @return {BasicForm} this
40768      */
40769     setValues : function(values){
40770         if(values instanceof Array){ // array of objects
40771             for(var i = 0, len = values.length; i < len; i++){
40772                 var v = values[i];
40773                 var f = this.findField(v.id);
40774                 if(f){
40775                     f.setValue(v.value);
40776                     if(this.trackResetOnLoad){
40777                         f.originalValue = f.getValue();
40778                     }
40779                 }
40780             }
40781         }else{ // object hash
40782             var field, id;
40783             for(id in values){
40784                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40785                     
40786                     if (field.setFromData && 
40787                         field.valueField && 
40788                         field.displayField &&
40789                         // combos' with local stores can 
40790                         // be queried via setValue()
40791                         // to set their value..
40792                         (field.store && !field.store.isLocal)
40793                         ) {
40794                         // it's a combo
40795                         var sd = { };
40796                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40797                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40798                         field.setFromData(sd);
40799                         
40800                     } else {
40801                         field.setValue(values[id]);
40802                     }
40803                     
40804                     
40805                     if(this.trackResetOnLoad){
40806                         field.originalValue = field.getValue();
40807                     }
40808                 }
40809             }
40810         }
40811          
40812         Roo.each(this.childForms || [], function (f) {
40813             f.setValues(values);
40814         });
40815                 
40816         return this;
40817     },
40818
40819     /**
40820      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40821      * they are returned as an array.
40822      * @param {Boolean} asString
40823      * @return {Object}
40824      */
40825     getValues : function(asString){
40826         if (this.childForms) {
40827             // copy values from the child forms
40828             Roo.each(this.childForms, function (f) {
40829                 this.setValues(f.getValues());
40830             }, this);
40831         }
40832         
40833         
40834         
40835         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40836         if(asString === true){
40837             return fs;
40838         }
40839         return Roo.urlDecode(fs);
40840     },
40841     
40842     /**
40843      * Returns the fields in this form as an object with key/value pairs. 
40844      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40845      * @return {Object}
40846      */
40847     getFieldValues : function()
40848     {
40849         if (this.childForms) {
40850             // copy values from the child forms
40851             Roo.each(this.childForms, function (f) {
40852                 this.setValues(f.getValues());
40853             }, this);
40854         }
40855         
40856         var ret = {};
40857         this.items.each(function(f){
40858             if (!f.getName()) {
40859                 return;
40860             }
40861             var v = f.getValue();
40862             if ((typeof(v) == 'object') && f.getRawValue) {
40863                 v = f.getRawValue() ; // dates..
40864             }
40865             ret[f.getName()] = v;
40866         });
40867         
40868         return ret;
40869     },
40870
40871     /**
40872      * Clears all invalid messages in this form.
40873      * @return {BasicForm} this
40874      */
40875     clearInvalid : function(){
40876         this.items.each(function(f){
40877            f.clearInvalid();
40878         });
40879         
40880         Roo.each(this.childForms || [], function (f) {
40881             f.clearInvalid();
40882         });
40883         
40884         
40885         return this;
40886     },
40887
40888     /**
40889      * Resets this form.
40890      * @return {BasicForm} this
40891      */
40892     reset : function(){
40893         this.items.each(function(f){
40894             f.reset();
40895         });
40896         
40897         Roo.each(this.childForms || [], function (f) {
40898             f.reset();
40899         });
40900        
40901         
40902         return this;
40903     },
40904
40905     /**
40906      * Add Roo.form components to this form.
40907      * @param {Field} field1
40908      * @param {Field} field2 (optional)
40909      * @param {Field} etc (optional)
40910      * @return {BasicForm} this
40911      */
40912     add : function(){
40913         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40914         return this;
40915     },
40916
40917
40918     /**
40919      * Removes a field from the items collection (does NOT remove its markup).
40920      * @param {Field} field
40921      * @return {BasicForm} this
40922      */
40923     remove : function(field){
40924         this.items.remove(field);
40925         return this;
40926     },
40927
40928     /**
40929      * Looks at the fields in this form, checks them for an id attribute,
40930      * and calls applyTo on the existing dom element with that id.
40931      * @return {BasicForm} this
40932      */
40933     render : function(){
40934         this.items.each(function(f){
40935             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40936                 f.applyTo(f.id);
40937             }
40938         });
40939         return this;
40940     },
40941
40942     /**
40943      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40944      * @param {Object} values
40945      * @return {BasicForm} this
40946      */
40947     applyToFields : function(o){
40948         this.items.each(function(f){
40949            Roo.apply(f, o);
40950         });
40951         return this;
40952     },
40953
40954     /**
40955      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40956      * @param {Object} values
40957      * @return {BasicForm} this
40958      */
40959     applyIfToFields : function(o){
40960         this.items.each(function(f){
40961            Roo.applyIf(f, o);
40962         });
40963         return this;
40964     }
40965 });
40966
40967 // back compat
40968 Roo.BasicForm = Roo.form.BasicForm;/*
40969  * Based on:
40970  * Ext JS Library 1.1.1
40971  * Copyright(c) 2006-2007, Ext JS, LLC.
40972  *
40973  * Originally Released Under LGPL - original licence link has changed is not relivant.
40974  *
40975  * Fork - LGPL
40976  * <script type="text/javascript">
40977  */
40978
40979 /**
40980  * @class Roo.form.Form
40981  * @extends Roo.form.BasicForm
40982  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40983  * @constructor
40984  * @param {Object} config Configuration options
40985  */
40986 Roo.form.Form = function(config){
40987     var xitems =  [];
40988     if (config.items) {
40989         xitems = config.items;
40990         delete config.items;
40991     }
40992    
40993     
40994     Roo.form.Form.superclass.constructor.call(this, null, config);
40995     this.url = this.url || this.action;
40996     if(!this.root){
40997         this.root = new Roo.form.Layout(Roo.applyIf({
40998             id: Roo.id()
40999         }, config));
41000     }
41001     this.active = this.root;
41002     /**
41003      * Array of all the buttons that have been added to this form via {@link addButton}
41004      * @type Array
41005      */
41006     this.buttons = [];
41007     this.allItems = [];
41008     this.addEvents({
41009         /**
41010          * @event clientvalidation
41011          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41012          * @param {Form} this
41013          * @param {Boolean} valid true if the form has passed client-side validation
41014          */
41015         clientvalidation: true,
41016         /**
41017          * @event rendered
41018          * Fires when the form is rendered
41019          * @param {Roo.form.Form} form
41020          */
41021         rendered : true
41022     });
41023     
41024     if (this.progressUrl) {
41025             // push a hidden field onto the list of fields..
41026             this.addxtype( {
41027                     xns: Roo.form, 
41028                     xtype : 'Hidden', 
41029                     name : 'UPLOAD_IDENTIFIER' 
41030             });
41031         }
41032         
41033     
41034     Roo.each(xitems, this.addxtype, this);
41035     
41036     
41037     
41038 };
41039
41040 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41041     /**
41042      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41043      */
41044     /**
41045      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41046      */
41047     /**
41048      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41049      */
41050     buttonAlign:'center',
41051
41052     /**
41053      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41054      */
41055     minButtonWidth:75,
41056
41057     /**
41058      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41059      * This property cascades to child containers if not set.
41060      */
41061     labelAlign:'left',
41062
41063     /**
41064      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41065      * fires a looping event with that state. This is required to bind buttons to the valid
41066      * state using the config value formBind:true on the button.
41067      */
41068     monitorValid : false,
41069
41070     /**
41071      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41072      */
41073     monitorPoll : 200,
41074     
41075     /**
41076      * @cfg {String} progressUrl - Url to return progress data 
41077      */
41078     
41079     progressUrl : false,
41080   
41081     /**
41082      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41083      * fields are added and the column is closed. If no fields are passed the column remains open
41084      * until end() is called.
41085      * @param {Object} config The config to pass to the column
41086      * @param {Field} field1 (optional)
41087      * @param {Field} field2 (optional)
41088      * @param {Field} etc (optional)
41089      * @return Column The column container object
41090      */
41091     column : function(c){
41092         var col = new Roo.form.Column(c);
41093         this.start(col);
41094         if(arguments.length > 1){ // duplicate code required because of Opera
41095             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41096             this.end();
41097         }
41098         return col;
41099     },
41100
41101     /**
41102      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41103      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41104      * until end() is called.
41105      * @param {Object} config The config to pass to the fieldset
41106      * @param {Field} field1 (optional)
41107      * @param {Field} field2 (optional)
41108      * @param {Field} etc (optional)
41109      * @return FieldSet The fieldset container object
41110      */
41111     fieldset : function(c){
41112         var fs = new Roo.form.FieldSet(c);
41113         this.start(fs);
41114         if(arguments.length > 1){ // duplicate code required because of Opera
41115             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41116             this.end();
41117         }
41118         return fs;
41119     },
41120
41121     /**
41122      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41123      * fields are added and the container is closed. If no fields are passed the container remains open
41124      * until end() is called.
41125      * @param {Object} config The config to pass to the Layout
41126      * @param {Field} field1 (optional)
41127      * @param {Field} field2 (optional)
41128      * @param {Field} etc (optional)
41129      * @return Layout The container object
41130      */
41131     container : function(c){
41132         var l = new Roo.form.Layout(c);
41133         this.start(l);
41134         if(arguments.length > 1){ // duplicate code required because of Opera
41135             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41136             this.end();
41137         }
41138         return l;
41139     },
41140
41141     /**
41142      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41143      * @param {Object} container A Roo.form.Layout or subclass of Layout
41144      * @return {Form} this
41145      */
41146     start : function(c){
41147         // cascade label info
41148         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41149         this.active.stack.push(c);
41150         c.ownerCt = this.active;
41151         this.active = c;
41152         return this;
41153     },
41154
41155     /**
41156      * Closes the current open container
41157      * @return {Form} this
41158      */
41159     end : function(){
41160         if(this.active == this.root){
41161             return this;
41162         }
41163         this.active = this.active.ownerCt;
41164         return this;
41165     },
41166
41167     /**
41168      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41169      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41170      * as the label of the field.
41171      * @param {Field} field1
41172      * @param {Field} field2 (optional)
41173      * @param {Field} etc. (optional)
41174      * @return {Form} this
41175      */
41176     add : function(){
41177         this.active.stack.push.apply(this.active.stack, arguments);
41178         this.allItems.push.apply(this.allItems,arguments);
41179         var r = [];
41180         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41181             if(a[i].isFormField){
41182                 r.push(a[i]);
41183             }
41184         }
41185         if(r.length > 0){
41186             Roo.form.Form.superclass.add.apply(this, r);
41187         }
41188         return this;
41189     },
41190     
41191
41192     
41193     
41194     
41195      /**
41196      * Find any element that has been added to a form, using it's ID or name
41197      * This can include framesets, columns etc. along with regular fields..
41198      * @param {String} id - id or name to find.
41199      
41200      * @return {Element} e - or false if nothing found.
41201      */
41202     findbyId : function(id)
41203     {
41204         var ret = false;
41205         if (!id) {
41206             return ret;
41207         }
41208         Roo.each(this.allItems, function(f){
41209             if (f.id == id || f.name == id ){
41210                 ret = f;
41211                 return false;
41212             }
41213         });
41214         return ret;
41215     },
41216
41217     
41218     
41219     /**
41220      * Render this form into the passed container. This should only be called once!
41221      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41222      * @return {Form} this
41223      */
41224     render : function(ct)
41225     {
41226         
41227         
41228         
41229         ct = Roo.get(ct);
41230         var o = this.autoCreate || {
41231             tag: 'form',
41232             method : this.method || 'POST',
41233             id : this.id || Roo.id()
41234         };
41235         this.initEl(ct.createChild(o));
41236
41237         this.root.render(this.el);
41238         
41239        
41240              
41241         this.items.each(function(f){
41242             f.render('x-form-el-'+f.id);
41243         });
41244
41245         if(this.buttons.length > 0){
41246             // tables are required to maintain order and for correct IE layout
41247             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41248                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41249                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41250             }}, null, true);
41251             var tr = tb.getElementsByTagName('tr')[0];
41252             for(var i = 0, len = this.buttons.length; i < len; i++) {
41253                 var b = this.buttons[i];
41254                 var td = document.createElement('td');
41255                 td.className = 'x-form-btn-td';
41256                 b.render(tr.appendChild(td));
41257             }
41258         }
41259         if(this.monitorValid){ // initialize after render
41260             this.startMonitoring();
41261         }
41262         this.fireEvent('rendered', this);
41263         return this;
41264     },
41265
41266     /**
41267      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41268      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41269      * object or a valid Roo.DomHelper element config
41270      * @param {Function} handler The function called when the button is clicked
41271      * @param {Object} scope (optional) The scope of the handler function
41272      * @return {Roo.Button}
41273      */
41274     addButton : function(config, handler, scope){
41275         var bc = {
41276             handler: handler,
41277             scope: scope,
41278             minWidth: this.minButtonWidth,
41279             hideParent:true
41280         };
41281         if(typeof config == "string"){
41282             bc.text = config;
41283         }else{
41284             Roo.apply(bc, config);
41285         }
41286         var btn = new Roo.Button(null, bc);
41287         this.buttons.push(btn);
41288         return btn;
41289     },
41290
41291      /**
41292      * Adds a series of form elements (using the xtype property as the factory method.
41293      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41294      * @param {Object} config 
41295      */
41296     
41297     addxtype : function()
41298     {
41299         var ar = Array.prototype.slice.call(arguments, 0);
41300         var ret = false;
41301         for(var i = 0; i < ar.length; i++) {
41302             if (!ar[i]) {
41303                 continue; // skip -- if this happends something invalid got sent, we 
41304                 // should ignore it, as basically that interface element will not show up
41305                 // and that should be pretty obvious!!
41306             }
41307             
41308             if (Roo.form[ar[i].xtype]) {
41309                 ar[i].form = this;
41310                 var fe = Roo.factory(ar[i], Roo.form);
41311                 if (!ret) {
41312                     ret = fe;
41313                 }
41314                 fe.form = this;
41315                 if (fe.store) {
41316                     fe.store.form = this;
41317                 }
41318                 if (fe.isLayout) {  
41319                          
41320                     this.start(fe);
41321                     this.allItems.push(fe);
41322                     if (fe.items && fe.addxtype) {
41323                         fe.addxtype.apply(fe, fe.items);
41324                         delete fe.items;
41325                     }
41326                      this.end();
41327                     continue;
41328                 }
41329                 
41330                 
41331                  
41332                 this.add(fe);
41333               //  console.log('adding ' + ar[i].xtype);
41334             }
41335             if (ar[i].xtype == 'Button') {  
41336                 //console.log('adding button');
41337                 //console.log(ar[i]);
41338                 this.addButton(ar[i]);
41339                 this.allItems.push(fe);
41340                 continue;
41341             }
41342             
41343             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41344                 alert('end is not supported on xtype any more, use items');
41345             //    this.end();
41346             //    //console.log('adding end');
41347             }
41348             
41349         }
41350         return ret;
41351     },
41352     
41353     /**
41354      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41355      * option "monitorValid"
41356      */
41357     startMonitoring : function(){
41358         if(!this.bound){
41359             this.bound = true;
41360             Roo.TaskMgr.start({
41361                 run : this.bindHandler,
41362                 interval : this.monitorPoll || 200,
41363                 scope: this
41364             });
41365         }
41366     },
41367
41368     /**
41369      * Stops monitoring of the valid state of this form
41370      */
41371     stopMonitoring : function(){
41372         this.bound = false;
41373     },
41374
41375     // private
41376     bindHandler : function(){
41377         if(!this.bound){
41378             return false; // stops binding
41379         }
41380         var valid = true;
41381         this.items.each(function(f){
41382             if(!f.isValid(true)){
41383                 valid = false;
41384                 return false;
41385             }
41386         });
41387         for(var i = 0, len = this.buttons.length; i < len; i++){
41388             var btn = this.buttons[i];
41389             if(btn.formBind === true && btn.disabled === valid){
41390                 btn.setDisabled(!valid);
41391             }
41392         }
41393         this.fireEvent('clientvalidation', this, valid);
41394     }
41395     
41396     
41397     
41398     
41399     
41400     
41401     
41402     
41403 });
41404
41405
41406 // back compat
41407 Roo.Form = Roo.form.Form;
41408 /*
41409  * Based on:
41410  * Ext JS Library 1.1.1
41411  * Copyright(c) 2006-2007, Ext JS, LLC.
41412  *
41413  * Originally Released Under LGPL - original licence link has changed is not relivant.
41414  *
41415  * Fork - LGPL
41416  * <script type="text/javascript">
41417  */
41418  
41419  /**
41420  * @class Roo.form.Action
41421  * Internal Class used to handle form actions
41422  * @constructor
41423  * @param {Roo.form.BasicForm} el The form element or its id
41424  * @param {Object} config Configuration options
41425  */
41426  
41427  
41428 // define the action interface
41429 Roo.form.Action = function(form, options){
41430     this.form = form;
41431     this.options = options || {};
41432 };
41433 /**
41434  * Client Validation Failed
41435  * @const 
41436  */
41437 Roo.form.Action.CLIENT_INVALID = 'client';
41438 /**
41439  * Server Validation Failed
41440  * @const 
41441  */
41442  Roo.form.Action.SERVER_INVALID = 'server';
41443  /**
41444  * Connect to Server Failed
41445  * @const 
41446  */
41447 Roo.form.Action.CONNECT_FAILURE = 'connect';
41448 /**
41449  * Reading Data from Server Failed
41450  * @const 
41451  */
41452 Roo.form.Action.LOAD_FAILURE = 'load';
41453
41454 Roo.form.Action.prototype = {
41455     type : 'default',
41456     failureType : undefined,
41457     response : undefined,
41458     result : undefined,
41459
41460     // interface method
41461     run : function(options){
41462
41463     },
41464
41465     // interface method
41466     success : function(response){
41467
41468     },
41469
41470     // interface method
41471     handleResponse : function(response){
41472
41473     },
41474
41475     // default connection failure
41476     failure : function(response){
41477         
41478         this.response = response;
41479         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41480         this.form.afterAction(this, false);
41481     },
41482
41483     processResponse : function(response){
41484         this.response = response;
41485         if(!response.responseText){
41486             return true;
41487         }
41488         this.result = this.handleResponse(response);
41489         return this.result;
41490     },
41491
41492     // utility functions used internally
41493     getUrl : function(appendParams){
41494         var url = this.options.url || this.form.url || this.form.el.dom.action;
41495         if(appendParams){
41496             var p = this.getParams();
41497             if(p){
41498                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41499             }
41500         }
41501         return url;
41502     },
41503
41504     getMethod : function(){
41505         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41506     },
41507
41508     getParams : function(){
41509         var bp = this.form.baseParams;
41510         var p = this.options.params;
41511         if(p){
41512             if(typeof p == "object"){
41513                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41514             }else if(typeof p == 'string' && bp){
41515                 p += '&' + Roo.urlEncode(bp);
41516             }
41517         }else if(bp){
41518             p = Roo.urlEncode(bp);
41519         }
41520         return p;
41521     },
41522
41523     createCallback : function(){
41524         return {
41525             success: this.success,
41526             failure: this.failure,
41527             scope: this,
41528             timeout: (this.form.timeout*1000),
41529             upload: this.form.fileUpload ? this.success : undefined
41530         };
41531     }
41532 };
41533
41534 Roo.form.Action.Submit = function(form, options){
41535     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41536 };
41537
41538 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41539     type : 'submit',
41540
41541     haveProgress : false,
41542     uploadComplete : false,
41543     
41544     // uploadProgress indicator.
41545     uploadProgress : function()
41546     {
41547         if (!this.form.progressUrl) {
41548             return;
41549         }
41550         
41551         if (!this.haveProgress) {
41552             Roo.MessageBox.progress("Uploading", "Uploading");
41553         }
41554         if (this.uploadComplete) {
41555            Roo.MessageBox.hide();
41556            return;
41557         }
41558         
41559         this.haveProgress = true;
41560    
41561         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41562         
41563         var c = new Roo.data.Connection();
41564         c.request({
41565             url : this.form.progressUrl,
41566             params: {
41567                 id : uid
41568             },
41569             method: 'GET',
41570             success : function(req){
41571                //console.log(data);
41572                 var rdata = false;
41573                 var edata;
41574                 try  {
41575                    rdata = Roo.decode(req.responseText)
41576                 } catch (e) {
41577                     Roo.log("Invalid data from server..");
41578                     Roo.log(edata);
41579                     return;
41580                 }
41581                 if (!rdata || !rdata.success) {
41582                     Roo.log(rdata);
41583                     return;
41584                 }
41585                 var data = rdata.data;
41586                 
41587                 if (this.uploadComplete) {
41588                    Roo.MessageBox.hide();
41589                    return;
41590                 }
41591                    
41592                 if (data){
41593                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41594                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41595                     );
41596                 }
41597                 this.uploadProgress.defer(2000,this);
41598             },
41599        
41600             failure: function(data) {
41601                 Roo.log('progress url failed ');
41602                 Roo.log(data);
41603             },
41604             scope : this
41605         });
41606            
41607     },
41608     
41609     
41610     run : function()
41611     {
41612         // run get Values on the form, so it syncs any secondary forms.
41613         this.form.getValues();
41614         
41615         var o = this.options;
41616         var method = this.getMethod();
41617         var isPost = method == 'POST';
41618         if(o.clientValidation === false || this.form.isValid()){
41619             
41620             if (this.form.progressUrl) {
41621                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41622                     (new Date() * 1) + '' + Math.random());
41623                     
41624             } 
41625             
41626             
41627             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41628                 form:this.form.el.dom,
41629                 url:this.getUrl(!isPost),
41630                 method: method,
41631                 params:isPost ? this.getParams() : null,
41632                 isUpload: this.form.fileUpload
41633             }));
41634             
41635             this.uploadProgress();
41636
41637         }else if (o.clientValidation !== false){ // client validation failed
41638             this.failureType = Roo.form.Action.CLIENT_INVALID;
41639             this.form.afterAction(this, false);
41640         }
41641     },
41642
41643     success : function(response)
41644     {
41645         this.uploadComplete= true;
41646         if (this.haveProgress) {
41647             Roo.MessageBox.hide();
41648         }
41649         
41650         
41651         var result = this.processResponse(response);
41652         if(result === true || result.success){
41653             this.form.afterAction(this, true);
41654             return;
41655         }
41656         if(result.errors){
41657             this.form.markInvalid(result.errors);
41658             this.failureType = Roo.form.Action.SERVER_INVALID;
41659         }
41660         this.form.afterAction(this, false);
41661     },
41662     failure : function(response)
41663     {
41664         this.uploadComplete= true;
41665         if (this.haveProgress) {
41666             Roo.MessageBox.hide();
41667         }
41668         
41669         
41670         this.response = response;
41671         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41672         this.form.afterAction(this, false);
41673     },
41674     
41675     handleResponse : function(response){
41676         if(this.form.errorReader){
41677             var rs = this.form.errorReader.read(response);
41678             var errors = [];
41679             if(rs.records){
41680                 for(var i = 0, len = rs.records.length; i < len; i++) {
41681                     var r = rs.records[i];
41682                     errors[i] = r.data;
41683                 }
41684             }
41685             if(errors.length < 1){
41686                 errors = null;
41687             }
41688             return {
41689                 success : rs.success,
41690                 errors : errors
41691             };
41692         }
41693         var ret = false;
41694         try {
41695             ret = Roo.decode(response.responseText);
41696         } catch (e) {
41697             ret = {
41698                 success: false,
41699                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41700                 errors : []
41701             };
41702         }
41703         return ret;
41704         
41705     }
41706 });
41707
41708
41709 Roo.form.Action.Load = function(form, options){
41710     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41711     this.reader = this.form.reader;
41712 };
41713
41714 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41715     type : 'load',
41716
41717     run : function(){
41718         
41719         Roo.Ajax.request(Roo.apply(
41720                 this.createCallback(), {
41721                     method:this.getMethod(),
41722                     url:this.getUrl(false),
41723                     params:this.getParams()
41724         }));
41725     },
41726
41727     success : function(response){
41728         
41729         var result = this.processResponse(response);
41730         if(result === true || !result.success || !result.data){
41731             this.failureType = Roo.form.Action.LOAD_FAILURE;
41732             this.form.afterAction(this, false);
41733             return;
41734         }
41735         this.form.clearInvalid();
41736         this.form.setValues(result.data);
41737         this.form.afterAction(this, true);
41738     },
41739
41740     handleResponse : function(response){
41741         if(this.form.reader){
41742             var rs = this.form.reader.read(response);
41743             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41744             return {
41745                 success : rs.success,
41746                 data : data
41747             };
41748         }
41749         return Roo.decode(response.responseText);
41750     }
41751 });
41752
41753 Roo.form.Action.ACTION_TYPES = {
41754     'load' : Roo.form.Action.Load,
41755     'submit' : Roo.form.Action.Submit
41756 };/*
41757  * Based on:
41758  * Ext JS Library 1.1.1
41759  * Copyright(c) 2006-2007, Ext JS, LLC.
41760  *
41761  * Originally Released Under LGPL - original licence link has changed is not relivant.
41762  *
41763  * Fork - LGPL
41764  * <script type="text/javascript">
41765  */
41766  
41767 /**
41768  * @class Roo.form.Layout
41769  * @extends Roo.Component
41770  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41771  * @constructor
41772  * @param {Object} config Configuration options
41773  */
41774 Roo.form.Layout = function(config){
41775     var xitems = [];
41776     if (config.items) {
41777         xitems = config.items;
41778         delete config.items;
41779     }
41780     Roo.form.Layout.superclass.constructor.call(this, config);
41781     this.stack = [];
41782     Roo.each(xitems, this.addxtype, this);
41783      
41784 };
41785
41786 Roo.extend(Roo.form.Layout, Roo.Component, {
41787     /**
41788      * @cfg {String/Object} autoCreate
41789      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41790      */
41791     /**
41792      * @cfg {String/Object/Function} style
41793      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41794      * a function which returns such a specification.
41795      */
41796     /**
41797      * @cfg {String} labelAlign
41798      * Valid values are "left," "top" and "right" (defaults to "left")
41799      */
41800     /**
41801      * @cfg {Number} labelWidth
41802      * Fixed width in pixels of all field labels (defaults to undefined)
41803      */
41804     /**
41805      * @cfg {Boolean} clear
41806      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41807      */
41808     clear : true,
41809     /**
41810      * @cfg {String} labelSeparator
41811      * The separator to use after field labels (defaults to ':')
41812      */
41813     labelSeparator : ':',
41814     /**
41815      * @cfg {Boolean} hideLabels
41816      * True to suppress the display of field labels in this layout (defaults to false)
41817      */
41818     hideLabels : false,
41819
41820     // private
41821     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41822     
41823     isLayout : true,
41824     
41825     // private
41826     onRender : function(ct, position){
41827         if(this.el){ // from markup
41828             this.el = Roo.get(this.el);
41829         }else {  // generate
41830             var cfg = this.getAutoCreate();
41831             this.el = ct.createChild(cfg, position);
41832         }
41833         if(this.style){
41834             this.el.applyStyles(this.style);
41835         }
41836         if(this.labelAlign){
41837             this.el.addClass('x-form-label-'+this.labelAlign);
41838         }
41839         if(this.hideLabels){
41840             this.labelStyle = "display:none";
41841             this.elementStyle = "padding-left:0;";
41842         }else{
41843             if(typeof this.labelWidth == 'number'){
41844                 this.labelStyle = "width:"+this.labelWidth+"px;";
41845                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41846             }
41847             if(this.labelAlign == 'top'){
41848                 this.labelStyle = "width:auto;";
41849                 this.elementStyle = "padding-left:0;";
41850             }
41851         }
41852         var stack = this.stack;
41853         var slen = stack.length;
41854         if(slen > 0){
41855             if(!this.fieldTpl){
41856                 var t = new Roo.Template(
41857                     '<div class="x-form-item {5}">',
41858                         '<label for="{0}" style="{2}">{1}{4}</label>',
41859                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41860                         '</div>',
41861                     '</div><div class="x-form-clear-left"></div>'
41862                 );
41863                 t.disableFormats = true;
41864                 t.compile();
41865                 Roo.form.Layout.prototype.fieldTpl = t;
41866             }
41867             for(var i = 0; i < slen; i++) {
41868                 if(stack[i].isFormField){
41869                     this.renderField(stack[i]);
41870                 }else{
41871                     this.renderComponent(stack[i]);
41872                 }
41873             }
41874         }
41875         if(this.clear){
41876             this.el.createChild({cls:'x-form-clear'});
41877         }
41878     },
41879
41880     // private
41881     renderField : function(f){
41882         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41883                f.id, //0
41884                f.fieldLabel, //1
41885                f.labelStyle||this.labelStyle||'', //2
41886                this.elementStyle||'', //3
41887                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41888                f.itemCls||this.itemCls||''  //5
41889        ], true).getPrevSibling());
41890     },
41891
41892     // private
41893     renderComponent : function(c){
41894         c.render(c.isLayout ? this.el : this.el.createChild());    
41895     },
41896     /**
41897      * Adds a object form elements (using the xtype property as the factory method.)
41898      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41899      * @param {Object} config 
41900      */
41901     addxtype : function(o)
41902     {
41903         // create the lement.
41904         o.form = this.form;
41905         var fe = Roo.factory(o, Roo.form);
41906         this.form.allItems.push(fe);
41907         this.stack.push(fe);
41908         
41909         if (fe.isFormField) {
41910             this.form.items.add(fe);
41911         }
41912          
41913         return fe;
41914     }
41915 });
41916
41917 /**
41918  * @class Roo.form.Column
41919  * @extends Roo.form.Layout
41920  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41921  * @constructor
41922  * @param {Object} config Configuration options
41923  */
41924 Roo.form.Column = function(config){
41925     Roo.form.Column.superclass.constructor.call(this, config);
41926 };
41927
41928 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41929     /**
41930      * @cfg {Number/String} width
41931      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41932      */
41933     /**
41934      * @cfg {String/Object} autoCreate
41935      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41936      */
41937
41938     // private
41939     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41940
41941     // private
41942     onRender : function(ct, position){
41943         Roo.form.Column.superclass.onRender.call(this, ct, position);
41944         if(this.width){
41945             this.el.setWidth(this.width);
41946         }
41947     }
41948 });
41949
41950
41951 /**
41952  * @class Roo.form.Row
41953  * @extends Roo.form.Layout
41954  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41955  * @constructor
41956  * @param {Object} config Configuration options
41957  */
41958
41959  
41960 Roo.form.Row = function(config){
41961     Roo.form.Row.superclass.constructor.call(this, config);
41962 };
41963  
41964 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41965       /**
41966      * @cfg {Number/String} width
41967      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41968      */
41969     /**
41970      * @cfg {Number/String} height
41971      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41972      */
41973     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41974     
41975     padWidth : 20,
41976     // private
41977     onRender : function(ct, position){
41978         //console.log('row render');
41979         if(!this.rowTpl){
41980             var t = new Roo.Template(
41981                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41982                     '<label for="{0}" style="{2}">{1}{4}</label>',
41983                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41984                     '</div>',
41985                 '</div>'
41986             );
41987             t.disableFormats = true;
41988             t.compile();
41989             Roo.form.Layout.prototype.rowTpl = t;
41990         }
41991         this.fieldTpl = this.rowTpl;
41992         
41993         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41994         var labelWidth = 100;
41995         
41996         if ((this.labelAlign != 'top')) {
41997             if (typeof this.labelWidth == 'number') {
41998                 labelWidth = this.labelWidth
41999             }
42000             this.padWidth =  20 + labelWidth;
42001             
42002         }
42003         
42004         Roo.form.Column.superclass.onRender.call(this, ct, position);
42005         if(this.width){
42006             this.el.setWidth(this.width);
42007         }
42008         if(this.height){
42009             this.el.setHeight(this.height);
42010         }
42011     },
42012     
42013     // private
42014     renderField : function(f){
42015         f.fieldEl = this.fieldTpl.append(this.el, [
42016                f.id, f.fieldLabel,
42017                f.labelStyle||this.labelStyle||'',
42018                this.elementStyle||'',
42019                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42020                f.itemCls||this.itemCls||'',
42021                f.width ? f.width + this.padWidth : 160 + this.padWidth
42022        ],true);
42023     }
42024 });
42025  
42026
42027 /**
42028  * @class Roo.form.FieldSet
42029  * @extends Roo.form.Layout
42030  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42031  * @constructor
42032  * @param {Object} config Configuration options
42033  */
42034 Roo.form.FieldSet = function(config){
42035     Roo.form.FieldSet.superclass.constructor.call(this, config);
42036 };
42037
42038 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42039     /**
42040      * @cfg {String} legend
42041      * The text to display as the legend for the FieldSet (defaults to '')
42042      */
42043     /**
42044      * @cfg {String/Object} autoCreate
42045      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42046      */
42047
42048     // private
42049     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42050
42051     // private
42052     onRender : function(ct, position){
42053         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42054         if(this.legend){
42055             this.setLegend(this.legend);
42056         }
42057     },
42058
42059     // private
42060     setLegend : function(text){
42061         if(this.rendered){
42062             this.el.child('legend').update(text);
42063         }
42064     }
42065 });/*
42066  * Based on:
42067  * Ext JS Library 1.1.1
42068  * Copyright(c) 2006-2007, Ext JS, LLC.
42069  *
42070  * Originally Released Under LGPL - original licence link has changed is not relivant.
42071  *
42072  * Fork - LGPL
42073  * <script type="text/javascript">
42074  */
42075 /**
42076  * @class Roo.form.VTypes
42077  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42078  * @singleton
42079  */
42080 Roo.form.VTypes = function(){
42081     // closure these in so they are only created once.
42082     var alpha = /^[a-zA-Z_]+$/;
42083     var alphanum = /^[a-zA-Z0-9_]+$/;
42084     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42085     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42086
42087     // All these messages and functions are configurable
42088     return {
42089         /**
42090          * The function used to validate email addresses
42091          * @param {String} value The email address
42092          */
42093         'email' : function(v){
42094             return email.test(v);
42095         },
42096         /**
42097          * The error text to display when the email validation function returns false
42098          * @type String
42099          */
42100         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42101         /**
42102          * The keystroke filter mask to be applied on email input
42103          * @type RegExp
42104          */
42105         'emailMask' : /[a-z0-9_\.\-@]/i,
42106
42107         /**
42108          * The function used to validate URLs
42109          * @param {String} value The URL
42110          */
42111         'url' : function(v){
42112             return url.test(v);
42113         },
42114         /**
42115          * The error text to display when the url validation function returns false
42116          * @type String
42117          */
42118         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42119         
42120         /**
42121          * The function used to validate alpha values
42122          * @param {String} value The value
42123          */
42124         'alpha' : function(v){
42125             return alpha.test(v);
42126         },
42127         /**
42128          * The error text to display when the alpha validation function returns false
42129          * @type String
42130          */
42131         'alphaText' : 'This field should only contain letters and _',
42132         /**
42133          * The keystroke filter mask to be applied on alpha input
42134          * @type RegExp
42135          */
42136         'alphaMask' : /[a-z_]/i,
42137
42138         /**
42139          * The function used to validate alphanumeric values
42140          * @param {String} value The value
42141          */
42142         'alphanum' : function(v){
42143             return alphanum.test(v);
42144         },
42145         /**
42146          * The error text to display when the alphanumeric validation function returns false
42147          * @type String
42148          */
42149         'alphanumText' : 'This field should only contain letters, numbers and _',
42150         /**
42151          * The keystroke filter mask to be applied on alphanumeric input
42152          * @type RegExp
42153          */
42154         'alphanumMask' : /[a-z0-9_]/i
42155     };
42156 }();//<script type="text/javascript">
42157
42158 /**
42159  * @class Roo.form.FCKeditor
42160  * @extends Roo.form.TextArea
42161  * Wrapper around the FCKEditor http://www.fckeditor.net
42162  * @constructor
42163  * Creates a new FCKeditor
42164  * @param {Object} config Configuration options
42165  */
42166 Roo.form.FCKeditor = function(config){
42167     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42168     this.addEvents({
42169          /**
42170          * @event editorinit
42171          * Fired when the editor is initialized - you can add extra handlers here..
42172          * @param {FCKeditor} this
42173          * @param {Object} the FCK object.
42174          */
42175         editorinit : true
42176     });
42177     
42178     
42179 };
42180 Roo.form.FCKeditor.editors = { };
42181 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42182 {
42183     //defaultAutoCreate : {
42184     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42185     //},
42186     // private
42187     /**
42188      * @cfg {Object} fck options - see fck manual for details.
42189      */
42190     fckconfig : false,
42191     
42192     /**
42193      * @cfg {Object} fck toolbar set (Basic or Default)
42194      */
42195     toolbarSet : 'Basic',
42196     /**
42197      * @cfg {Object} fck BasePath
42198      */ 
42199     basePath : '/fckeditor/',
42200     
42201     
42202     frame : false,
42203     
42204     value : '',
42205     
42206    
42207     onRender : function(ct, position)
42208     {
42209         if(!this.el){
42210             this.defaultAutoCreate = {
42211                 tag: "textarea",
42212                 style:"width:300px;height:60px;",
42213                 autocomplete: "off"
42214             };
42215         }
42216         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42217         /*
42218         if(this.grow){
42219             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42220             if(this.preventScrollbars){
42221                 this.el.setStyle("overflow", "hidden");
42222             }
42223             this.el.setHeight(this.growMin);
42224         }
42225         */
42226         //console.log('onrender' + this.getId() );
42227         Roo.form.FCKeditor.editors[this.getId()] = this;
42228          
42229
42230         this.replaceTextarea() ;
42231         
42232     },
42233     
42234     getEditor : function() {
42235         return this.fckEditor;
42236     },
42237     /**
42238      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42239      * @param {Mixed} value The value to set
42240      */
42241     
42242     
42243     setValue : function(value)
42244     {
42245         //console.log('setValue: ' + value);
42246         
42247         if(typeof(value) == 'undefined') { // not sure why this is happending...
42248             return;
42249         }
42250         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42251         
42252         //if(!this.el || !this.getEditor()) {
42253         //    this.value = value;
42254             //this.setValue.defer(100,this,[value]);    
42255         //    return;
42256         //} 
42257         
42258         if(!this.getEditor()) {
42259             return;
42260         }
42261         
42262         this.getEditor().SetData(value);
42263         
42264         //
42265
42266     },
42267
42268     /**
42269      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42270      * @return {Mixed} value The field value
42271      */
42272     getValue : function()
42273     {
42274         
42275         if (this.frame && this.frame.dom.style.display == 'none') {
42276             return Roo.form.FCKeditor.superclass.getValue.call(this);
42277         }
42278         
42279         if(!this.el || !this.getEditor()) {
42280            
42281            // this.getValue.defer(100,this); 
42282             return this.value;
42283         }
42284        
42285         
42286         var value=this.getEditor().GetData();
42287         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42288         return Roo.form.FCKeditor.superclass.getValue.call(this);
42289         
42290
42291     },
42292
42293     /**
42294      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42295      * @return {Mixed} value The field value
42296      */
42297     getRawValue : function()
42298     {
42299         if (this.frame && this.frame.dom.style.display == 'none') {
42300             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42301         }
42302         
42303         if(!this.el || !this.getEditor()) {
42304             //this.getRawValue.defer(100,this); 
42305             return this.value;
42306             return;
42307         }
42308         
42309         
42310         
42311         var value=this.getEditor().GetData();
42312         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42313         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42314          
42315     },
42316     
42317     setSize : function(w,h) {
42318         
42319         
42320         
42321         //if (this.frame && this.frame.dom.style.display == 'none') {
42322         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42323         //    return;
42324         //}
42325         //if(!this.el || !this.getEditor()) {
42326         //    this.setSize.defer(100,this, [w,h]); 
42327         //    return;
42328         //}
42329         
42330         
42331         
42332         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42333         
42334         this.frame.dom.setAttribute('width', w);
42335         this.frame.dom.setAttribute('height', h);
42336         this.frame.setSize(w,h);
42337         
42338     },
42339     
42340     toggleSourceEdit : function(value) {
42341         
42342       
42343          
42344         this.el.dom.style.display = value ? '' : 'none';
42345         this.frame.dom.style.display = value ?  'none' : '';
42346         
42347     },
42348     
42349     
42350     focus: function(tag)
42351     {
42352         if (this.frame.dom.style.display == 'none') {
42353             return Roo.form.FCKeditor.superclass.focus.call(this);
42354         }
42355         if(!this.el || !this.getEditor()) {
42356             this.focus.defer(100,this, [tag]); 
42357             return;
42358         }
42359         
42360         
42361         
42362         
42363         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42364         this.getEditor().Focus();
42365         if (tgs.length) {
42366             if (!this.getEditor().Selection.GetSelection()) {
42367                 this.focus.defer(100,this, [tag]); 
42368                 return;
42369             }
42370             
42371             
42372             var r = this.getEditor().EditorDocument.createRange();
42373             r.setStart(tgs[0],0);
42374             r.setEnd(tgs[0],0);
42375             this.getEditor().Selection.GetSelection().removeAllRanges();
42376             this.getEditor().Selection.GetSelection().addRange(r);
42377             this.getEditor().Focus();
42378         }
42379         
42380     },
42381     
42382     
42383     
42384     replaceTextarea : function()
42385     {
42386         if ( document.getElementById( this.getId() + '___Frame' ) )
42387             return ;
42388         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42389         //{
42390             // We must check the elements firstly using the Id and then the name.
42391         var oTextarea = document.getElementById( this.getId() );
42392         
42393         var colElementsByName = document.getElementsByName( this.getId() ) ;
42394          
42395         oTextarea.style.display = 'none' ;
42396
42397         if ( oTextarea.tabIndex ) {            
42398             this.TabIndex = oTextarea.tabIndex ;
42399         }
42400         
42401         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42402         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42403         this.frame = Roo.get(this.getId() + '___Frame')
42404     },
42405     
42406     _getConfigHtml : function()
42407     {
42408         var sConfig = '' ;
42409
42410         for ( var o in this.fckconfig ) {
42411             sConfig += sConfig.length > 0  ? '&amp;' : '';
42412             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42413         }
42414
42415         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42416     },
42417     
42418     
42419     _getIFrameHtml : function()
42420     {
42421         var sFile = 'fckeditor.html' ;
42422         /* no idea what this is about..
42423         try
42424         {
42425             if ( (/fcksource=true/i).test( window.top.location.search ) )
42426                 sFile = 'fckeditor.original.html' ;
42427         }
42428         catch (e) { 
42429         */
42430
42431         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42432         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42433         
42434         
42435         var html = '<iframe id="' + this.getId() +
42436             '___Frame" src="' + sLink +
42437             '" width="' + this.width +
42438             '" height="' + this.height + '"' +
42439             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42440             ' frameborder="0" scrolling="no"></iframe>' ;
42441
42442         return html ;
42443     },
42444     
42445     _insertHtmlBefore : function( html, element )
42446     {
42447         if ( element.insertAdjacentHTML )       {
42448             // IE
42449             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42450         } else { // Gecko
42451             var oRange = document.createRange() ;
42452             oRange.setStartBefore( element ) ;
42453             var oFragment = oRange.createContextualFragment( html );
42454             element.parentNode.insertBefore( oFragment, element ) ;
42455         }
42456     }
42457     
42458     
42459   
42460     
42461     
42462     
42463     
42464
42465 });
42466
42467 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42468
42469 function FCKeditor_OnComplete(editorInstance){
42470     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42471     f.fckEditor = editorInstance;
42472     //console.log("loaded");
42473     f.fireEvent('editorinit', f, editorInstance);
42474
42475   
42476
42477  
42478
42479
42480
42481
42482
42483
42484
42485
42486
42487
42488
42489
42490
42491
42492
42493 //<script type="text/javascript">
42494 /**
42495  * @class Roo.form.GridField
42496  * @extends Roo.form.Field
42497  * Embed a grid (or editable grid into a form)
42498  * STATUS ALPHA
42499  * 
42500  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42501  * it needs 
42502  * xgrid.store = Roo.data.Store
42503  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42504  * xgrid.store.reader = Roo.data.JsonReader 
42505  * 
42506  * 
42507  * @constructor
42508  * Creates a new GridField
42509  * @param {Object} config Configuration options
42510  */
42511 Roo.form.GridField = function(config){
42512     Roo.form.GridField.superclass.constructor.call(this, config);
42513      
42514 };
42515
42516 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42517     /**
42518      * @cfg {Number} width  - used to restrict width of grid..
42519      */
42520     width : 100,
42521     /**
42522      * @cfg {Number} height - used to restrict height of grid..
42523      */
42524     height : 50,
42525      /**
42526      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42527          * 
42528          *}
42529      */
42530     xgrid : false, 
42531     /**
42532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42533      * {tag: "input", type: "checkbox", autocomplete: "off"})
42534      */
42535    // defaultAutoCreate : { tag: 'div' },
42536     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42537     /**
42538      * @cfg {String} addTitle Text to include for adding a title.
42539      */
42540     addTitle : false,
42541     //
42542     onResize : function(){
42543         Roo.form.Field.superclass.onResize.apply(this, arguments);
42544     },
42545
42546     initEvents : function(){
42547         // Roo.form.Checkbox.superclass.initEvents.call(this);
42548         // has no events...
42549        
42550     },
42551
42552
42553     getResizeEl : function(){
42554         return this.wrap;
42555     },
42556
42557     getPositionEl : function(){
42558         return this.wrap;
42559     },
42560
42561     // private
42562     onRender : function(ct, position){
42563         
42564         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42565         var style = this.style;
42566         delete this.style;
42567         
42568         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42569         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42570         this.viewEl = this.wrap.createChild({ tag: 'div' });
42571         if (style) {
42572             this.viewEl.applyStyles(style);
42573         }
42574         if (this.width) {
42575             this.viewEl.setWidth(this.width);
42576         }
42577         if (this.height) {
42578             this.viewEl.setHeight(this.height);
42579         }
42580         //if(this.inputValue !== undefined){
42581         //this.setValue(this.value);
42582         
42583         
42584         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42585         
42586         
42587         this.grid.render();
42588         this.grid.getDataSource().on('remove', this.refreshValue, this);
42589         this.grid.getDataSource().on('update', this.refreshValue, this);
42590         this.grid.on('afteredit', this.refreshValue, this);
42591  
42592     },
42593      
42594     
42595     /**
42596      * Sets the value of the item. 
42597      * @param {String} either an object  or a string..
42598      */
42599     setValue : function(v){
42600         //this.value = v;
42601         v = v || []; // empty set..
42602         // this does not seem smart - it really only affects memoryproxy grids..
42603         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42604             var ds = this.grid.getDataSource();
42605             // assumes a json reader..
42606             var data = {}
42607             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42608             ds.loadData( data);
42609         }
42610         Roo.form.GridField.superclass.setValue.call(this, v);
42611         this.refreshValue();
42612         // should load data in the grid really....
42613     },
42614     
42615     // private
42616     refreshValue: function() {
42617          var val = [];
42618         this.grid.getDataSource().each(function(r) {
42619             val.push(r.data);
42620         });
42621         this.el.dom.value = Roo.encode(val);
42622     }
42623     
42624      
42625     
42626     
42627 });/*
42628  * Based on:
42629  * Ext JS Library 1.1.1
42630  * Copyright(c) 2006-2007, Ext JS, LLC.
42631  *
42632  * Originally Released Under LGPL - original licence link has changed is not relivant.
42633  *
42634  * Fork - LGPL
42635  * <script type="text/javascript">
42636  */
42637 /**
42638  * @class Roo.form.DisplayField
42639  * @extends Roo.form.Field
42640  * A generic Field to display non-editable data.
42641  * @constructor
42642  * Creates a new Display Field item.
42643  * @param {Object} config Configuration options
42644  */
42645 Roo.form.DisplayField = function(config){
42646     Roo.form.DisplayField.superclass.constructor.call(this, config);
42647     
42648 };
42649
42650 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42651     inputType:      'hidden',
42652     allowBlank:     true,
42653     readOnly:         true,
42654     
42655  
42656     /**
42657      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42658      */
42659     focusClass : undefined,
42660     /**
42661      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42662      */
42663     fieldClass: 'x-form-field',
42664     
42665      /**
42666      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42667      */
42668     valueRenderer: undefined,
42669     
42670     width: 100,
42671     /**
42672      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42673      * {tag: "input", type: "checkbox", autocomplete: "off"})
42674      */
42675      
42676  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42677
42678     onResize : function(){
42679         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42680         
42681     },
42682
42683     initEvents : function(){
42684         // Roo.form.Checkbox.superclass.initEvents.call(this);
42685         // has no events...
42686        
42687     },
42688
42689
42690     getResizeEl : function(){
42691         return this.wrap;
42692     },
42693
42694     getPositionEl : function(){
42695         return this.wrap;
42696     },
42697
42698     // private
42699     onRender : function(ct, position){
42700         
42701         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42702         //if(this.inputValue !== undefined){
42703         this.wrap = this.el.wrap();
42704         
42705         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42706         
42707         if (this.bodyStyle) {
42708             this.viewEl.applyStyles(this.bodyStyle);
42709         }
42710         //this.viewEl.setStyle('padding', '2px');
42711         
42712         this.setValue(this.value);
42713         
42714     },
42715 /*
42716     // private
42717     initValue : Roo.emptyFn,
42718
42719   */
42720
42721         // private
42722     onClick : function(){
42723         
42724     },
42725
42726     /**
42727      * Sets the checked state of the checkbox.
42728      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42729      */
42730     setValue : function(v){
42731         this.value = v;
42732         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42733         // this might be called before we have a dom element..
42734         if (!this.viewEl) {
42735             return;
42736         }
42737         this.viewEl.dom.innerHTML = html;
42738         Roo.form.DisplayField.superclass.setValue.call(this, v);
42739
42740     }
42741 });/*
42742  * 
42743  * Licence- LGPL
42744  * 
42745  */
42746
42747 /**
42748  * @class Roo.form.DayPicker
42749  * @extends Roo.form.Field
42750  * A Day picker show [M] [T] [W] ....
42751  * @constructor
42752  * Creates a new Day Picker
42753  * @param {Object} config Configuration options
42754  */
42755 Roo.form.DayPicker= function(config){
42756     Roo.form.DayPicker.superclass.constructor.call(this, config);
42757      
42758 };
42759
42760 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42761     /**
42762      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42763      */
42764     focusClass : undefined,
42765     /**
42766      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42767      */
42768     fieldClass: "x-form-field",
42769    
42770     /**
42771      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42772      * {tag: "input", type: "checkbox", autocomplete: "off"})
42773      */
42774     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42775     
42776    
42777     actionMode : 'viewEl', 
42778     //
42779     // private
42780  
42781     inputType : 'hidden',
42782     
42783      
42784     inputElement: false, // real input element?
42785     basedOn: false, // ????
42786     
42787     isFormField: true, // not sure where this is needed!!!!
42788
42789     onResize : function(){
42790         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42791         if(!this.boxLabel){
42792             this.el.alignTo(this.wrap, 'c-c');
42793         }
42794     },
42795
42796     initEvents : function(){
42797         Roo.form.Checkbox.superclass.initEvents.call(this);
42798         this.el.on("click", this.onClick,  this);
42799         this.el.on("change", this.onClick,  this);
42800     },
42801
42802
42803     getResizeEl : function(){
42804         return this.wrap;
42805     },
42806
42807     getPositionEl : function(){
42808         return this.wrap;
42809     },
42810
42811     
42812     // private
42813     onRender : function(ct, position){
42814         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42815        
42816         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42817         
42818         var r1 = '<table><tr>';
42819         var r2 = '<tr class="x-form-daypick-icons">';
42820         for (var i=0; i < 7; i++) {
42821             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42822             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42823         }
42824         
42825         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42826         viewEl.select('img').on('click', this.onClick, this);
42827         this.viewEl = viewEl;   
42828         
42829         
42830         // this will not work on Chrome!!!
42831         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42832         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42833         
42834         
42835           
42836
42837     },
42838
42839     // private
42840     initValue : Roo.emptyFn,
42841
42842     /**
42843      * Returns the checked state of the checkbox.
42844      * @return {Boolean} True if checked, else false
42845      */
42846     getValue : function(){
42847         return this.el.dom.value;
42848         
42849     },
42850
42851         // private
42852     onClick : function(e){ 
42853         //this.setChecked(!this.checked);
42854         Roo.get(e.target).toggleClass('x-menu-item-checked');
42855         this.refreshValue();
42856         //if(this.el.dom.checked != this.checked){
42857         //    this.setValue(this.el.dom.checked);
42858        // }
42859     },
42860     
42861     // private
42862     refreshValue : function()
42863     {
42864         var val = '';
42865         this.viewEl.select('img',true).each(function(e,i,n)  {
42866             val += e.is(".x-menu-item-checked") ? String(n) : '';
42867         });
42868         this.setValue(val, true);
42869     },
42870
42871     /**
42872      * Sets the checked state of the checkbox.
42873      * On is always based on a string comparison between inputValue and the param.
42874      * @param {Boolean/String} value - the value to set 
42875      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42876      */
42877     setValue : function(v,suppressEvent){
42878         if (!this.el.dom) {
42879             return;
42880         }
42881         var old = this.el.dom.value ;
42882         this.el.dom.value = v;
42883         if (suppressEvent) {
42884             return ;
42885         }
42886          
42887         // update display..
42888         this.viewEl.select('img',true).each(function(e,i,n)  {
42889             
42890             var on = e.is(".x-menu-item-checked");
42891             var newv = v.indexOf(String(n)) > -1;
42892             if (on != newv) {
42893                 e.toggleClass('x-menu-item-checked');
42894             }
42895             
42896         });
42897         
42898         
42899         this.fireEvent('change', this, v, old);
42900         
42901         
42902     },
42903    
42904     // handle setting of hidden value by some other method!!?!?
42905     setFromHidden: function()
42906     {
42907         if(!this.el){
42908             return;
42909         }
42910         //console.log("SET FROM HIDDEN");
42911         //alert('setFrom hidden');
42912         this.setValue(this.el.dom.value);
42913     },
42914     
42915     onDestroy : function()
42916     {
42917         if(this.viewEl){
42918             Roo.get(this.viewEl).remove();
42919         }
42920          
42921         Roo.form.DayPicker.superclass.onDestroy.call(this);
42922     }
42923
42924 });//<script type="text/javasscript">
42925  
42926
42927 /**
42928  * @class Roo.DDView
42929  * A DnD enabled version of Roo.View.
42930  * @param {Element/String} container The Element in which to create the View.
42931  * @param {String} tpl The template string used to create the markup for each element of the View
42932  * @param {Object} config The configuration properties. These include all the config options of
42933  * {@link Roo.View} plus some specific to this class.<br>
42934  * <p>
42935  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42936  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42937  * <p>
42938  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42939 .x-view-drag-insert-above {
42940         border-top:1px dotted #3366cc;
42941 }
42942 .x-view-drag-insert-below {
42943         border-bottom:1px dotted #3366cc;
42944 }
42945 </code></pre>
42946  * 
42947  */
42948  
42949 Roo.DDView = function(container, tpl, config) {
42950     Roo.DDView.superclass.constructor.apply(this, arguments);
42951     this.getEl().setStyle("outline", "0px none");
42952     this.getEl().unselectable();
42953     if (this.dragGroup) {
42954                 this.setDraggable(this.dragGroup.split(","));
42955     }
42956     if (this.dropGroup) {
42957                 this.setDroppable(this.dropGroup.split(","));
42958     }
42959     if (this.deletable) {
42960         this.setDeletable();
42961     }
42962     this.isDirtyFlag = false;
42963         this.addEvents({
42964                 "drop" : true
42965         });
42966 };
42967
42968 Roo.extend(Roo.DDView, Roo.View, {
42969 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42970 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42971 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42972 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42973
42974         isFormField: true,
42975
42976         reset: Roo.emptyFn,
42977         
42978         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42979
42980         validate: function() {
42981                 return true;
42982         },
42983         
42984         destroy: function() {
42985                 this.purgeListeners();
42986                 this.getEl.removeAllListeners();
42987                 this.getEl().remove();
42988                 if (this.dragZone) {
42989                         if (this.dragZone.destroy) {
42990                                 this.dragZone.destroy();
42991                         }
42992                 }
42993                 if (this.dropZone) {
42994                         if (this.dropZone.destroy) {
42995                                 this.dropZone.destroy();
42996                         }
42997                 }
42998         },
42999
43000 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43001         getName: function() {
43002                 return this.name;
43003         },
43004
43005 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43006         setValue: function(v) {
43007                 if (!this.store) {
43008                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43009                 }
43010                 var data = {};
43011                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43012                 this.store.proxy = new Roo.data.MemoryProxy(data);
43013                 this.store.load();
43014         },
43015
43016 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43017         getValue: function() {
43018                 var result = '(';
43019                 this.store.each(function(rec) {
43020                         result += rec.id + ',';
43021                 });
43022                 return result.substr(0, result.length - 1) + ')';
43023         },
43024         
43025         getIds: function() {
43026                 var i = 0, result = new Array(this.store.getCount());
43027                 this.store.each(function(rec) {
43028                         result[i++] = rec.id;
43029                 });
43030                 return result;
43031         },
43032         
43033         isDirty: function() {
43034                 return this.isDirtyFlag;
43035         },
43036
43037 /**
43038  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43039  *      whole Element becomes the target, and this causes the drop gesture to append.
43040  */
43041     getTargetFromEvent : function(e) {
43042                 var target = e.getTarget();
43043                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43044                 target = target.parentNode;
43045                 }
43046                 if (!target) {
43047                         target = this.el.dom.lastChild || this.el.dom;
43048                 }
43049                 return target;
43050     },
43051
43052 /**
43053  *      Create the drag data which consists of an object which has the property "ddel" as
43054  *      the drag proxy element. 
43055  */
43056     getDragData : function(e) {
43057         var target = this.findItemFromChild(e.getTarget());
43058                 if(target) {
43059                         this.handleSelection(e);
43060                         var selNodes = this.getSelectedNodes();
43061             var dragData = {
43062                 source: this,
43063                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43064                 nodes: selNodes,
43065                 records: []
43066                         };
43067                         var selectedIndices = this.getSelectedIndexes();
43068                         for (var i = 0; i < selectedIndices.length; i++) {
43069                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43070                         }
43071                         if (selNodes.length == 1) {
43072                                 dragData.ddel = target.cloneNode(true); // the div element
43073                         } else {
43074                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43075                                 div.className = 'multi-proxy';
43076                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43077                                         div.appendChild(selNodes[i].cloneNode(true));
43078                                 }
43079                                 dragData.ddel = div;
43080                         }
43081             //console.log(dragData)
43082             //console.log(dragData.ddel.innerHTML)
43083                         return dragData;
43084                 }
43085         //console.log('nodragData')
43086                 return false;
43087     },
43088     
43089 /**     Specify to which ddGroup items in this DDView may be dragged. */
43090     setDraggable: function(ddGroup) {
43091         if (ddGroup instanceof Array) {
43092                 Roo.each(ddGroup, this.setDraggable, this);
43093                 return;
43094         }
43095         if (this.dragZone) {
43096                 this.dragZone.addToGroup(ddGroup);
43097         } else {
43098                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43099                                 containerScroll: true,
43100                                 ddGroup: ddGroup 
43101
43102                         });
43103 //                      Draggability implies selection. DragZone's mousedown selects the element.
43104                         if (!this.multiSelect) { this.singleSelect = true; }
43105
43106 //                      Wire the DragZone's handlers up to methods in *this*
43107                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43108                 }
43109     },
43110
43111 /**     Specify from which ddGroup this DDView accepts drops. */
43112     setDroppable: function(ddGroup) {
43113         if (ddGroup instanceof Array) {
43114                 Roo.each(ddGroup, this.setDroppable, this);
43115                 return;
43116         }
43117         if (this.dropZone) {
43118                 this.dropZone.addToGroup(ddGroup);
43119         } else {
43120                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43121                                 containerScroll: true,
43122                                 ddGroup: ddGroup
43123                         });
43124
43125 //                      Wire the DropZone's handlers up to methods in *this*
43126                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43127                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43128                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43129                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43130                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43131                 }
43132     },
43133
43134 /**     Decide whether to drop above or below a View node. */
43135     getDropPoint : function(e, n, dd){
43136         if (n == this.el.dom) { return "above"; }
43137                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43138                 var c = t + (b - t) / 2;
43139                 var y = Roo.lib.Event.getPageY(e);
43140                 if(y <= c) {
43141                         return "above";
43142                 }else{
43143                         return "below";
43144                 }
43145     },
43146
43147     onNodeEnter : function(n, dd, e, data){
43148                 return false;
43149     },
43150     
43151     onNodeOver : function(n, dd, e, data){
43152                 var pt = this.getDropPoint(e, n, dd);
43153                 // set the insert point style on the target node
43154                 var dragElClass = this.dropNotAllowed;
43155                 if (pt) {
43156                         var targetElClass;
43157                         if (pt == "above"){
43158                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43159                                 targetElClass = "x-view-drag-insert-above";
43160                         } else {
43161                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43162                                 targetElClass = "x-view-drag-insert-below";
43163                         }
43164                         if (this.lastInsertClass != targetElClass){
43165                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43166                                 this.lastInsertClass = targetElClass;
43167                         }
43168                 }
43169                 return dragElClass;
43170         },
43171
43172     onNodeOut : function(n, dd, e, data){
43173                 this.removeDropIndicators(n);
43174     },
43175
43176     onNodeDrop : function(n, dd, e, data){
43177         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43178                 return false;
43179         }
43180         var pt = this.getDropPoint(e, n, dd);
43181                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43182                 if (pt == "below") { insertAt++; }
43183                 for (var i = 0; i < data.records.length; i++) {
43184                         var r = data.records[i];
43185                         var dup = this.store.getById(r.id);
43186                         if (dup && (dd != this.dragZone)) {
43187                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43188                         } else {
43189                                 if (data.copy) {
43190                                         this.store.insert(insertAt++, r.copy());
43191                                 } else {
43192                                         data.source.isDirtyFlag = true;
43193                                         r.store.remove(r);
43194                                         this.store.insert(insertAt++, r);
43195                                 }
43196                                 this.isDirtyFlag = true;
43197                         }
43198                 }
43199                 this.dragZone.cachedTarget = null;
43200                 return true;
43201     },
43202
43203     removeDropIndicators : function(n){
43204                 if(n){
43205                         Roo.fly(n).removeClass([
43206                                 "x-view-drag-insert-above",
43207                                 "x-view-drag-insert-below"]);
43208                         this.lastInsertClass = "_noclass";
43209                 }
43210     },
43211
43212 /**
43213  *      Utility method. Add a delete option to the DDView's context menu.
43214  *      @param {String} imageUrl The URL of the "delete" icon image.
43215  */
43216         setDeletable: function(imageUrl) {
43217                 if (!this.singleSelect && !this.multiSelect) {
43218                         this.singleSelect = true;
43219                 }
43220                 var c = this.getContextMenu();
43221                 this.contextMenu.on("itemclick", function(item) {
43222                         switch (item.id) {
43223                                 case "delete":
43224                                         this.remove(this.getSelectedIndexes());
43225                                         break;
43226                         }
43227                 }, this);
43228                 this.contextMenu.add({
43229                         icon: imageUrl,
43230                         id: "delete",
43231                         text: 'Delete'
43232                 });
43233         },
43234         
43235 /**     Return the context menu for this DDView. */
43236         getContextMenu: function() {
43237                 if (!this.contextMenu) {
43238 //                      Create the View's context menu
43239                         this.contextMenu = new Roo.menu.Menu({
43240                                 id: this.id + "-contextmenu"
43241                         });
43242                         this.el.on("contextmenu", this.showContextMenu, this);
43243                 }
43244                 return this.contextMenu;
43245         },
43246         
43247         disableContextMenu: function() {
43248                 if (this.contextMenu) {
43249                         this.el.un("contextmenu", this.showContextMenu, this);
43250                 }
43251         },
43252
43253         showContextMenu: function(e, item) {
43254         item = this.findItemFromChild(e.getTarget());
43255                 if (item) {
43256                         e.stopEvent();
43257                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43258                         this.contextMenu.showAt(e.getXY());
43259             }
43260     },
43261
43262 /**
43263  *      Remove {@link Roo.data.Record}s at the specified indices.
43264  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43265  */
43266     remove: function(selectedIndices) {
43267                 selectedIndices = [].concat(selectedIndices);
43268                 for (var i = 0; i < selectedIndices.length; i++) {
43269                         var rec = this.store.getAt(selectedIndices[i]);
43270                         this.store.remove(rec);
43271                 }
43272     },
43273
43274 /**
43275  *      Double click fires the event, but also, if this is draggable, and there is only one other
43276  *      related DropZone, it transfers the selected node.
43277  */
43278     onDblClick : function(e){
43279         var item = this.findItemFromChild(e.getTarget());
43280         if(item){
43281             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43282                 return false;
43283             }
43284             if (this.dragGroup) {
43285                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43286                     while (targets.indexOf(this.dropZone) > -1) {
43287                             targets.remove(this.dropZone);
43288                                 }
43289                     if (targets.length == 1) {
43290                                         this.dragZone.cachedTarget = null;
43291                         var el = Roo.get(targets[0].getEl());
43292                         var box = el.getBox(true);
43293                         targets[0].onNodeDrop(el.dom, {
43294                                 target: el.dom,
43295                                 xy: [box.x, box.y + box.height - 1]
43296                         }, null, this.getDragData(e));
43297                     }
43298                 }
43299         }
43300     },
43301     
43302     handleSelection: function(e) {
43303                 this.dragZone.cachedTarget = null;
43304         var item = this.findItemFromChild(e.getTarget());
43305         if (!item) {
43306                 this.clearSelections(true);
43307                 return;
43308         }
43309                 if (item && (this.multiSelect || this.singleSelect)){
43310                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43311                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43312                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43313                                 this.unselect(item);
43314                         } else {
43315                                 this.select(item, this.multiSelect && e.ctrlKey);
43316                                 this.lastSelection = item;
43317                         }
43318                 }
43319     },
43320
43321     onItemClick : function(item, index, e){
43322                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43323                         return false;
43324                 }
43325                 return true;
43326     },
43327
43328     unselect : function(nodeInfo, suppressEvent){
43329                 var node = this.getNode(nodeInfo);
43330                 if(node && this.isSelected(node)){
43331                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43332                                 Roo.fly(node).removeClass(this.selectedClass);
43333                                 this.selections.remove(node);
43334                                 if(!suppressEvent){
43335                                         this.fireEvent("selectionchange", this, this.selections);
43336                                 }
43337                         }
43338                 }
43339     }
43340 });
43341 /*
43342  * Based on:
43343  * Ext JS Library 1.1.1
43344  * Copyright(c) 2006-2007, Ext JS, LLC.
43345  *
43346  * Originally Released Under LGPL - original licence link has changed is not relivant.
43347  *
43348  * Fork - LGPL
43349  * <script type="text/javascript">
43350  */
43351  
43352 /**
43353  * @class Roo.LayoutManager
43354  * @extends Roo.util.Observable
43355  * Base class for layout managers.
43356  */
43357 Roo.LayoutManager = function(container, config){
43358     Roo.LayoutManager.superclass.constructor.call(this);
43359     this.el = Roo.get(container);
43360     // ie scrollbar fix
43361     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43362         document.body.scroll = "no";
43363     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43364         this.el.position('relative');
43365     }
43366     this.id = this.el.id;
43367     this.el.addClass("x-layout-container");
43368     /** false to disable window resize monitoring @type Boolean */
43369     this.monitorWindowResize = true;
43370     this.regions = {};
43371     this.addEvents({
43372         /**
43373          * @event layout
43374          * Fires when a layout is performed. 
43375          * @param {Roo.LayoutManager} this
43376          */
43377         "layout" : true,
43378         /**
43379          * @event regionresized
43380          * Fires when the user resizes a region. 
43381          * @param {Roo.LayoutRegion} region The resized region
43382          * @param {Number} newSize The new size (width for east/west, height for north/south)
43383          */
43384         "regionresized" : true,
43385         /**
43386          * @event regioncollapsed
43387          * Fires when a region is collapsed. 
43388          * @param {Roo.LayoutRegion} region The collapsed region
43389          */
43390         "regioncollapsed" : true,
43391         /**
43392          * @event regionexpanded
43393          * Fires when a region is expanded.  
43394          * @param {Roo.LayoutRegion} region The expanded region
43395          */
43396         "regionexpanded" : true
43397     });
43398     this.updating = false;
43399     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43400 };
43401
43402 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43403     /**
43404      * Returns true if this layout is currently being updated
43405      * @return {Boolean}
43406      */
43407     isUpdating : function(){
43408         return this.updating; 
43409     },
43410     
43411     /**
43412      * Suspend the LayoutManager from doing auto-layouts while
43413      * making multiple add or remove calls
43414      */
43415     beginUpdate : function(){
43416         this.updating = true;    
43417     },
43418     
43419     /**
43420      * Restore auto-layouts and optionally disable the manager from performing a layout
43421      * @param {Boolean} noLayout true to disable a layout update 
43422      */
43423     endUpdate : function(noLayout){
43424         this.updating = false;
43425         if(!noLayout){
43426             this.layout();
43427         }    
43428     },
43429     
43430     layout: function(){
43431         
43432     },
43433     
43434     onRegionResized : function(region, newSize){
43435         this.fireEvent("regionresized", region, newSize);
43436         this.layout();
43437     },
43438     
43439     onRegionCollapsed : function(region){
43440         this.fireEvent("regioncollapsed", region);
43441     },
43442     
43443     onRegionExpanded : function(region){
43444         this.fireEvent("regionexpanded", region);
43445     },
43446         
43447     /**
43448      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43449      * performs box-model adjustments.
43450      * @return {Object} The size as an object {width: (the width), height: (the height)}
43451      */
43452     getViewSize : function(){
43453         var size;
43454         if(this.el.dom != document.body){
43455             size = this.el.getSize();
43456         }else{
43457             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43458         }
43459         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43460         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43461         return size;
43462     },
43463     
43464     /**
43465      * Returns the Element this layout is bound to.
43466      * @return {Roo.Element}
43467      */
43468     getEl : function(){
43469         return this.el;
43470     },
43471     
43472     /**
43473      * Returns the specified region.
43474      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43475      * @return {Roo.LayoutRegion}
43476      */
43477     getRegion : function(target){
43478         return this.regions[target.toLowerCase()];
43479     },
43480     
43481     onWindowResize : function(){
43482         if(this.monitorWindowResize){
43483             this.layout();
43484         }
43485     }
43486 });/*
43487  * Based on:
43488  * Ext JS Library 1.1.1
43489  * Copyright(c) 2006-2007, Ext JS, LLC.
43490  *
43491  * Originally Released Under LGPL - original licence link has changed is not relivant.
43492  *
43493  * Fork - LGPL
43494  * <script type="text/javascript">
43495  */
43496 /**
43497  * @class Roo.BorderLayout
43498  * @extends Roo.LayoutManager
43499  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43500  * please see: <br><br>
43501  * <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>
43502  * <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>
43503  * Example:
43504  <pre><code>
43505  var layout = new Roo.BorderLayout(document.body, {
43506     north: {
43507         initialSize: 25,
43508         titlebar: false
43509     },
43510     west: {
43511         split:true,
43512         initialSize: 200,
43513         minSize: 175,
43514         maxSize: 400,
43515         titlebar: true,
43516         collapsible: true
43517     },
43518     east: {
43519         split:true,
43520         initialSize: 202,
43521         minSize: 175,
43522         maxSize: 400,
43523         titlebar: true,
43524         collapsible: true
43525     },
43526     south: {
43527         split:true,
43528         initialSize: 100,
43529         minSize: 100,
43530         maxSize: 200,
43531         titlebar: true,
43532         collapsible: true
43533     },
43534     center: {
43535         titlebar: true,
43536         autoScroll:true,
43537         resizeTabs: true,
43538         minTabWidth: 50,
43539         preferredTabWidth: 150
43540     }
43541 });
43542
43543 // shorthand
43544 var CP = Roo.ContentPanel;
43545
43546 layout.beginUpdate();
43547 layout.add("north", new CP("north", "North"));
43548 layout.add("south", new CP("south", {title: "South", closable: true}));
43549 layout.add("west", new CP("west", {title: "West"}));
43550 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43551 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43552 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43553 layout.getRegion("center").showPanel("center1");
43554 layout.endUpdate();
43555 </code></pre>
43556
43557 <b>The container the layout is rendered into can be either the body element or any other element.
43558 If it is not the body element, the container needs to either be an absolute positioned element,
43559 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43560 the container size if it is not the body element.</b>
43561
43562 * @constructor
43563 * Create a new BorderLayout
43564 * @param {String/HTMLElement/Element} container The container this layout is bound to
43565 * @param {Object} config Configuration options
43566  */
43567 Roo.BorderLayout = function(container, config){
43568     config = config || {};
43569     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43570     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43571     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43572         var target = this.factory.validRegions[i];
43573         if(config[target]){
43574             this.addRegion(target, config[target]);
43575         }
43576     }
43577 };
43578
43579 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43580     /**
43581      * Creates and adds a new region if it doesn't already exist.
43582      * @param {String} target The target region key (north, south, east, west or center).
43583      * @param {Object} config The regions config object
43584      * @return {BorderLayoutRegion} The new region
43585      */
43586     addRegion : function(target, config){
43587         if(!this.regions[target]){
43588             var r = this.factory.create(target, this, config);
43589             this.bindRegion(target, r);
43590         }
43591         return this.regions[target];
43592     },
43593
43594     // private (kinda)
43595     bindRegion : function(name, r){
43596         this.regions[name] = r;
43597         r.on("visibilitychange", this.layout, this);
43598         r.on("paneladded", this.layout, this);
43599         r.on("panelremoved", this.layout, this);
43600         r.on("invalidated", this.layout, this);
43601         r.on("resized", this.onRegionResized, this);
43602         r.on("collapsed", this.onRegionCollapsed, this);
43603         r.on("expanded", this.onRegionExpanded, this);
43604     },
43605
43606     /**
43607      * Performs a layout update.
43608      */
43609     layout : function(){
43610         if(this.updating) return;
43611         var size = this.getViewSize();
43612         var w = size.width;
43613         var h = size.height;
43614         var centerW = w;
43615         var centerH = h;
43616         var centerY = 0;
43617         var centerX = 0;
43618         //var x = 0, y = 0;
43619
43620         var rs = this.regions;
43621         var north = rs["north"];
43622         var south = rs["south"]; 
43623         var west = rs["west"];
43624         var east = rs["east"];
43625         var center = rs["center"];
43626         //if(this.hideOnLayout){ // not supported anymore
43627             //c.el.setStyle("display", "none");
43628         //}
43629         if(north && north.isVisible()){
43630             var b = north.getBox();
43631             var m = north.getMargins();
43632             b.width = w - (m.left+m.right);
43633             b.x = m.left;
43634             b.y = m.top;
43635             centerY = b.height + b.y + m.bottom;
43636             centerH -= centerY;
43637             north.updateBox(this.safeBox(b));
43638         }
43639         if(south && south.isVisible()){
43640             var b = south.getBox();
43641             var m = south.getMargins();
43642             b.width = w - (m.left+m.right);
43643             b.x = m.left;
43644             var totalHeight = (b.height + m.top + m.bottom);
43645             b.y = h - totalHeight + m.top;
43646             centerH -= totalHeight;
43647             south.updateBox(this.safeBox(b));
43648         }
43649         if(west && west.isVisible()){
43650             var b = west.getBox();
43651             var m = west.getMargins();
43652             b.height = centerH - (m.top+m.bottom);
43653             b.x = m.left;
43654             b.y = centerY + m.top;
43655             var totalWidth = (b.width + m.left + m.right);
43656             centerX += totalWidth;
43657             centerW -= totalWidth;
43658             west.updateBox(this.safeBox(b));
43659         }
43660         if(east && east.isVisible()){
43661             var b = east.getBox();
43662             var m = east.getMargins();
43663             b.height = centerH - (m.top+m.bottom);
43664             var totalWidth = (b.width + m.left + m.right);
43665             b.x = w - totalWidth + m.left;
43666             b.y = centerY + m.top;
43667             centerW -= totalWidth;
43668             east.updateBox(this.safeBox(b));
43669         }
43670         if(center){
43671             var m = center.getMargins();
43672             var centerBox = {
43673                 x: centerX + m.left,
43674                 y: centerY + m.top,
43675                 width: centerW - (m.left+m.right),
43676                 height: centerH - (m.top+m.bottom)
43677             };
43678             //if(this.hideOnLayout){
43679                 //center.el.setStyle("display", "block");
43680             //}
43681             center.updateBox(this.safeBox(centerBox));
43682         }
43683         this.el.repaint();
43684         this.fireEvent("layout", this);
43685     },
43686
43687     // private
43688     safeBox : function(box){
43689         box.width = Math.max(0, box.width);
43690         box.height = Math.max(0, box.height);
43691         return box;
43692     },
43693
43694     /**
43695      * Adds a ContentPanel (or subclass) to this layout.
43696      * @param {String} target The target region key (north, south, east, west or center).
43697      * @param {Roo.ContentPanel} panel The panel to add
43698      * @return {Roo.ContentPanel} The added panel
43699      */
43700     add : function(target, panel){
43701          
43702         target = target.toLowerCase();
43703         return this.regions[target].add(panel);
43704     },
43705
43706     /**
43707      * Remove a ContentPanel (or subclass) to this layout.
43708      * @param {String} target The target region key (north, south, east, west or center).
43709      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43710      * @return {Roo.ContentPanel} The removed panel
43711      */
43712     remove : function(target, panel){
43713         target = target.toLowerCase();
43714         return this.regions[target].remove(panel);
43715     },
43716
43717     /**
43718      * Searches all regions for a panel with the specified id
43719      * @param {String} panelId
43720      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43721      */
43722     findPanel : function(panelId){
43723         var rs = this.regions;
43724         for(var target in rs){
43725             if(typeof rs[target] != "function"){
43726                 var p = rs[target].getPanel(panelId);
43727                 if(p){
43728                     return p;
43729                 }
43730             }
43731         }
43732         return null;
43733     },
43734
43735     /**
43736      * Searches all regions for a panel with the specified id and activates (shows) it.
43737      * @param {String/ContentPanel} panelId The panels id or the panel itself
43738      * @return {Roo.ContentPanel} The shown panel or null
43739      */
43740     showPanel : function(panelId) {
43741       var rs = this.regions;
43742       for(var target in rs){
43743          var r = rs[target];
43744          if(typeof r != "function"){
43745             if(r.hasPanel(panelId)){
43746                return r.showPanel(panelId);
43747             }
43748          }
43749       }
43750       return null;
43751    },
43752
43753    /**
43754      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43755      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43756      */
43757     restoreState : function(provider){
43758         if(!provider){
43759             provider = Roo.state.Manager;
43760         }
43761         var sm = new Roo.LayoutStateManager();
43762         sm.init(this, provider);
43763     },
43764
43765     /**
43766      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43767      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43768      * a valid ContentPanel config object.  Example:
43769      * <pre><code>
43770 // Create the main layout
43771 var layout = new Roo.BorderLayout('main-ct', {
43772     west: {
43773         split:true,
43774         minSize: 175,
43775         titlebar: true
43776     },
43777     center: {
43778         title:'Components'
43779     }
43780 }, 'main-ct');
43781
43782 // Create and add multiple ContentPanels at once via configs
43783 layout.batchAdd({
43784    west: {
43785        id: 'source-files',
43786        autoCreate:true,
43787        title:'Ext Source Files',
43788        autoScroll:true,
43789        fitToFrame:true
43790    },
43791    center : {
43792        el: cview,
43793        autoScroll:true,
43794        fitToFrame:true,
43795        toolbar: tb,
43796        resizeEl:'cbody'
43797    }
43798 });
43799 </code></pre>
43800      * @param {Object} regions An object containing ContentPanel configs by region name
43801      */
43802     batchAdd : function(regions){
43803         this.beginUpdate();
43804         for(var rname in regions){
43805             var lr = this.regions[rname];
43806             if(lr){
43807                 this.addTypedPanels(lr, regions[rname]);
43808             }
43809         }
43810         this.endUpdate();
43811     },
43812
43813     // private
43814     addTypedPanels : function(lr, ps){
43815         if(typeof ps == 'string'){
43816             lr.add(new Roo.ContentPanel(ps));
43817         }
43818         else if(ps instanceof Array){
43819             for(var i =0, len = ps.length; i < len; i++){
43820                 this.addTypedPanels(lr, ps[i]);
43821             }
43822         }
43823         else if(!ps.events){ // raw config?
43824             var el = ps.el;
43825             delete ps.el; // prevent conflict
43826             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43827         }
43828         else {  // panel object assumed!
43829             lr.add(ps);
43830         }
43831     },
43832     /**
43833      * Adds a xtype elements to the layout.
43834      * <pre><code>
43835
43836 layout.addxtype({
43837        xtype : 'ContentPanel',
43838        region: 'west',
43839        items: [ .... ]
43840    }
43841 );
43842
43843 layout.addxtype({
43844         xtype : 'NestedLayoutPanel',
43845         region: 'west',
43846         layout: {
43847            center: { },
43848            west: { }   
43849         },
43850         items : [ ... list of content panels or nested layout panels.. ]
43851    }
43852 );
43853 </code></pre>
43854      * @param {Object} cfg Xtype definition of item to add.
43855      */
43856     addxtype : function(cfg)
43857     {
43858         // basically accepts a pannel...
43859         // can accept a layout region..!?!?
43860        // console.log('BorderLayout add ' + cfg.xtype)
43861         
43862         if (!cfg.xtype.match(/Panel$/)) {
43863             return false;
43864         }
43865         var ret = false;
43866         var region = cfg.region;
43867         delete cfg.region;
43868         
43869           
43870         var xitems = [];
43871         if (cfg.items) {
43872             xitems = cfg.items;
43873             delete cfg.items;
43874         }
43875         
43876         
43877         switch(cfg.xtype) 
43878         {
43879             case 'ContentPanel':  // ContentPanel (el, cfg)
43880             case 'ScrollPanel':  // ContentPanel (el, cfg)
43881                 if(cfg.autoCreate) {
43882                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43883                 } else {
43884                     var el = this.el.createChild();
43885                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43886                 }
43887                 
43888                 this.add(region, ret);
43889                 break;
43890             
43891             
43892             case 'TreePanel': // our new panel!
43893                 cfg.el = this.el.createChild();
43894                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43895                 this.add(region, ret);
43896                 break;
43897             
43898             case 'NestedLayoutPanel': 
43899                 // create a new Layout (which is  a Border Layout...
43900                 var el = this.el.createChild();
43901                 var clayout = cfg.layout;
43902                 delete cfg.layout;
43903                 clayout.items   = clayout.items  || [];
43904                 // replace this exitems with the clayout ones..
43905                 xitems = clayout.items;
43906                  
43907                 
43908                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43909                     cfg.background = false;
43910                 }
43911                 var layout = new Roo.BorderLayout(el, clayout);
43912                 
43913                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43914                 //console.log('adding nested layout panel '  + cfg.toSource());
43915                 this.add(region, ret);
43916                 
43917                 break;
43918                 
43919             case 'GridPanel': 
43920             
43921                 // needs grid and region
43922                 
43923                 //var el = this.getRegion(region).el.createChild();
43924                 var el = this.el.createChild();
43925                 // create the grid first...
43926                 
43927                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43928                 delete cfg.grid;
43929                 if (region == 'center' && this.active ) {
43930                     cfg.background = false;
43931                 }
43932                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43933                 
43934                 this.add(region, ret);
43935                 if (cfg.background) {
43936                     ret.on('activate', function(gp) {
43937                         if (!gp.grid.rendered) {
43938                             gp.grid.render();
43939                         }
43940                     });
43941                 } else {
43942                     grid.render();
43943                 }
43944                 break;
43945            
43946                
43947                 
43948                 
43949             default: 
43950                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43951                 return;
43952              // GridPanel (grid, cfg)
43953             
43954         }
43955         this.beginUpdate();
43956         // add children..
43957         Roo.each(xitems, function(i)  {
43958             ret.addxtype(i);
43959         });
43960         this.endUpdate();
43961         return ret;
43962         
43963     }
43964 });
43965
43966 /**
43967  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43968  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43969  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43970  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43971  * <pre><code>
43972 // shorthand
43973 var CP = Roo.ContentPanel;
43974
43975 var layout = Roo.BorderLayout.create({
43976     north: {
43977         initialSize: 25,
43978         titlebar: false,
43979         panels: [new CP("north", "North")]
43980     },
43981     west: {
43982         split:true,
43983         initialSize: 200,
43984         minSize: 175,
43985         maxSize: 400,
43986         titlebar: true,
43987         collapsible: true,
43988         panels: [new CP("west", {title: "West"})]
43989     },
43990     east: {
43991         split:true,
43992         initialSize: 202,
43993         minSize: 175,
43994         maxSize: 400,
43995         titlebar: true,
43996         collapsible: true,
43997         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43998     },
43999     south: {
44000         split:true,
44001         initialSize: 100,
44002         minSize: 100,
44003         maxSize: 200,
44004         titlebar: true,
44005         collapsible: true,
44006         panels: [new CP("south", {title: "South", closable: true})]
44007     },
44008     center: {
44009         titlebar: true,
44010         autoScroll:true,
44011         resizeTabs: true,
44012         minTabWidth: 50,
44013         preferredTabWidth: 150,
44014         panels: [
44015             new CP("center1", {title: "Close Me", closable: true}),
44016             new CP("center2", {title: "Center Panel", closable: false})
44017         ]
44018     }
44019 }, document.body);
44020
44021 layout.getRegion("center").showPanel("center1");
44022 </code></pre>
44023  * @param config
44024  * @param targetEl
44025  */
44026 Roo.BorderLayout.create = function(config, targetEl){
44027     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44028     layout.beginUpdate();
44029     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44030     for(var j = 0, jlen = regions.length; j < jlen; j++){
44031         var lr = regions[j];
44032         if(layout.regions[lr] && config[lr].panels){
44033             var r = layout.regions[lr];
44034             var ps = config[lr].panels;
44035             layout.addTypedPanels(r, ps);
44036         }
44037     }
44038     layout.endUpdate();
44039     return layout;
44040 };
44041
44042 // private
44043 Roo.BorderLayout.RegionFactory = {
44044     // private
44045     validRegions : ["north","south","east","west","center"],
44046
44047     // private
44048     create : function(target, mgr, config){
44049         target = target.toLowerCase();
44050         if(config.lightweight || config.basic){
44051             return new Roo.BasicLayoutRegion(mgr, config, target);
44052         }
44053         switch(target){
44054             case "north":
44055                 return new Roo.NorthLayoutRegion(mgr, config);
44056             case "south":
44057                 return new Roo.SouthLayoutRegion(mgr, config);
44058             case "east":
44059                 return new Roo.EastLayoutRegion(mgr, config);
44060             case "west":
44061                 return new Roo.WestLayoutRegion(mgr, config);
44062             case "center":
44063                 return new Roo.CenterLayoutRegion(mgr, config);
44064         }
44065         throw 'Layout region "'+target+'" not supported.';
44066     }
44067 };/*
44068  * Based on:
44069  * Ext JS Library 1.1.1
44070  * Copyright(c) 2006-2007, Ext JS, LLC.
44071  *
44072  * Originally Released Under LGPL - original licence link has changed is not relivant.
44073  *
44074  * Fork - LGPL
44075  * <script type="text/javascript">
44076  */
44077  
44078 /**
44079  * @class Roo.BasicLayoutRegion
44080  * @extends Roo.util.Observable
44081  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44082  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44083  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44084  */
44085 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44086     this.mgr = mgr;
44087     this.position  = pos;
44088     this.events = {
44089         /**
44090          * @scope Roo.BasicLayoutRegion
44091          */
44092         
44093         /**
44094          * @event beforeremove
44095          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44096          * @param {Roo.LayoutRegion} this
44097          * @param {Roo.ContentPanel} panel The panel
44098          * @param {Object} e The cancel event object
44099          */
44100         "beforeremove" : true,
44101         /**
44102          * @event invalidated
44103          * Fires when the layout for this region is changed.
44104          * @param {Roo.LayoutRegion} this
44105          */
44106         "invalidated" : true,
44107         /**
44108          * @event visibilitychange
44109          * Fires when this region is shown or hidden 
44110          * @param {Roo.LayoutRegion} this
44111          * @param {Boolean} visibility true or false
44112          */
44113         "visibilitychange" : true,
44114         /**
44115          * @event paneladded
44116          * Fires when a panel is added. 
44117          * @param {Roo.LayoutRegion} this
44118          * @param {Roo.ContentPanel} panel The panel
44119          */
44120         "paneladded" : true,
44121         /**
44122          * @event panelremoved
44123          * Fires when a panel is removed. 
44124          * @param {Roo.LayoutRegion} this
44125          * @param {Roo.ContentPanel} panel The panel
44126          */
44127         "panelremoved" : true,
44128         /**
44129          * @event collapsed
44130          * Fires when this region is collapsed.
44131          * @param {Roo.LayoutRegion} this
44132          */
44133         "collapsed" : true,
44134         /**
44135          * @event expanded
44136          * Fires when this region is expanded.
44137          * @param {Roo.LayoutRegion} this
44138          */
44139         "expanded" : true,
44140         /**
44141          * @event slideshow
44142          * Fires when this region is slid into view.
44143          * @param {Roo.LayoutRegion} this
44144          */
44145         "slideshow" : true,
44146         /**
44147          * @event slidehide
44148          * Fires when this region slides out of view. 
44149          * @param {Roo.LayoutRegion} this
44150          */
44151         "slidehide" : true,
44152         /**
44153          * @event panelactivated
44154          * Fires when a panel is activated. 
44155          * @param {Roo.LayoutRegion} this
44156          * @param {Roo.ContentPanel} panel The activated panel
44157          */
44158         "panelactivated" : true,
44159         /**
44160          * @event resized
44161          * Fires when the user resizes this region. 
44162          * @param {Roo.LayoutRegion} this
44163          * @param {Number} newSize The new size (width for east/west, height for north/south)
44164          */
44165         "resized" : true
44166     };
44167     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44168     this.panels = new Roo.util.MixedCollection();
44169     this.panels.getKey = this.getPanelId.createDelegate(this);
44170     this.box = null;
44171     this.activePanel = null;
44172     // ensure listeners are added...
44173     
44174     if (config.listeners || config.events) {
44175         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44176             listeners : config.listeners || {},
44177             events : config.events || {}
44178         });
44179     }
44180     
44181     if(skipConfig !== true){
44182         this.applyConfig(config);
44183     }
44184 };
44185
44186 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44187     getPanelId : function(p){
44188         return p.getId();
44189     },
44190     
44191     applyConfig : function(config){
44192         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44193         this.config = config;
44194         
44195     },
44196     
44197     /**
44198      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44199      * the width, for horizontal (north, south) the height.
44200      * @param {Number} newSize The new width or height
44201      */
44202     resizeTo : function(newSize){
44203         var el = this.el ? this.el :
44204                  (this.activePanel ? this.activePanel.getEl() : null);
44205         if(el){
44206             switch(this.position){
44207                 case "east":
44208                 case "west":
44209                     el.setWidth(newSize);
44210                     this.fireEvent("resized", this, newSize);
44211                 break;
44212                 case "north":
44213                 case "south":
44214                     el.setHeight(newSize);
44215                     this.fireEvent("resized", this, newSize);
44216                 break;                
44217             }
44218         }
44219     },
44220     
44221     getBox : function(){
44222         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44223     },
44224     
44225     getMargins : function(){
44226         return this.margins;
44227     },
44228     
44229     updateBox : function(box){
44230         this.box = box;
44231         var el = this.activePanel.getEl();
44232         el.dom.style.left = box.x + "px";
44233         el.dom.style.top = box.y + "px";
44234         this.activePanel.setSize(box.width, box.height);
44235     },
44236     
44237     /**
44238      * Returns the container element for this region.
44239      * @return {Roo.Element}
44240      */
44241     getEl : function(){
44242         return this.activePanel;
44243     },
44244     
44245     /**
44246      * Returns true if this region is currently visible.
44247      * @return {Boolean}
44248      */
44249     isVisible : function(){
44250         return this.activePanel ? true : false;
44251     },
44252     
44253     setActivePanel : function(panel){
44254         panel = this.getPanel(panel);
44255         if(this.activePanel && this.activePanel != panel){
44256             this.activePanel.setActiveState(false);
44257             this.activePanel.getEl().setLeftTop(-10000,-10000);
44258         }
44259         this.activePanel = panel;
44260         panel.setActiveState(true);
44261         if(this.box){
44262             panel.setSize(this.box.width, this.box.height);
44263         }
44264         this.fireEvent("panelactivated", this, panel);
44265         this.fireEvent("invalidated");
44266     },
44267     
44268     /**
44269      * Show the specified panel.
44270      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44271      * @return {Roo.ContentPanel} The shown panel or null
44272      */
44273     showPanel : function(panel){
44274         if(panel = this.getPanel(panel)){
44275             this.setActivePanel(panel);
44276         }
44277         return panel;
44278     },
44279     
44280     /**
44281      * Get the active panel for this region.
44282      * @return {Roo.ContentPanel} The active panel or null
44283      */
44284     getActivePanel : function(){
44285         return this.activePanel;
44286     },
44287     
44288     /**
44289      * Add the passed ContentPanel(s)
44290      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44291      * @return {Roo.ContentPanel} The panel added (if only one was added)
44292      */
44293     add : function(panel){
44294         if(arguments.length > 1){
44295             for(var i = 0, len = arguments.length; i < len; i++) {
44296                 this.add(arguments[i]);
44297             }
44298             return null;
44299         }
44300         if(this.hasPanel(panel)){
44301             this.showPanel(panel);
44302             return panel;
44303         }
44304         var el = panel.getEl();
44305         if(el.dom.parentNode != this.mgr.el.dom){
44306             this.mgr.el.dom.appendChild(el.dom);
44307         }
44308         if(panel.setRegion){
44309             panel.setRegion(this);
44310         }
44311         this.panels.add(panel);
44312         el.setStyle("position", "absolute");
44313         if(!panel.background){
44314             this.setActivePanel(panel);
44315             if(this.config.initialSize && this.panels.getCount()==1){
44316                 this.resizeTo(this.config.initialSize);
44317             }
44318         }
44319         this.fireEvent("paneladded", this, panel);
44320         return panel;
44321     },
44322     
44323     /**
44324      * Returns true if the panel is in this region.
44325      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44326      * @return {Boolean}
44327      */
44328     hasPanel : function(panel){
44329         if(typeof panel == "object"){ // must be panel obj
44330             panel = panel.getId();
44331         }
44332         return this.getPanel(panel) ? true : false;
44333     },
44334     
44335     /**
44336      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44337      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44338      * @param {Boolean} preservePanel Overrides the config preservePanel option
44339      * @return {Roo.ContentPanel} The panel that was removed
44340      */
44341     remove : function(panel, preservePanel){
44342         panel = this.getPanel(panel);
44343         if(!panel){
44344             return null;
44345         }
44346         var e = {};
44347         this.fireEvent("beforeremove", this, panel, e);
44348         if(e.cancel === true){
44349             return null;
44350         }
44351         var panelId = panel.getId();
44352         this.panels.removeKey(panelId);
44353         return panel;
44354     },
44355     
44356     /**
44357      * Returns the panel specified or null if it's not in this region.
44358      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44359      * @return {Roo.ContentPanel}
44360      */
44361     getPanel : function(id){
44362         if(typeof id == "object"){ // must be panel obj
44363             return id;
44364         }
44365         return this.panels.get(id);
44366     },
44367     
44368     /**
44369      * Returns this regions position (north/south/east/west/center).
44370      * @return {String} 
44371      */
44372     getPosition: function(){
44373         return this.position;    
44374     }
44375 });/*
44376  * Based on:
44377  * Ext JS Library 1.1.1
44378  * Copyright(c) 2006-2007, Ext JS, LLC.
44379  *
44380  * Originally Released Under LGPL - original licence link has changed is not relivant.
44381  *
44382  * Fork - LGPL
44383  * <script type="text/javascript">
44384  */
44385  
44386 /**
44387  * @class Roo.LayoutRegion
44388  * @extends Roo.BasicLayoutRegion
44389  * This class represents a region in a layout manager.
44390  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44391  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44392  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44393  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44394  * @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})
44395  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44396  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44397  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44398  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44399  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44400  * @cfg {String} title The title for the region (overrides panel titles)
44401  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44402  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44403  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44404  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44405  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44406  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44407  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44408  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44409  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44410  * @cfg {Boolean} showPin True to show a pin button
44411 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44412 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44413 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44414 * @cfg {Number} width  For East/West panels
44415 * @cfg {Number} height For North/South panels
44416 * @cfg {Boolean} split To show the splitter
44417  */
44418 Roo.LayoutRegion = function(mgr, config, pos){
44419     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44420     var dh = Roo.DomHelper;
44421     /** This region's container element 
44422     * @type Roo.Element */
44423     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44424     /** This region's title element 
44425     * @type Roo.Element */
44426
44427     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44428         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44429         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44430     ]}, true);
44431     this.titleEl.enableDisplayMode();
44432     /** This region's title text element 
44433     * @type HTMLElement */
44434     this.titleTextEl = this.titleEl.dom.firstChild;
44435     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44436     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44437     this.closeBtn.enableDisplayMode();
44438     this.closeBtn.on("click", this.closeClicked, this);
44439     this.closeBtn.hide();
44440
44441     this.createBody(config);
44442     this.visible = true;
44443     this.collapsed = false;
44444
44445     if(config.hideWhenEmpty){
44446         this.hide();
44447         this.on("paneladded", this.validateVisibility, this);
44448         this.on("panelremoved", this.validateVisibility, this);
44449     }
44450     this.applyConfig(config);
44451 };
44452
44453 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44454
44455     createBody : function(){
44456         /** This region's body element 
44457         * @type Roo.Element */
44458         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44459     },
44460
44461     applyConfig : function(c){
44462         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44463             var dh = Roo.DomHelper;
44464             if(c.titlebar !== false){
44465                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44466                 this.collapseBtn.on("click", this.collapse, this);
44467                 this.collapseBtn.enableDisplayMode();
44468
44469                 if(c.showPin === true || this.showPin){
44470                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44471                     this.stickBtn.enableDisplayMode();
44472                     this.stickBtn.on("click", this.expand, this);
44473                     this.stickBtn.hide();
44474                 }
44475             }
44476             /** This region's collapsed element
44477             * @type Roo.Element */
44478             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44479                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44480             ]}, true);
44481             if(c.floatable !== false){
44482                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44483                this.collapsedEl.on("click", this.collapseClick, this);
44484             }
44485
44486             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44487                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44488                    id: "message", unselectable: "on", style:{"float":"left"}});
44489                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44490              }
44491             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44492             this.expandBtn.on("click", this.expand, this);
44493         }
44494         if(this.collapseBtn){
44495             this.collapseBtn.setVisible(c.collapsible == true);
44496         }
44497         this.cmargins = c.cmargins || this.cmargins ||
44498                          (this.position == "west" || this.position == "east" ?
44499                              {top: 0, left: 2, right:2, bottom: 0} :
44500                              {top: 2, left: 0, right:0, bottom: 2});
44501         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44502         this.bottomTabs = c.tabPosition != "top";
44503         this.autoScroll = c.autoScroll || false;
44504         if(this.autoScroll){
44505             this.bodyEl.setStyle("overflow", "auto");
44506         }else{
44507             this.bodyEl.setStyle("overflow", "hidden");
44508         }
44509         //if(c.titlebar !== false){
44510             if((!c.titlebar && !c.title) || c.titlebar === false){
44511                 this.titleEl.hide();
44512             }else{
44513                 this.titleEl.show();
44514                 if(c.title){
44515                     this.titleTextEl.innerHTML = c.title;
44516                 }
44517             }
44518         //}
44519         this.duration = c.duration || .30;
44520         this.slideDuration = c.slideDuration || .45;
44521         this.config = c;
44522         if(c.collapsed){
44523             this.collapse(true);
44524         }
44525         if(c.hidden){
44526             this.hide();
44527         }
44528     },
44529     /**
44530      * Returns true if this region is currently visible.
44531      * @return {Boolean}
44532      */
44533     isVisible : function(){
44534         return this.visible;
44535     },
44536
44537     /**
44538      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44539      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44540      */
44541     setCollapsedTitle : function(title){
44542         title = title || "&#160;";
44543         if(this.collapsedTitleTextEl){
44544             this.collapsedTitleTextEl.innerHTML = title;
44545         }
44546     },
44547
44548     getBox : function(){
44549         var b;
44550         if(!this.collapsed){
44551             b = this.el.getBox(false, true);
44552         }else{
44553             b = this.collapsedEl.getBox(false, true);
44554         }
44555         return b;
44556     },
44557
44558     getMargins : function(){
44559         return this.collapsed ? this.cmargins : this.margins;
44560     },
44561
44562     highlight : function(){
44563         this.el.addClass("x-layout-panel-dragover");
44564     },
44565
44566     unhighlight : function(){
44567         this.el.removeClass("x-layout-panel-dragover");
44568     },
44569
44570     updateBox : function(box){
44571         this.box = box;
44572         if(!this.collapsed){
44573             this.el.dom.style.left = box.x + "px";
44574             this.el.dom.style.top = box.y + "px";
44575             this.updateBody(box.width, box.height);
44576         }else{
44577             this.collapsedEl.dom.style.left = box.x + "px";
44578             this.collapsedEl.dom.style.top = box.y + "px";
44579             this.collapsedEl.setSize(box.width, box.height);
44580         }
44581         if(this.tabs){
44582             this.tabs.autoSizeTabs();
44583         }
44584     },
44585
44586     updateBody : function(w, h){
44587         if(w !== null){
44588             this.el.setWidth(w);
44589             w -= this.el.getBorderWidth("rl");
44590             if(this.config.adjustments){
44591                 w += this.config.adjustments[0];
44592             }
44593         }
44594         if(h !== null){
44595             this.el.setHeight(h);
44596             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44597             h -= this.el.getBorderWidth("tb");
44598             if(this.config.adjustments){
44599                 h += this.config.adjustments[1];
44600             }
44601             this.bodyEl.setHeight(h);
44602             if(this.tabs){
44603                 h = this.tabs.syncHeight(h);
44604             }
44605         }
44606         if(this.panelSize){
44607             w = w !== null ? w : this.panelSize.width;
44608             h = h !== null ? h : this.panelSize.height;
44609         }
44610         if(this.activePanel){
44611             var el = this.activePanel.getEl();
44612             w = w !== null ? w : el.getWidth();
44613             h = h !== null ? h : el.getHeight();
44614             this.panelSize = {width: w, height: h};
44615             this.activePanel.setSize(w, h);
44616         }
44617         if(Roo.isIE && this.tabs){
44618             this.tabs.el.repaint();
44619         }
44620     },
44621
44622     /**
44623      * Returns the container element for this region.
44624      * @return {Roo.Element}
44625      */
44626     getEl : function(){
44627         return this.el;
44628     },
44629
44630     /**
44631      * Hides this region.
44632      */
44633     hide : function(){
44634         if(!this.collapsed){
44635             this.el.dom.style.left = "-2000px";
44636             this.el.hide();
44637         }else{
44638             this.collapsedEl.dom.style.left = "-2000px";
44639             this.collapsedEl.hide();
44640         }
44641         this.visible = false;
44642         this.fireEvent("visibilitychange", this, false);
44643     },
44644
44645     /**
44646      * Shows this region if it was previously hidden.
44647      */
44648     show : function(){
44649         if(!this.collapsed){
44650             this.el.show();
44651         }else{
44652             this.collapsedEl.show();
44653         }
44654         this.visible = true;
44655         this.fireEvent("visibilitychange", this, true);
44656     },
44657
44658     closeClicked : function(){
44659         if(this.activePanel){
44660             this.remove(this.activePanel);
44661         }
44662     },
44663
44664     collapseClick : function(e){
44665         if(this.isSlid){
44666            e.stopPropagation();
44667            this.slideIn();
44668         }else{
44669            e.stopPropagation();
44670            this.slideOut();
44671         }
44672     },
44673
44674     /**
44675      * Collapses this region.
44676      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44677      */
44678     collapse : function(skipAnim){
44679         if(this.collapsed) return;
44680         this.collapsed = true;
44681         if(this.split){
44682             this.split.el.hide();
44683         }
44684         if(this.config.animate && skipAnim !== true){
44685             this.fireEvent("invalidated", this);
44686             this.animateCollapse();
44687         }else{
44688             this.el.setLocation(-20000,-20000);
44689             this.el.hide();
44690             this.collapsedEl.show();
44691             this.fireEvent("collapsed", this);
44692             this.fireEvent("invalidated", this);
44693         }
44694     },
44695
44696     animateCollapse : function(){
44697         // overridden
44698     },
44699
44700     /**
44701      * Expands this region if it was previously collapsed.
44702      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44703      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44704      */
44705     expand : function(e, skipAnim){
44706         if(e) e.stopPropagation();
44707         if(!this.collapsed || this.el.hasActiveFx()) return;
44708         if(this.isSlid){
44709             this.afterSlideIn();
44710             skipAnim = true;
44711         }
44712         this.collapsed = false;
44713         if(this.config.animate && skipAnim !== true){
44714             this.animateExpand();
44715         }else{
44716             this.el.show();
44717             if(this.split){
44718                 this.split.el.show();
44719             }
44720             this.collapsedEl.setLocation(-2000,-2000);
44721             this.collapsedEl.hide();
44722             this.fireEvent("invalidated", this);
44723             this.fireEvent("expanded", this);
44724         }
44725     },
44726
44727     animateExpand : function(){
44728         // overridden
44729     },
44730
44731     initTabs : function(){
44732         this.bodyEl.setStyle("overflow", "hidden");
44733         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44734             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44735             disableTooltips: this.config.disableTabTips
44736         });
44737         if(this.config.hideTabs){
44738             ts.stripWrap.setDisplayed(false);
44739         }
44740         this.tabs = ts;
44741         ts.resizeTabs = this.config.resizeTabs === true;
44742         ts.minTabWidth = this.config.minTabWidth || 40;
44743         ts.maxTabWidth = this.config.maxTabWidth || 250;
44744         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44745         ts.monitorResize = false;
44746         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44747         ts.bodyEl.addClass('x-layout-tabs-body');
44748         this.panels.each(this.initPanelAsTab, this);
44749     },
44750
44751     initPanelAsTab : function(panel){
44752         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44753                     this.config.closeOnTab && panel.isClosable());
44754         if(panel.tabTip !== undefined){
44755             ti.setTooltip(panel.tabTip);
44756         }
44757         ti.on("activate", function(){
44758               this.setActivePanel(panel);
44759         }, this);
44760         if(this.config.closeOnTab){
44761             ti.on("beforeclose", function(t, e){
44762                 e.cancel = true;
44763                 this.remove(panel);
44764             }, this);
44765         }
44766         return ti;
44767     },
44768
44769     updatePanelTitle : function(panel, title){
44770         if(this.activePanel == panel){
44771             this.updateTitle(title);
44772         }
44773         if(this.tabs){
44774             var ti = this.tabs.getTab(panel.getEl().id);
44775             ti.setText(title);
44776             if(panel.tabTip !== undefined){
44777                 ti.setTooltip(panel.tabTip);
44778             }
44779         }
44780     },
44781
44782     updateTitle : function(title){
44783         if(this.titleTextEl && !this.config.title){
44784             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44785         }
44786     },
44787
44788     setActivePanel : function(panel){
44789         panel = this.getPanel(panel);
44790         if(this.activePanel && this.activePanel != panel){
44791             this.activePanel.setActiveState(false);
44792         }
44793         this.activePanel = panel;
44794         panel.setActiveState(true);
44795         if(this.panelSize){
44796             panel.setSize(this.panelSize.width, this.panelSize.height);
44797         }
44798         if(this.closeBtn){
44799             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44800         }
44801         this.updateTitle(panel.getTitle());
44802         if(this.tabs){
44803             this.fireEvent("invalidated", this);
44804         }
44805         this.fireEvent("panelactivated", this, panel);
44806     },
44807
44808     /**
44809      * Shows the specified panel.
44810      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44811      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44812      */
44813     showPanel : function(panel){
44814         if(panel = this.getPanel(panel)){
44815             if(this.tabs){
44816                 var tab = this.tabs.getTab(panel.getEl().id);
44817                 if(tab.isHidden()){
44818                     this.tabs.unhideTab(tab.id);
44819                 }
44820                 tab.activate();
44821             }else{
44822                 this.setActivePanel(panel);
44823             }
44824         }
44825         return panel;
44826     },
44827
44828     /**
44829      * Get the active panel for this region.
44830      * @return {Roo.ContentPanel} The active panel or null
44831      */
44832     getActivePanel : function(){
44833         return this.activePanel;
44834     },
44835
44836     validateVisibility : function(){
44837         if(this.panels.getCount() < 1){
44838             this.updateTitle("&#160;");
44839             this.closeBtn.hide();
44840             this.hide();
44841         }else{
44842             if(!this.isVisible()){
44843                 this.show();
44844             }
44845         }
44846     },
44847
44848     /**
44849      * Adds the passed ContentPanel(s) to this region.
44850      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44851      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44852      */
44853     add : function(panel){
44854         if(arguments.length > 1){
44855             for(var i = 0, len = arguments.length; i < len; i++) {
44856                 this.add(arguments[i]);
44857             }
44858             return null;
44859         }
44860         if(this.hasPanel(panel)){
44861             this.showPanel(panel);
44862             return panel;
44863         }
44864         panel.setRegion(this);
44865         this.panels.add(panel);
44866         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44867             this.bodyEl.dom.appendChild(panel.getEl().dom);
44868             if(panel.background !== true){
44869                 this.setActivePanel(panel);
44870             }
44871             this.fireEvent("paneladded", this, panel);
44872             return panel;
44873         }
44874         if(!this.tabs){
44875             this.initTabs();
44876         }else{
44877             this.initPanelAsTab(panel);
44878         }
44879         if(panel.background !== true){
44880             this.tabs.activate(panel.getEl().id);
44881         }
44882         this.fireEvent("paneladded", this, panel);
44883         return panel;
44884     },
44885
44886     /**
44887      * Hides the tab for the specified panel.
44888      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44889      */
44890     hidePanel : function(panel){
44891         if(this.tabs && (panel = this.getPanel(panel))){
44892             this.tabs.hideTab(panel.getEl().id);
44893         }
44894     },
44895
44896     /**
44897      * Unhides the tab for a previously hidden panel.
44898      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44899      */
44900     unhidePanel : function(panel){
44901         if(this.tabs && (panel = this.getPanel(panel))){
44902             this.tabs.unhideTab(panel.getEl().id);
44903         }
44904     },
44905
44906     clearPanels : function(){
44907         while(this.panels.getCount() > 0){
44908              this.remove(this.panels.first());
44909         }
44910     },
44911
44912     /**
44913      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44914      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44915      * @param {Boolean} preservePanel Overrides the config preservePanel option
44916      * @return {Roo.ContentPanel} The panel that was removed
44917      */
44918     remove : function(panel, preservePanel){
44919         panel = this.getPanel(panel);
44920         if(!panel){
44921             return null;
44922         }
44923         var e = {};
44924         this.fireEvent("beforeremove", this, panel, e);
44925         if(e.cancel === true){
44926             return null;
44927         }
44928         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44929         var panelId = panel.getId();
44930         this.panels.removeKey(panelId);
44931         if(preservePanel){
44932             document.body.appendChild(panel.getEl().dom);
44933         }
44934         if(this.tabs){
44935             this.tabs.removeTab(panel.getEl().id);
44936         }else if (!preservePanel){
44937             this.bodyEl.dom.removeChild(panel.getEl().dom);
44938         }
44939         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44940             var p = this.panels.first();
44941             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44942             tempEl.appendChild(p.getEl().dom);
44943             this.bodyEl.update("");
44944             this.bodyEl.dom.appendChild(p.getEl().dom);
44945             tempEl = null;
44946             this.updateTitle(p.getTitle());
44947             this.tabs = null;
44948             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44949             this.setActivePanel(p);
44950         }
44951         panel.setRegion(null);
44952         if(this.activePanel == panel){
44953             this.activePanel = null;
44954         }
44955         if(this.config.autoDestroy !== false && preservePanel !== true){
44956             try{panel.destroy();}catch(e){}
44957         }
44958         this.fireEvent("panelremoved", this, panel);
44959         return panel;
44960     },
44961
44962     /**
44963      * Returns the TabPanel component used by this region
44964      * @return {Roo.TabPanel}
44965      */
44966     getTabs : function(){
44967         return this.tabs;
44968     },
44969
44970     createTool : function(parentEl, className){
44971         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44972             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44973         btn.addClassOnOver("x-layout-tools-button-over");
44974         return btn;
44975     }
44976 });/*
44977  * Based on:
44978  * Ext JS Library 1.1.1
44979  * Copyright(c) 2006-2007, Ext JS, LLC.
44980  *
44981  * Originally Released Under LGPL - original licence link has changed is not relivant.
44982  *
44983  * Fork - LGPL
44984  * <script type="text/javascript">
44985  */
44986  
44987
44988
44989 /**
44990  * @class Roo.SplitLayoutRegion
44991  * @extends Roo.LayoutRegion
44992  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44993  */
44994 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44995     this.cursor = cursor;
44996     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44997 };
44998
44999 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45000     splitTip : "Drag to resize.",
45001     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45002     useSplitTips : false,
45003
45004     applyConfig : function(config){
45005         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45006         if(config.split){
45007             if(!this.split){
45008                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45009                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45010                 /** The SplitBar for this region 
45011                 * @type Roo.SplitBar */
45012                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45013                 this.split.on("moved", this.onSplitMove, this);
45014                 this.split.useShim = config.useShim === true;
45015                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45016                 if(this.useSplitTips){
45017                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45018                 }
45019                 if(config.collapsible){
45020                     this.split.el.on("dblclick", this.collapse,  this);
45021                 }
45022             }
45023             if(typeof config.minSize != "undefined"){
45024                 this.split.minSize = config.minSize;
45025             }
45026             if(typeof config.maxSize != "undefined"){
45027                 this.split.maxSize = config.maxSize;
45028             }
45029             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45030                 this.hideSplitter();
45031             }
45032         }
45033     },
45034
45035     getHMaxSize : function(){
45036          var cmax = this.config.maxSize || 10000;
45037          var center = this.mgr.getRegion("center");
45038          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45039     },
45040
45041     getVMaxSize : function(){
45042          var cmax = this.config.maxSize || 10000;
45043          var center = this.mgr.getRegion("center");
45044          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45045     },
45046
45047     onSplitMove : function(split, newSize){
45048         this.fireEvent("resized", this, newSize);
45049     },
45050     
45051     /** 
45052      * Returns the {@link Roo.SplitBar} for this region.
45053      * @return {Roo.SplitBar}
45054      */
45055     getSplitBar : function(){
45056         return this.split;
45057     },
45058     
45059     hide : function(){
45060         this.hideSplitter();
45061         Roo.SplitLayoutRegion.superclass.hide.call(this);
45062     },
45063
45064     hideSplitter : function(){
45065         if(this.split){
45066             this.split.el.setLocation(-2000,-2000);
45067             this.split.el.hide();
45068         }
45069     },
45070
45071     show : function(){
45072         if(this.split){
45073             this.split.el.show();
45074         }
45075         Roo.SplitLayoutRegion.superclass.show.call(this);
45076     },
45077     
45078     beforeSlide: function(){
45079         if(Roo.isGecko){// firefox overflow auto bug workaround
45080             this.bodyEl.clip();
45081             if(this.tabs) this.tabs.bodyEl.clip();
45082             if(this.activePanel){
45083                 this.activePanel.getEl().clip();
45084                 
45085                 if(this.activePanel.beforeSlide){
45086                     this.activePanel.beforeSlide();
45087                 }
45088             }
45089         }
45090     },
45091     
45092     afterSlide : function(){
45093         if(Roo.isGecko){// firefox overflow auto bug workaround
45094             this.bodyEl.unclip();
45095             if(this.tabs) this.tabs.bodyEl.unclip();
45096             if(this.activePanel){
45097                 this.activePanel.getEl().unclip();
45098                 if(this.activePanel.afterSlide){
45099                     this.activePanel.afterSlide();
45100                 }
45101             }
45102         }
45103     },
45104
45105     initAutoHide : function(){
45106         if(this.autoHide !== false){
45107             if(!this.autoHideHd){
45108                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45109                 this.autoHideHd = {
45110                     "mouseout": function(e){
45111                         if(!e.within(this.el, true)){
45112                             st.delay(500);
45113                         }
45114                     },
45115                     "mouseover" : function(e){
45116                         st.cancel();
45117                     },
45118                     scope : this
45119                 };
45120             }
45121             this.el.on(this.autoHideHd);
45122         }
45123     },
45124
45125     clearAutoHide : function(){
45126         if(this.autoHide !== false){
45127             this.el.un("mouseout", this.autoHideHd.mouseout);
45128             this.el.un("mouseover", this.autoHideHd.mouseover);
45129         }
45130     },
45131
45132     clearMonitor : function(){
45133         Roo.get(document).un("click", this.slideInIf, this);
45134     },
45135
45136     // these names are backwards but not changed for compat
45137     slideOut : function(){
45138         if(this.isSlid || this.el.hasActiveFx()){
45139             return;
45140         }
45141         this.isSlid = true;
45142         if(this.collapseBtn){
45143             this.collapseBtn.hide();
45144         }
45145         this.closeBtnState = this.closeBtn.getStyle('display');
45146         this.closeBtn.hide();
45147         if(this.stickBtn){
45148             this.stickBtn.show();
45149         }
45150         this.el.show();
45151         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45152         this.beforeSlide();
45153         this.el.setStyle("z-index", 10001);
45154         this.el.slideIn(this.getSlideAnchor(), {
45155             callback: function(){
45156                 this.afterSlide();
45157                 this.initAutoHide();
45158                 Roo.get(document).on("click", this.slideInIf, this);
45159                 this.fireEvent("slideshow", this);
45160             },
45161             scope: this,
45162             block: true
45163         });
45164     },
45165
45166     afterSlideIn : function(){
45167         this.clearAutoHide();
45168         this.isSlid = false;
45169         this.clearMonitor();
45170         this.el.setStyle("z-index", "");
45171         if(this.collapseBtn){
45172             this.collapseBtn.show();
45173         }
45174         this.closeBtn.setStyle('display', this.closeBtnState);
45175         if(this.stickBtn){
45176             this.stickBtn.hide();
45177         }
45178         this.fireEvent("slidehide", this);
45179     },
45180
45181     slideIn : function(cb){
45182         if(!this.isSlid || this.el.hasActiveFx()){
45183             Roo.callback(cb);
45184             return;
45185         }
45186         this.isSlid = false;
45187         this.beforeSlide();
45188         this.el.slideOut(this.getSlideAnchor(), {
45189             callback: function(){
45190                 this.el.setLeftTop(-10000, -10000);
45191                 this.afterSlide();
45192                 this.afterSlideIn();
45193                 Roo.callback(cb);
45194             },
45195             scope: this,
45196             block: true
45197         });
45198     },
45199     
45200     slideInIf : function(e){
45201         if(!e.within(this.el)){
45202             this.slideIn();
45203         }
45204     },
45205
45206     animateCollapse : function(){
45207         this.beforeSlide();
45208         this.el.setStyle("z-index", 20000);
45209         var anchor = this.getSlideAnchor();
45210         this.el.slideOut(anchor, {
45211             callback : function(){
45212                 this.el.setStyle("z-index", "");
45213                 this.collapsedEl.slideIn(anchor, {duration:.3});
45214                 this.afterSlide();
45215                 this.el.setLocation(-10000,-10000);
45216                 this.el.hide();
45217                 this.fireEvent("collapsed", this);
45218             },
45219             scope: this,
45220             block: true
45221         });
45222     },
45223
45224     animateExpand : function(){
45225         this.beforeSlide();
45226         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45227         this.el.setStyle("z-index", 20000);
45228         this.collapsedEl.hide({
45229             duration:.1
45230         });
45231         this.el.slideIn(this.getSlideAnchor(), {
45232             callback : function(){
45233                 this.el.setStyle("z-index", "");
45234                 this.afterSlide();
45235                 if(this.split){
45236                     this.split.el.show();
45237                 }
45238                 this.fireEvent("invalidated", this);
45239                 this.fireEvent("expanded", this);
45240             },
45241             scope: this,
45242             block: true
45243         });
45244     },
45245
45246     anchors : {
45247         "west" : "left",
45248         "east" : "right",
45249         "north" : "top",
45250         "south" : "bottom"
45251     },
45252
45253     sanchors : {
45254         "west" : "l",
45255         "east" : "r",
45256         "north" : "t",
45257         "south" : "b"
45258     },
45259
45260     canchors : {
45261         "west" : "tl-tr",
45262         "east" : "tr-tl",
45263         "north" : "tl-bl",
45264         "south" : "bl-tl"
45265     },
45266
45267     getAnchor : function(){
45268         return this.anchors[this.position];
45269     },
45270
45271     getCollapseAnchor : function(){
45272         return this.canchors[this.position];
45273     },
45274
45275     getSlideAnchor : function(){
45276         return this.sanchors[this.position];
45277     },
45278
45279     getAlignAdj : function(){
45280         var cm = this.cmargins;
45281         switch(this.position){
45282             case "west":
45283                 return [0, 0];
45284             break;
45285             case "east":
45286                 return [0, 0];
45287             break;
45288             case "north":
45289                 return [0, 0];
45290             break;
45291             case "south":
45292                 return [0, 0];
45293             break;
45294         }
45295     },
45296
45297     getExpandAdj : function(){
45298         var c = this.collapsedEl, cm = this.cmargins;
45299         switch(this.position){
45300             case "west":
45301                 return [-(cm.right+c.getWidth()+cm.left), 0];
45302             break;
45303             case "east":
45304                 return [cm.right+c.getWidth()+cm.left, 0];
45305             break;
45306             case "north":
45307                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45308             break;
45309             case "south":
45310                 return [0, cm.top+cm.bottom+c.getHeight()];
45311             break;
45312         }
45313     }
45314 });/*
45315  * Based on:
45316  * Ext JS Library 1.1.1
45317  * Copyright(c) 2006-2007, Ext JS, LLC.
45318  *
45319  * Originally Released Under LGPL - original licence link has changed is not relivant.
45320  *
45321  * Fork - LGPL
45322  * <script type="text/javascript">
45323  */
45324 /*
45325  * These classes are private internal classes
45326  */
45327 Roo.CenterLayoutRegion = function(mgr, config){
45328     Roo.LayoutRegion.call(this, mgr, config, "center");
45329     this.visible = true;
45330     this.minWidth = config.minWidth || 20;
45331     this.minHeight = config.minHeight || 20;
45332 };
45333
45334 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45335     hide : function(){
45336         // center panel can't be hidden
45337     },
45338     
45339     show : function(){
45340         // center panel can't be hidden
45341     },
45342     
45343     getMinWidth: function(){
45344         return this.minWidth;
45345     },
45346     
45347     getMinHeight: function(){
45348         return this.minHeight;
45349     }
45350 });
45351
45352
45353 Roo.NorthLayoutRegion = function(mgr, config){
45354     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45355     if(this.split){
45356         this.split.placement = Roo.SplitBar.TOP;
45357         this.split.orientation = Roo.SplitBar.VERTICAL;
45358         this.split.el.addClass("x-layout-split-v");
45359     }
45360     var size = config.initialSize || config.height;
45361     if(typeof size != "undefined"){
45362         this.el.setHeight(size);
45363     }
45364 };
45365 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45366     orientation: Roo.SplitBar.VERTICAL,
45367     getBox : function(){
45368         if(this.collapsed){
45369             return this.collapsedEl.getBox();
45370         }
45371         var box = this.el.getBox();
45372         if(this.split){
45373             box.height += this.split.el.getHeight();
45374         }
45375         return box;
45376     },
45377     
45378     updateBox : function(box){
45379         if(this.split && !this.collapsed){
45380             box.height -= this.split.el.getHeight();
45381             this.split.el.setLeft(box.x);
45382             this.split.el.setTop(box.y+box.height);
45383             this.split.el.setWidth(box.width);
45384         }
45385         if(this.collapsed){
45386             this.updateBody(box.width, null);
45387         }
45388         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45389     }
45390 });
45391
45392 Roo.SouthLayoutRegion = function(mgr, config){
45393     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45394     if(this.split){
45395         this.split.placement = Roo.SplitBar.BOTTOM;
45396         this.split.orientation = Roo.SplitBar.VERTICAL;
45397         this.split.el.addClass("x-layout-split-v");
45398     }
45399     var size = config.initialSize || config.height;
45400     if(typeof size != "undefined"){
45401         this.el.setHeight(size);
45402     }
45403 };
45404 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45405     orientation: Roo.SplitBar.VERTICAL,
45406     getBox : function(){
45407         if(this.collapsed){
45408             return this.collapsedEl.getBox();
45409         }
45410         var box = this.el.getBox();
45411         if(this.split){
45412             var sh = this.split.el.getHeight();
45413             box.height += sh;
45414             box.y -= sh;
45415         }
45416         return box;
45417     },
45418     
45419     updateBox : function(box){
45420         if(this.split && !this.collapsed){
45421             var sh = this.split.el.getHeight();
45422             box.height -= sh;
45423             box.y += sh;
45424             this.split.el.setLeft(box.x);
45425             this.split.el.setTop(box.y-sh);
45426             this.split.el.setWidth(box.width);
45427         }
45428         if(this.collapsed){
45429             this.updateBody(box.width, null);
45430         }
45431         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45432     }
45433 });
45434
45435 Roo.EastLayoutRegion = function(mgr, config){
45436     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45437     if(this.split){
45438         this.split.placement = Roo.SplitBar.RIGHT;
45439         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45440         this.split.el.addClass("x-layout-split-h");
45441     }
45442     var size = config.initialSize || config.width;
45443     if(typeof size != "undefined"){
45444         this.el.setWidth(size);
45445     }
45446 };
45447 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45448     orientation: Roo.SplitBar.HORIZONTAL,
45449     getBox : function(){
45450         if(this.collapsed){
45451             return this.collapsedEl.getBox();
45452         }
45453         var box = this.el.getBox();
45454         if(this.split){
45455             var sw = this.split.el.getWidth();
45456             box.width += sw;
45457             box.x -= sw;
45458         }
45459         return box;
45460     },
45461
45462     updateBox : function(box){
45463         if(this.split && !this.collapsed){
45464             var sw = this.split.el.getWidth();
45465             box.width -= sw;
45466             this.split.el.setLeft(box.x);
45467             this.split.el.setTop(box.y);
45468             this.split.el.setHeight(box.height);
45469             box.x += sw;
45470         }
45471         if(this.collapsed){
45472             this.updateBody(null, box.height);
45473         }
45474         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45475     }
45476 });
45477
45478 Roo.WestLayoutRegion = function(mgr, config){
45479     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45480     if(this.split){
45481         this.split.placement = Roo.SplitBar.LEFT;
45482         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45483         this.split.el.addClass("x-layout-split-h");
45484     }
45485     var size = config.initialSize || config.width;
45486     if(typeof size != "undefined"){
45487         this.el.setWidth(size);
45488     }
45489 };
45490 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45491     orientation: Roo.SplitBar.HORIZONTAL,
45492     getBox : function(){
45493         if(this.collapsed){
45494             return this.collapsedEl.getBox();
45495         }
45496         var box = this.el.getBox();
45497         if(this.split){
45498             box.width += this.split.el.getWidth();
45499         }
45500         return box;
45501     },
45502     
45503     updateBox : function(box){
45504         if(this.split && !this.collapsed){
45505             var sw = this.split.el.getWidth();
45506             box.width -= sw;
45507             this.split.el.setLeft(box.x+box.width);
45508             this.split.el.setTop(box.y);
45509             this.split.el.setHeight(box.height);
45510         }
45511         if(this.collapsed){
45512             this.updateBody(null, box.height);
45513         }
45514         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45515     }
45516 });
45517 /*
45518  * Based on:
45519  * Ext JS Library 1.1.1
45520  * Copyright(c) 2006-2007, Ext JS, LLC.
45521  *
45522  * Originally Released Under LGPL - original licence link has changed is not relivant.
45523  *
45524  * Fork - LGPL
45525  * <script type="text/javascript">
45526  */
45527  
45528  
45529 /*
45530  * Private internal class for reading and applying state
45531  */
45532 Roo.LayoutStateManager = function(layout){
45533      // default empty state
45534      this.state = {
45535         north: {},
45536         south: {},
45537         east: {},
45538         west: {}       
45539     };
45540 };
45541
45542 Roo.LayoutStateManager.prototype = {
45543     init : function(layout, provider){
45544         this.provider = provider;
45545         var state = provider.get(layout.id+"-layout-state");
45546         if(state){
45547             var wasUpdating = layout.isUpdating();
45548             if(!wasUpdating){
45549                 layout.beginUpdate();
45550             }
45551             for(var key in state){
45552                 if(typeof state[key] != "function"){
45553                     var rstate = state[key];
45554                     var r = layout.getRegion(key);
45555                     if(r && rstate){
45556                         if(rstate.size){
45557                             r.resizeTo(rstate.size);
45558                         }
45559                         if(rstate.collapsed == true){
45560                             r.collapse(true);
45561                         }else{
45562                             r.expand(null, true);
45563                         }
45564                     }
45565                 }
45566             }
45567             if(!wasUpdating){
45568                 layout.endUpdate();
45569             }
45570             this.state = state; 
45571         }
45572         this.layout = layout;
45573         layout.on("regionresized", this.onRegionResized, this);
45574         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45575         layout.on("regionexpanded", this.onRegionExpanded, this);
45576     },
45577     
45578     storeState : function(){
45579         this.provider.set(this.layout.id+"-layout-state", this.state);
45580     },
45581     
45582     onRegionResized : function(region, newSize){
45583         this.state[region.getPosition()].size = newSize;
45584         this.storeState();
45585     },
45586     
45587     onRegionCollapsed : function(region){
45588         this.state[region.getPosition()].collapsed = true;
45589         this.storeState();
45590     },
45591     
45592     onRegionExpanded : function(region){
45593         this.state[region.getPosition()].collapsed = false;
45594         this.storeState();
45595     }
45596 };/*
45597  * Based on:
45598  * Ext JS Library 1.1.1
45599  * Copyright(c) 2006-2007, Ext JS, LLC.
45600  *
45601  * Originally Released Under LGPL - original licence link has changed is not relivant.
45602  *
45603  * Fork - LGPL
45604  * <script type="text/javascript">
45605  */
45606 /**
45607  * @class Roo.ContentPanel
45608  * @extends Roo.util.Observable
45609  * A basic ContentPanel element.
45610  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45611  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45612  * @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
45613  * @cfg {Boolean} closable True if the panel can be closed/removed
45614  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45615  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45616  * @cfg {Toolbar} toolbar A toolbar for this panel
45617  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45618  * @cfg {String} title The title for this panel
45619  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45620  * @cfg {String} url Calls {@link #setUrl} with this value
45621  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45622  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45623  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45624  * @constructor
45625  * Create a new ContentPanel.
45626  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45627  * @param {String/Object} config A string to set only the title or a config object
45628  * @param {String} content (optional) Set the HTML content for this panel
45629  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45630  */
45631 Roo.ContentPanel = function(el, config, content){
45632     
45633      
45634     /*
45635     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45636         config = el;
45637         el = Roo.id();
45638     }
45639     if (config && config.parentLayout) { 
45640         el = config.parentLayout.el.createChild(); 
45641     }
45642     */
45643     if(el.autoCreate){ // xtype is available if this is called from factory
45644         config = el;
45645         el = Roo.id();
45646     }
45647     this.el = Roo.get(el);
45648     if(!this.el && config && config.autoCreate){
45649         if(typeof config.autoCreate == "object"){
45650             if(!config.autoCreate.id){
45651                 config.autoCreate.id = config.id||el;
45652             }
45653             this.el = Roo.DomHelper.append(document.body,
45654                         config.autoCreate, true);
45655         }else{
45656             this.el = Roo.DomHelper.append(document.body,
45657                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45658         }
45659     }
45660     this.closable = false;
45661     this.loaded = false;
45662     this.active = false;
45663     if(typeof config == "string"){
45664         this.title = config;
45665     }else{
45666         Roo.apply(this, config);
45667     }
45668     
45669     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45670         this.wrapEl = this.el.wrap();    
45671         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45672         
45673     }
45674     
45675     
45676     
45677     if(this.resizeEl){
45678         this.resizeEl = Roo.get(this.resizeEl, true);
45679     }else{
45680         this.resizeEl = this.el;
45681     }
45682     this.addEvents({
45683         /**
45684          * @event activate
45685          * Fires when this panel is activated. 
45686          * @param {Roo.ContentPanel} this
45687          */
45688         "activate" : true,
45689         /**
45690          * @event deactivate
45691          * Fires when this panel is activated. 
45692          * @param {Roo.ContentPanel} this
45693          */
45694         "deactivate" : true,
45695
45696         /**
45697          * @event resize
45698          * Fires when this panel is resized if fitToFrame is true.
45699          * @param {Roo.ContentPanel} this
45700          * @param {Number} width The width after any component adjustments
45701          * @param {Number} height The height after any component adjustments
45702          */
45703         "resize" : true
45704     });
45705     if(this.autoScroll){
45706         this.resizeEl.setStyle("overflow", "auto");
45707     } else {
45708         // fix randome scrolling
45709         this.el.on('scroll', function() {
45710             Roo.log('fix random scolling');
45711             this.scrollTo('top',0); 
45712         });
45713     }
45714     content = content || this.content;
45715     if(content){
45716         this.setContent(content);
45717     }
45718     if(config && config.url){
45719         this.setUrl(this.url, this.params, this.loadOnce);
45720     }
45721     
45722     
45723     
45724     Roo.ContentPanel.superclass.constructor.call(this);
45725 };
45726
45727 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45728     tabTip:'',
45729     setRegion : function(region){
45730         this.region = region;
45731         if(region){
45732            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45733         }else{
45734            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45735         } 
45736     },
45737     
45738     /**
45739      * Returns the toolbar for this Panel if one was configured. 
45740      * @return {Roo.Toolbar} 
45741      */
45742     getToolbar : function(){
45743         return this.toolbar;
45744     },
45745     
45746     setActiveState : function(active){
45747         this.active = active;
45748         if(!active){
45749             this.fireEvent("deactivate", this);
45750         }else{
45751             this.fireEvent("activate", this);
45752         }
45753     },
45754     /**
45755      * Updates this panel's element
45756      * @param {String} content The new content
45757      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45758     */
45759     setContent : function(content, loadScripts){
45760         this.el.update(content, loadScripts);
45761     },
45762
45763     ignoreResize : function(w, h){
45764         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45765             return true;
45766         }else{
45767             this.lastSize = {width: w, height: h};
45768             return false;
45769         }
45770     },
45771     /**
45772      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45773      * @return {Roo.UpdateManager} The UpdateManager
45774      */
45775     getUpdateManager : function(){
45776         return this.el.getUpdateManager();
45777     },
45778      /**
45779      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45780      * @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:
45781 <pre><code>
45782 panel.load({
45783     url: "your-url.php",
45784     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45785     callback: yourFunction,
45786     scope: yourObject, //(optional scope)
45787     discardUrl: false,
45788     nocache: false,
45789     text: "Loading...",
45790     timeout: 30,
45791     scripts: false
45792 });
45793 </code></pre>
45794      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45795      * 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.
45796      * @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}
45797      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45798      * @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.
45799      * @return {Roo.ContentPanel} this
45800      */
45801     load : function(){
45802         var um = this.el.getUpdateManager();
45803         um.update.apply(um, arguments);
45804         return this;
45805     },
45806
45807
45808     /**
45809      * 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.
45810      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45811      * @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)
45812      * @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)
45813      * @return {Roo.UpdateManager} The UpdateManager
45814      */
45815     setUrl : function(url, params, loadOnce){
45816         if(this.refreshDelegate){
45817             this.removeListener("activate", this.refreshDelegate);
45818         }
45819         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45820         this.on("activate", this.refreshDelegate);
45821         return this.el.getUpdateManager();
45822     },
45823     
45824     _handleRefresh : function(url, params, loadOnce){
45825         if(!loadOnce || !this.loaded){
45826             var updater = this.el.getUpdateManager();
45827             updater.update(url, params, this._setLoaded.createDelegate(this));
45828         }
45829     },
45830     
45831     _setLoaded : function(){
45832         this.loaded = true;
45833     }, 
45834     
45835     /**
45836      * Returns this panel's id
45837      * @return {String} 
45838      */
45839     getId : function(){
45840         return this.el.id;
45841     },
45842     
45843     /** 
45844      * Returns this panel's element - used by regiosn to add.
45845      * @return {Roo.Element} 
45846      */
45847     getEl : function(){
45848         return this.wrapEl || this.el;
45849     },
45850     
45851     adjustForComponents : function(width, height){
45852         if(this.resizeEl != this.el){
45853             width -= this.el.getFrameWidth('lr');
45854             height -= this.el.getFrameWidth('tb');
45855         }
45856         if(this.toolbar){
45857             var te = this.toolbar.getEl();
45858             height -= te.getHeight();
45859             te.setWidth(width);
45860         }
45861         if(this.adjustments){
45862             width += this.adjustments[0];
45863             height += this.adjustments[1];
45864         }
45865         return {"width": width, "height": height};
45866     },
45867     
45868     setSize : function(width, height){
45869         if(this.fitToFrame && !this.ignoreResize(width, height)){
45870             if(this.fitContainer && this.resizeEl != this.el){
45871                 this.el.setSize(width, height);
45872             }
45873             var size = this.adjustForComponents(width, height);
45874             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45875             this.fireEvent('resize', this, size.width, size.height);
45876         }
45877     },
45878     
45879     /**
45880      * Returns this panel's title
45881      * @return {String} 
45882      */
45883     getTitle : function(){
45884         return this.title;
45885     },
45886     
45887     /**
45888      * Set this panel's title
45889      * @param {String} title
45890      */
45891     setTitle : function(title){
45892         this.title = title;
45893         if(this.region){
45894             this.region.updatePanelTitle(this, title);
45895         }
45896     },
45897     
45898     /**
45899      * Returns true is this panel was configured to be closable
45900      * @return {Boolean} 
45901      */
45902     isClosable : function(){
45903         return this.closable;
45904     },
45905     
45906     beforeSlide : function(){
45907         this.el.clip();
45908         this.resizeEl.clip();
45909     },
45910     
45911     afterSlide : function(){
45912         this.el.unclip();
45913         this.resizeEl.unclip();
45914     },
45915     
45916     /**
45917      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45918      *   Will fail silently if the {@link #setUrl} method has not been called.
45919      *   This does not activate the panel, just updates its content.
45920      */
45921     refresh : function(){
45922         if(this.refreshDelegate){
45923            this.loaded = false;
45924            this.refreshDelegate();
45925         }
45926     },
45927     
45928     /**
45929      * Destroys this panel
45930      */
45931     destroy : function(){
45932         this.el.removeAllListeners();
45933         var tempEl = document.createElement("span");
45934         tempEl.appendChild(this.el.dom);
45935         tempEl.innerHTML = "";
45936         this.el.remove();
45937         this.el = null;
45938     },
45939     
45940       /**
45941      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45942      * <pre><code>
45943
45944 layout.addxtype({
45945        xtype : 'Form',
45946        items: [ .... ]
45947    }
45948 );
45949
45950 </code></pre>
45951      * @param {Object} cfg Xtype definition of item to add.
45952      */
45953     
45954     addxtype : function(cfg) {
45955         // add form..
45956         if (cfg.xtype.match(/^Form$/)) {
45957             var el = this.el.createChild();
45958
45959             this.form = new  Roo.form.Form(cfg);
45960             
45961             
45962             if ( this.form.allItems.length) this.form.render(el.dom);
45963             return this.form;
45964         }
45965         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45966             // views..
45967             cfg.el = this.el.appendChild(document.createElement("div"));
45968             // factory?
45969             var ret = new Roo[cfg.xtype](cfg);
45970             ret.render(false, ''); // render blank..
45971             return ret;
45972             
45973         }
45974         return false;
45975         
45976     }
45977 });
45978
45979 /**
45980  * @class Roo.GridPanel
45981  * @extends Roo.ContentPanel
45982  * @constructor
45983  * Create a new GridPanel.
45984  * @param {Roo.grid.Grid} grid The grid for this panel
45985  * @param {String/Object} config A string to set only the panel's title, or a config object
45986  */
45987 Roo.GridPanel = function(grid, config){
45988     
45989   
45990     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45991         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45992         
45993     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45994     
45995     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45996     
45997     if(this.toolbar){
45998         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45999     }
46000     // xtype created footer. - not sure if will work as we normally have to render first..
46001     if (this.footer && !this.footer.el && this.footer.xtype) {
46002         
46003         this.footer.container = this.grid.getView().getFooterPanel(true);
46004         this.footer.dataSource = this.grid.dataSource;
46005         this.footer = Roo.factory(this.footer, Roo);
46006         
46007     }
46008     
46009     grid.monitorWindowResize = false; // turn off autosizing
46010     grid.autoHeight = false;
46011     grid.autoWidth = false;
46012     this.grid = grid;
46013     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46014 };
46015
46016 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46017     getId : function(){
46018         return this.grid.id;
46019     },
46020     
46021     /**
46022      * Returns the grid for this panel
46023      * @return {Roo.grid.Grid} 
46024      */
46025     getGrid : function(){
46026         return this.grid;    
46027     },
46028     
46029     setSize : function(width, height){
46030         if(!this.ignoreResize(width, height)){
46031             var grid = this.grid;
46032             var size = this.adjustForComponents(width, height);
46033             grid.getGridEl().setSize(size.width, size.height);
46034             grid.autoSize();
46035         }
46036     },
46037     
46038     beforeSlide : function(){
46039         this.grid.getView().scroller.clip();
46040     },
46041     
46042     afterSlide : function(){
46043         this.grid.getView().scroller.unclip();
46044     },
46045     
46046     destroy : function(){
46047         this.grid.destroy();
46048         delete this.grid;
46049         Roo.GridPanel.superclass.destroy.call(this); 
46050     }
46051 });
46052
46053
46054 /**
46055  * @class Roo.NestedLayoutPanel
46056  * @extends Roo.ContentPanel
46057  * @constructor
46058  * Create a new NestedLayoutPanel.
46059  * 
46060  * 
46061  * @param {Roo.BorderLayout} layout The layout for this panel
46062  * @param {String/Object} config A string to set only the title or a config object
46063  */
46064 Roo.NestedLayoutPanel = function(layout, config)
46065 {
46066     // construct with only one argument..
46067     /* FIXME - implement nicer consturctors
46068     if (layout.layout) {
46069         config = layout;
46070         layout = config.layout;
46071         delete config.layout;
46072     }
46073     if (layout.xtype && !layout.getEl) {
46074         // then layout needs constructing..
46075         layout = Roo.factory(layout, Roo);
46076     }
46077     */
46078     
46079     
46080     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46081     
46082     layout.monitorWindowResize = false; // turn off autosizing
46083     this.layout = layout;
46084     this.layout.getEl().addClass("x-layout-nested-layout");
46085     
46086     
46087     
46088     
46089 };
46090
46091 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46092
46093     setSize : function(width, height){
46094         if(!this.ignoreResize(width, height)){
46095             var size = this.adjustForComponents(width, height);
46096             var el = this.layout.getEl();
46097             el.setSize(size.width, size.height);
46098             var touch = el.dom.offsetWidth;
46099             this.layout.layout();
46100             // ie requires a double layout on the first pass
46101             if(Roo.isIE && !this.initialized){
46102                 this.initialized = true;
46103                 this.layout.layout();
46104             }
46105         }
46106     },
46107     
46108     // activate all subpanels if not currently active..
46109     
46110     setActiveState : function(active){
46111         this.active = active;
46112         if(!active){
46113             this.fireEvent("deactivate", this);
46114             return;
46115         }
46116         
46117         this.fireEvent("activate", this);
46118         // not sure if this should happen before or after..
46119         if (!this.layout) {
46120             return; // should not happen..
46121         }
46122         var reg = false;
46123         for (var r in this.layout.regions) {
46124             reg = this.layout.getRegion(r);
46125             if (reg.getActivePanel()) {
46126                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46127                 reg.setActivePanel(reg.getActivePanel());
46128                 continue;
46129             }
46130             if (!reg.panels.length) {
46131                 continue;
46132             }
46133             reg.showPanel(reg.getPanel(0));
46134         }
46135         
46136         
46137         
46138         
46139     },
46140     
46141     /**
46142      * Returns the nested BorderLayout for this panel
46143      * @return {Roo.BorderLayout} 
46144      */
46145     getLayout : function(){
46146         return this.layout;
46147     },
46148     
46149      /**
46150      * Adds a xtype elements to the layout of the nested panel
46151      * <pre><code>
46152
46153 panel.addxtype({
46154        xtype : 'ContentPanel',
46155        region: 'west',
46156        items: [ .... ]
46157    }
46158 );
46159
46160 panel.addxtype({
46161         xtype : 'NestedLayoutPanel',
46162         region: 'west',
46163         layout: {
46164            center: { },
46165            west: { }   
46166         },
46167         items : [ ... list of content panels or nested layout panels.. ]
46168    }
46169 );
46170 </code></pre>
46171      * @param {Object} cfg Xtype definition of item to add.
46172      */
46173     addxtype : function(cfg) {
46174         return this.layout.addxtype(cfg);
46175     
46176     }
46177 });
46178
46179 Roo.ScrollPanel = function(el, config, content){
46180     config = config || {};
46181     config.fitToFrame = true;
46182     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46183     
46184     this.el.dom.style.overflow = "hidden";
46185     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46186     this.el.removeClass("x-layout-inactive-content");
46187     this.el.on("mousewheel", this.onWheel, this);
46188
46189     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46190     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46191     up.unselectable(); down.unselectable();
46192     up.on("click", this.scrollUp, this);
46193     down.on("click", this.scrollDown, this);
46194     up.addClassOnOver("x-scroller-btn-over");
46195     down.addClassOnOver("x-scroller-btn-over");
46196     up.addClassOnClick("x-scroller-btn-click");
46197     down.addClassOnClick("x-scroller-btn-click");
46198     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46199
46200     this.resizeEl = this.el;
46201     this.el = wrap; this.up = up; this.down = down;
46202 };
46203
46204 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46205     increment : 100,
46206     wheelIncrement : 5,
46207     scrollUp : function(){
46208         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46209     },
46210
46211     scrollDown : function(){
46212         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46213     },
46214
46215     afterScroll : function(){
46216         var el = this.resizeEl;
46217         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46218         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46219         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46220     },
46221
46222     setSize : function(){
46223         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46224         this.afterScroll();
46225     },
46226
46227     onWheel : function(e){
46228         var d = e.getWheelDelta();
46229         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46230         this.afterScroll();
46231         e.stopEvent();
46232     },
46233
46234     setContent : function(content, loadScripts){
46235         this.resizeEl.update(content, loadScripts);
46236     }
46237
46238 });
46239
46240
46241
46242
46243
46244
46245
46246
46247
46248 /**
46249  * @class Roo.TreePanel
46250  * @extends Roo.ContentPanel
46251  * @constructor
46252  * Create a new TreePanel. - defaults to fit/scoll contents.
46253  * @param {String/Object} config A string to set only the panel's title, or a config object
46254  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46255  */
46256 Roo.TreePanel = function(config){
46257     var el = config.el;
46258     var tree = config.tree;
46259     delete config.tree; 
46260     delete config.el; // hopefull!
46261     
46262     // wrapper for IE7 strict & safari scroll issue
46263     
46264     var treeEl = el.createChild();
46265     config.resizeEl = treeEl;
46266     
46267     
46268     
46269     Roo.TreePanel.superclass.constructor.call(this, el, config);
46270  
46271  
46272     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46273     //console.log(tree);
46274     this.on('activate', function()
46275     {
46276         if (this.tree.rendered) {
46277             return;
46278         }
46279         //console.log('render tree');
46280         this.tree.render();
46281     });
46282     
46283     this.on('resize',  function (cp, w, h) {
46284             this.tree.innerCt.setWidth(w);
46285             this.tree.innerCt.setHeight(h);
46286             this.tree.innerCt.setStyle('overflow-y', 'auto');
46287     });
46288
46289         
46290     
46291 };
46292
46293 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46294     fitToFrame : true,
46295     autoScroll : true
46296 });
46297
46298
46299
46300
46301
46302
46303
46304
46305
46306
46307
46308 /*
46309  * Based on:
46310  * Ext JS Library 1.1.1
46311  * Copyright(c) 2006-2007, Ext JS, LLC.
46312  *
46313  * Originally Released Under LGPL - original licence link has changed is not relivant.
46314  *
46315  * Fork - LGPL
46316  * <script type="text/javascript">
46317  */
46318  
46319
46320 /**
46321  * @class Roo.ReaderLayout
46322  * @extends Roo.BorderLayout
46323  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46324  * center region containing two nested regions (a top one for a list view and one for item preview below),
46325  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46326  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46327  * expedites the setup of the overall layout and regions for this common application style.
46328  * Example:
46329  <pre><code>
46330 var reader = new Roo.ReaderLayout();
46331 var CP = Roo.ContentPanel;  // shortcut for adding
46332
46333 reader.beginUpdate();
46334 reader.add("north", new CP("north", "North"));
46335 reader.add("west", new CP("west", {title: "West"}));
46336 reader.add("east", new CP("east", {title: "East"}));
46337
46338 reader.regions.listView.add(new CP("listView", "List"));
46339 reader.regions.preview.add(new CP("preview", "Preview"));
46340 reader.endUpdate();
46341 </code></pre>
46342 * @constructor
46343 * Create a new ReaderLayout
46344 * @param {Object} config Configuration options
46345 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46346 * document.body if omitted)
46347 */
46348 Roo.ReaderLayout = function(config, renderTo){
46349     var c = config || {size:{}};
46350     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46351         north: c.north !== false ? Roo.apply({
46352             split:false,
46353             initialSize: 32,
46354             titlebar: false
46355         }, c.north) : false,
46356         west: c.west !== false ? Roo.apply({
46357             split:true,
46358             initialSize: 200,
46359             minSize: 175,
46360             maxSize: 400,
46361             titlebar: true,
46362             collapsible: true,
46363             animate: true,
46364             margins:{left:5,right:0,bottom:5,top:5},
46365             cmargins:{left:5,right:5,bottom:5,top:5}
46366         }, c.west) : false,
46367         east: c.east !== false ? Roo.apply({
46368             split:true,
46369             initialSize: 200,
46370             minSize: 175,
46371             maxSize: 400,
46372             titlebar: true,
46373             collapsible: true,
46374             animate: true,
46375             margins:{left:0,right:5,bottom:5,top:5},
46376             cmargins:{left:5,right:5,bottom:5,top:5}
46377         }, c.east) : false,
46378         center: Roo.apply({
46379             tabPosition: 'top',
46380             autoScroll:false,
46381             closeOnTab: true,
46382             titlebar:false,
46383             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46384         }, c.center)
46385     });
46386
46387     this.el.addClass('x-reader');
46388
46389     this.beginUpdate();
46390
46391     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46392         south: c.preview !== false ? Roo.apply({
46393             split:true,
46394             initialSize: 200,
46395             minSize: 100,
46396             autoScroll:true,
46397             collapsible:true,
46398             titlebar: true,
46399             cmargins:{top:5,left:0, right:0, bottom:0}
46400         }, c.preview) : false,
46401         center: Roo.apply({
46402             autoScroll:false,
46403             titlebar:false,
46404             minHeight:200
46405         }, c.listView)
46406     });
46407     this.add('center', new Roo.NestedLayoutPanel(inner,
46408             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46409
46410     this.endUpdate();
46411
46412     this.regions.preview = inner.getRegion('south');
46413     this.regions.listView = inner.getRegion('center');
46414 };
46415
46416 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46417  * Based on:
46418  * Ext JS Library 1.1.1
46419  * Copyright(c) 2006-2007, Ext JS, LLC.
46420  *
46421  * Originally Released Under LGPL - original licence link has changed is not relivant.
46422  *
46423  * Fork - LGPL
46424  * <script type="text/javascript">
46425  */
46426  
46427 /**
46428  * @class Roo.grid.Grid
46429  * @extends Roo.util.Observable
46430  * This class represents the primary interface of a component based grid control.
46431  * <br><br>Usage:<pre><code>
46432  var grid = new Roo.grid.Grid("my-container-id", {
46433      ds: myDataStore,
46434      cm: myColModel,
46435      selModel: mySelectionModel,
46436      autoSizeColumns: true,
46437      monitorWindowResize: false,
46438      trackMouseOver: true
46439  });
46440  // set any options
46441  grid.render();
46442  * </code></pre>
46443  * <b>Common Problems:</b><br/>
46444  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46445  * element will correct this<br/>
46446  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46447  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46448  * are unpredictable.<br/>
46449  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46450  * grid to calculate dimensions/offsets.<br/>
46451   * @constructor
46452  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46453  * The container MUST have some type of size defined for the grid to fill. The container will be
46454  * automatically set to position relative if it isn't already.
46455  * @param {Object} config A config object that sets properties on this grid.
46456  */
46457 Roo.grid.Grid = function(container, config){
46458         // initialize the container
46459         this.container = Roo.get(container);
46460         this.container.update("");
46461         this.container.setStyle("overflow", "hidden");
46462     this.container.addClass('x-grid-container');
46463
46464     this.id = this.container.id;
46465
46466     Roo.apply(this, config);
46467     // check and correct shorthanded configs
46468     if(this.ds){
46469         this.dataSource = this.ds;
46470         delete this.ds;
46471     }
46472     if(this.cm){
46473         this.colModel = this.cm;
46474         delete this.cm;
46475     }
46476     if(this.sm){
46477         this.selModel = this.sm;
46478         delete this.sm;
46479     }
46480
46481     if (this.selModel) {
46482         this.selModel = Roo.factory(this.selModel, Roo.grid);
46483         this.sm = this.selModel;
46484         this.sm.xmodule = this.xmodule || false;
46485     }
46486     if (typeof(this.colModel.config) == 'undefined') {
46487         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46488         this.cm = this.colModel;
46489         this.cm.xmodule = this.xmodule || false;
46490     }
46491     if (this.dataSource) {
46492         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46493         this.ds = this.dataSource;
46494         this.ds.xmodule = this.xmodule || false;
46495          
46496     }
46497     
46498     
46499     
46500     if(this.width){
46501         this.container.setWidth(this.width);
46502     }
46503
46504     if(this.height){
46505         this.container.setHeight(this.height);
46506     }
46507     /** @private */
46508         this.addEvents({
46509         // raw events
46510         /**
46511          * @event click
46512          * The raw click event for the entire grid.
46513          * @param {Roo.EventObject} e
46514          */
46515         "click" : true,
46516         /**
46517          * @event dblclick
46518          * The raw dblclick event for the entire grid.
46519          * @param {Roo.EventObject} e
46520          */
46521         "dblclick" : true,
46522         /**
46523          * @event contextmenu
46524          * The raw contextmenu event for the entire grid.
46525          * @param {Roo.EventObject} e
46526          */
46527         "contextmenu" : true,
46528         /**
46529          * @event mousedown
46530          * The raw mousedown event for the entire grid.
46531          * @param {Roo.EventObject} e
46532          */
46533         "mousedown" : true,
46534         /**
46535          * @event mouseup
46536          * The raw mouseup event for the entire grid.
46537          * @param {Roo.EventObject} e
46538          */
46539         "mouseup" : true,
46540         /**
46541          * @event mouseover
46542          * The raw mouseover event for the entire grid.
46543          * @param {Roo.EventObject} e
46544          */
46545         "mouseover" : true,
46546         /**
46547          * @event mouseout
46548          * The raw mouseout event for the entire grid.
46549          * @param {Roo.EventObject} e
46550          */
46551         "mouseout" : true,
46552         /**
46553          * @event keypress
46554          * The raw keypress event for the entire grid.
46555          * @param {Roo.EventObject} e
46556          */
46557         "keypress" : true,
46558         /**
46559          * @event keydown
46560          * The raw keydown event for the entire grid.
46561          * @param {Roo.EventObject} e
46562          */
46563         "keydown" : true,
46564
46565         // custom events
46566
46567         /**
46568          * @event cellclick
46569          * Fires when a cell is clicked
46570          * @param {Grid} this
46571          * @param {Number} rowIndex
46572          * @param {Number} columnIndex
46573          * @param {Roo.EventObject} e
46574          */
46575         "cellclick" : true,
46576         /**
46577          * @event celldblclick
46578          * Fires when a cell is double clicked
46579          * @param {Grid} this
46580          * @param {Number} rowIndex
46581          * @param {Number} columnIndex
46582          * @param {Roo.EventObject} e
46583          */
46584         "celldblclick" : true,
46585         /**
46586          * @event rowclick
46587          * Fires when a row is clicked
46588          * @param {Grid} this
46589          * @param {Number} rowIndex
46590          * @param {Roo.EventObject} e
46591          */
46592         "rowclick" : true,
46593         /**
46594          * @event rowdblclick
46595          * Fires when a row is double clicked
46596          * @param {Grid} this
46597          * @param {Number} rowIndex
46598          * @param {Roo.EventObject} e
46599          */
46600         "rowdblclick" : true,
46601         /**
46602          * @event headerclick
46603          * Fires when a header is clicked
46604          * @param {Grid} this
46605          * @param {Number} columnIndex
46606          * @param {Roo.EventObject} e
46607          */
46608         "headerclick" : true,
46609         /**
46610          * @event headerdblclick
46611          * Fires when a header cell is double clicked
46612          * @param {Grid} this
46613          * @param {Number} columnIndex
46614          * @param {Roo.EventObject} e
46615          */
46616         "headerdblclick" : true,
46617         /**
46618          * @event rowcontextmenu
46619          * Fires when a row is right clicked
46620          * @param {Grid} this
46621          * @param {Number} rowIndex
46622          * @param {Roo.EventObject} e
46623          */
46624         "rowcontextmenu" : true,
46625         /**
46626          * @event cellcontextmenu
46627          * Fires when a cell is right clicked
46628          * @param {Grid} this
46629          * @param {Number} rowIndex
46630          * @param {Number} cellIndex
46631          * @param {Roo.EventObject} e
46632          */
46633          "cellcontextmenu" : true,
46634         /**
46635          * @event headercontextmenu
46636          * Fires when a header is right clicked
46637          * @param {Grid} this
46638          * @param {Number} columnIndex
46639          * @param {Roo.EventObject} e
46640          */
46641         "headercontextmenu" : true,
46642         /**
46643          * @event bodyscroll
46644          * Fires when the body element is scrolled
46645          * @param {Number} scrollLeft
46646          * @param {Number} scrollTop
46647          */
46648         "bodyscroll" : true,
46649         /**
46650          * @event columnresize
46651          * Fires when the user resizes a column
46652          * @param {Number} columnIndex
46653          * @param {Number} newSize
46654          */
46655         "columnresize" : true,
46656         /**
46657          * @event columnmove
46658          * Fires when the user moves a column
46659          * @param {Number} oldIndex
46660          * @param {Number} newIndex
46661          */
46662         "columnmove" : true,
46663         /**
46664          * @event startdrag
46665          * Fires when row(s) start being dragged
46666          * @param {Grid} this
46667          * @param {Roo.GridDD} dd The drag drop object
46668          * @param {event} e The raw browser event
46669          */
46670         "startdrag" : true,
46671         /**
46672          * @event enddrag
46673          * Fires when a drag operation is complete
46674          * @param {Grid} this
46675          * @param {Roo.GridDD} dd The drag drop object
46676          * @param {event} e The raw browser event
46677          */
46678         "enddrag" : true,
46679         /**
46680          * @event dragdrop
46681          * Fires when dragged row(s) are dropped on a valid DD target
46682          * @param {Grid} this
46683          * @param {Roo.GridDD} dd The drag drop object
46684          * @param {String} targetId The target drag drop object
46685          * @param {event} e The raw browser event
46686          */
46687         "dragdrop" : true,
46688         /**
46689          * @event dragover
46690          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46691          * @param {Grid} this
46692          * @param {Roo.GridDD} dd The drag drop object
46693          * @param {String} targetId The target drag drop object
46694          * @param {event} e The raw browser event
46695          */
46696         "dragover" : true,
46697         /**
46698          * @event dragenter
46699          *  Fires when the dragged row(s) first cross another DD target while being dragged
46700          * @param {Grid} this
46701          * @param {Roo.GridDD} dd The drag drop object
46702          * @param {String} targetId The target drag drop object
46703          * @param {event} e The raw browser event
46704          */
46705         "dragenter" : true,
46706         /**
46707          * @event dragout
46708          * Fires when the dragged row(s) leave another DD target while being dragged
46709          * @param {Grid} this
46710          * @param {Roo.GridDD} dd The drag drop object
46711          * @param {String} targetId The target drag drop object
46712          * @param {event} e The raw browser event
46713          */
46714         "dragout" : true,
46715         /**
46716          * @event rowclass
46717          * Fires when a row is rendered, so you can change add a style to it.
46718          * @param {GridView} gridview   The grid view
46719          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46720          */
46721         'rowclass' : true,
46722
46723         /**
46724          * @event render
46725          * Fires when the grid is rendered
46726          * @param {Grid} grid
46727          */
46728         'render' : true
46729     });
46730
46731     Roo.grid.Grid.superclass.constructor.call(this);
46732 };
46733 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46734     
46735     /**
46736      * @cfg {String} ddGroup - drag drop group.
46737      */
46738
46739     /**
46740      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46741      */
46742     minColumnWidth : 25,
46743
46744     /**
46745      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46746      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46747      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46748      */
46749     autoSizeColumns : false,
46750
46751     /**
46752      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46753      */
46754     autoSizeHeaders : true,
46755
46756     /**
46757      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46758      */
46759     monitorWindowResize : true,
46760
46761     /**
46762      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46763      * rows measured to get a columns size. Default is 0 (all rows).
46764      */
46765     maxRowsToMeasure : 0,
46766
46767     /**
46768      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46769      */
46770     trackMouseOver : true,
46771
46772     /**
46773     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46774     */
46775     
46776     /**
46777     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46778     */
46779     enableDragDrop : false,
46780     
46781     /**
46782     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46783     */
46784     enableColumnMove : true,
46785     
46786     /**
46787     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46788     */
46789     enableColumnHide : true,
46790     
46791     /**
46792     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46793     */
46794     enableRowHeightSync : false,
46795     
46796     /**
46797     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46798     */
46799     stripeRows : true,
46800     
46801     /**
46802     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46803     */
46804     autoHeight : false,
46805
46806     /**
46807      * @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.
46808      */
46809     autoExpandColumn : false,
46810
46811     /**
46812     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46813     * Default is 50.
46814     */
46815     autoExpandMin : 50,
46816
46817     /**
46818     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46819     */
46820     autoExpandMax : 1000,
46821
46822     /**
46823     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46824     */
46825     view : null,
46826
46827     /**
46828     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46829     */
46830     loadMask : false,
46831     /**
46832     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46833     */
46834     dropTarget: false,
46835     
46836    
46837     
46838     // private
46839     rendered : false,
46840
46841     /**
46842     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46843     * of a fixed width. Default is false.
46844     */
46845     /**
46846     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46847     */
46848     /**
46849      * Called once after all setup has been completed and the grid is ready to be rendered.
46850      * @return {Roo.grid.Grid} this
46851      */
46852     render : function()
46853     {
46854         var c = this.container;
46855         // try to detect autoHeight/width mode
46856         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46857             this.autoHeight = true;
46858         }
46859         var view = this.getView();
46860         view.init(this);
46861
46862         c.on("click", this.onClick, this);
46863         c.on("dblclick", this.onDblClick, this);
46864         c.on("contextmenu", this.onContextMenu, this);
46865         c.on("keydown", this.onKeyDown, this);
46866
46867         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46868
46869         this.getSelectionModel().init(this);
46870
46871         view.render();
46872
46873         if(this.loadMask){
46874             this.loadMask = new Roo.LoadMask(this.container,
46875                     Roo.apply({store:this.dataSource}, this.loadMask));
46876         }
46877         
46878         
46879         if (this.toolbar && this.toolbar.xtype) {
46880             this.toolbar.container = this.getView().getHeaderPanel(true);
46881             this.toolbar = new Roo.Toolbar(this.toolbar);
46882         }
46883         if (this.footer && this.footer.xtype) {
46884             this.footer.dataSource = this.getDataSource();
46885             this.footer.container = this.getView().getFooterPanel(true);
46886             this.footer = Roo.factory(this.footer, Roo);
46887         }
46888         if (this.dropTarget && this.dropTarget.xtype) {
46889             delete this.dropTarget.xtype;
46890             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46891         }
46892         
46893         
46894         this.rendered = true;
46895         this.fireEvent('render', this);
46896         return this;
46897     },
46898
46899         /**
46900          * Reconfigures the grid to use a different Store and Column Model.
46901          * The View will be bound to the new objects and refreshed.
46902          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46903          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46904          */
46905     reconfigure : function(dataSource, colModel){
46906         if(this.loadMask){
46907             this.loadMask.destroy();
46908             this.loadMask = new Roo.LoadMask(this.container,
46909                     Roo.apply({store:dataSource}, this.loadMask));
46910         }
46911         this.view.bind(dataSource, colModel);
46912         this.dataSource = dataSource;
46913         this.colModel = colModel;
46914         this.view.refresh(true);
46915     },
46916
46917     // private
46918     onKeyDown : function(e){
46919         this.fireEvent("keydown", e);
46920     },
46921
46922     /**
46923      * Destroy this grid.
46924      * @param {Boolean} removeEl True to remove the element
46925      */
46926     destroy : function(removeEl, keepListeners){
46927         if(this.loadMask){
46928             this.loadMask.destroy();
46929         }
46930         var c = this.container;
46931         c.removeAllListeners();
46932         this.view.destroy();
46933         this.colModel.purgeListeners();
46934         if(!keepListeners){
46935             this.purgeListeners();
46936         }
46937         c.update("");
46938         if(removeEl === true){
46939             c.remove();
46940         }
46941     },
46942
46943     // private
46944     processEvent : function(name, e){
46945         this.fireEvent(name, e);
46946         var t = e.getTarget();
46947         var v = this.view;
46948         var header = v.findHeaderIndex(t);
46949         if(header !== false){
46950             this.fireEvent("header" + name, this, header, e);
46951         }else{
46952             var row = v.findRowIndex(t);
46953             var cell = v.findCellIndex(t);
46954             if(row !== false){
46955                 this.fireEvent("row" + name, this, row, e);
46956                 if(cell !== false){
46957                     this.fireEvent("cell" + name, this, row, cell, e);
46958                 }
46959             }
46960         }
46961     },
46962
46963     // private
46964     onClick : function(e){
46965         this.processEvent("click", e);
46966     },
46967
46968     // private
46969     onContextMenu : function(e, t){
46970         this.processEvent("contextmenu", e);
46971     },
46972
46973     // private
46974     onDblClick : function(e){
46975         this.processEvent("dblclick", e);
46976     },
46977
46978     // private
46979     walkCells : function(row, col, step, fn, scope){
46980         var cm = this.colModel, clen = cm.getColumnCount();
46981         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46982         if(step < 0){
46983             if(col < 0){
46984                 row--;
46985                 first = false;
46986             }
46987             while(row >= 0){
46988                 if(!first){
46989                     col = clen-1;
46990                 }
46991                 first = false;
46992                 while(col >= 0){
46993                     if(fn.call(scope || this, row, col, cm) === true){
46994                         return [row, col];
46995                     }
46996                     col--;
46997                 }
46998                 row--;
46999             }
47000         } else {
47001             if(col >= clen){
47002                 row++;
47003                 first = false;
47004             }
47005             while(row < rlen){
47006                 if(!first){
47007                     col = 0;
47008                 }
47009                 first = false;
47010                 while(col < clen){
47011                     if(fn.call(scope || this, row, col, cm) === true){
47012                         return [row, col];
47013                     }
47014                     col++;
47015                 }
47016                 row++;
47017             }
47018         }
47019         return null;
47020     },
47021
47022     // private
47023     getSelections : function(){
47024         return this.selModel.getSelections();
47025     },
47026
47027     /**
47028      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47029      * but if manual update is required this method will initiate it.
47030      */
47031     autoSize : function(){
47032         if(this.rendered){
47033             this.view.layout();
47034             if(this.view.adjustForScroll){
47035                 this.view.adjustForScroll();
47036             }
47037         }
47038     },
47039
47040     /**
47041      * Returns the grid's underlying element.
47042      * @return {Element} The element
47043      */
47044     getGridEl : function(){
47045         return this.container;
47046     },
47047
47048     // private for compatibility, overridden by editor grid
47049     stopEditing : function(){},
47050
47051     /**
47052      * Returns the grid's SelectionModel.
47053      * @return {SelectionModel}
47054      */
47055     getSelectionModel : function(){
47056         if(!this.selModel){
47057             this.selModel = new Roo.grid.RowSelectionModel();
47058         }
47059         return this.selModel;
47060     },
47061
47062     /**
47063      * Returns the grid's DataSource.
47064      * @return {DataSource}
47065      */
47066     getDataSource : function(){
47067         return this.dataSource;
47068     },
47069
47070     /**
47071      * Returns the grid's ColumnModel.
47072      * @return {ColumnModel}
47073      */
47074     getColumnModel : function(){
47075         return this.colModel;
47076     },
47077
47078     /**
47079      * Returns the grid's GridView object.
47080      * @return {GridView}
47081      */
47082     getView : function(){
47083         if(!this.view){
47084             this.view = new Roo.grid.GridView(this.viewConfig);
47085         }
47086         return this.view;
47087     },
47088     /**
47089      * Called to get grid's drag proxy text, by default returns this.ddText.
47090      * @return {String}
47091      */
47092     getDragDropText : function(){
47093         var count = this.selModel.getCount();
47094         return String.format(this.ddText, count, count == 1 ? '' : 's');
47095     }
47096 });
47097 /**
47098  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47099  * %0 is replaced with the number of selected rows.
47100  * @type String
47101  */
47102 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47103  * Based on:
47104  * Ext JS Library 1.1.1
47105  * Copyright(c) 2006-2007, Ext JS, LLC.
47106  *
47107  * Originally Released Under LGPL - original licence link has changed is not relivant.
47108  *
47109  * Fork - LGPL
47110  * <script type="text/javascript">
47111  */
47112  
47113 Roo.grid.AbstractGridView = function(){
47114         this.grid = null;
47115         
47116         this.events = {
47117             "beforerowremoved" : true,
47118             "beforerowsinserted" : true,
47119             "beforerefresh" : true,
47120             "rowremoved" : true,
47121             "rowsinserted" : true,
47122             "rowupdated" : true,
47123             "refresh" : true
47124         };
47125     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47126 };
47127
47128 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47129     rowClass : "x-grid-row",
47130     cellClass : "x-grid-cell",
47131     tdClass : "x-grid-td",
47132     hdClass : "x-grid-hd",
47133     splitClass : "x-grid-hd-split",
47134     
47135         init: function(grid){
47136         this.grid = grid;
47137                 var cid = this.grid.getGridEl().id;
47138         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47139         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47140         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47141         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47142         },
47143         
47144         getColumnRenderers : function(){
47145         var renderers = [];
47146         var cm = this.grid.colModel;
47147         var colCount = cm.getColumnCount();
47148         for(var i = 0; i < colCount; i++){
47149             renderers[i] = cm.getRenderer(i);
47150         }
47151         return renderers;
47152     },
47153     
47154     getColumnIds : function(){
47155         var ids = [];
47156         var cm = this.grid.colModel;
47157         var colCount = cm.getColumnCount();
47158         for(var i = 0; i < colCount; i++){
47159             ids[i] = cm.getColumnId(i);
47160         }
47161         return ids;
47162     },
47163     
47164     getDataIndexes : function(){
47165         if(!this.indexMap){
47166             this.indexMap = this.buildIndexMap();
47167         }
47168         return this.indexMap.colToData;
47169     },
47170     
47171     getColumnIndexByDataIndex : function(dataIndex){
47172         if(!this.indexMap){
47173             this.indexMap = this.buildIndexMap();
47174         }
47175         return this.indexMap.dataToCol[dataIndex];
47176     },
47177     
47178     /**
47179      * Set a css style for a column dynamically. 
47180      * @param {Number} colIndex The index of the column
47181      * @param {String} name The css property name
47182      * @param {String} value The css value
47183      */
47184     setCSSStyle : function(colIndex, name, value){
47185         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47186         Roo.util.CSS.updateRule(selector, name, value);
47187     },
47188     
47189     generateRules : function(cm){
47190         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47191         Roo.util.CSS.removeStyleSheet(rulesId);
47192         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47193             var cid = cm.getColumnId(i);
47194             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47195                          this.tdSelector, cid, " {\n}\n",
47196                          this.hdSelector, cid, " {\n}\n",
47197                          this.splitSelector, cid, " {\n}\n");
47198         }
47199         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47200     }
47201 });/*
47202  * Based on:
47203  * Ext JS Library 1.1.1
47204  * Copyright(c) 2006-2007, Ext JS, LLC.
47205  *
47206  * Originally Released Under LGPL - original licence link has changed is not relivant.
47207  *
47208  * Fork - LGPL
47209  * <script type="text/javascript">
47210  */
47211
47212 // private
47213 // This is a support class used internally by the Grid components
47214 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47215     this.grid = grid;
47216     this.view = grid.getView();
47217     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47218     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47219     if(hd2){
47220         this.setHandleElId(Roo.id(hd));
47221         this.setOuterHandleElId(Roo.id(hd2));
47222     }
47223     this.scroll = false;
47224 };
47225 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47226     maxDragWidth: 120,
47227     getDragData : function(e){
47228         var t = Roo.lib.Event.getTarget(e);
47229         var h = this.view.findHeaderCell(t);
47230         if(h){
47231             return {ddel: h.firstChild, header:h};
47232         }
47233         return false;
47234     },
47235
47236     onInitDrag : function(e){
47237         this.view.headersDisabled = true;
47238         var clone = this.dragData.ddel.cloneNode(true);
47239         clone.id = Roo.id();
47240         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47241         this.proxy.update(clone);
47242         return true;
47243     },
47244
47245     afterValidDrop : function(){
47246         var v = this.view;
47247         setTimeout(function(){
47248             v.headersDisabled = false;
47249         }, 50);
47250     },
47251
47252     afterInvalidDrop : function(){
47253         var v = this.view;
47254         setTimeout(function(){
47255             v.headersDisabled = false;
47256         }, 50);
47257     }
47258 });
47259 /*
47260  * Based on:
47261  * Ext JS Library 1.1.1
47262  * Copyright(c) 2006-2007, Ext JS, LLC.
47263  *
47264  * Originally Released Under LGPL - original licence link has changed is not relivant.
47265  *
47266  * Fork - LGPL
47267  * <script type="text/javascript">
47268  */
47269 // private
47270 // This is a support class used internally by the Grid components
47271 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47272     this.grid = grid;
47273     this.view = grid.getView();
47274     // split the proxies so they don't interfere with mouse events
47275     this.proxyTop = Roo.DomHelper.append(document.body, {
47276         cls:"col-move-top", html:"&#160;"
47277     }, true);
47278     this.proxyBottom = Roo.DomHelper.append(document.body, {
47279         cls:"col-move-bottom", html:"&#160;"
47280     }, true);
47281     this.proxyTop.hide = this.proxyBottom.hide = function(){
47282         this.setLeftTop(-100,-100);
47283         this.setStyle("visibility", "hidden");
47284     };
47285     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47286     // temporarily disabled
47287     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47288     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47289 };
47290 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47291     proxyOffsets : [-4, -9],
47292     fly: Roo.Element.fly,
47293
47294     getTargetFromEvent : function(e){
47295         var t = Roo.lib.Event.getTarget(e);
47296         var cindex = this.view.findCellIndex(t);
47297         if(cindex !== false){
47298             return this.view.getHeaderCell(cindex);
47299         }
47300         return null;
47301     },
47302
47303     nextVisible : function(h){
47304         var v = this.view, cm = this.grid.colModel;
47305         h = h.nextSibling;
47306         while(h){
47307             if(!cm.isHidden(v.getCellIndex(h))){
47308                 return h;
47309             }
47310             h = h.nextSibling;
47311         }
47312         return null;
47313     },
47314
47315     prevVisible : function(h){
47316         var v = this.view, cm = this.grid.colModel;
47317         h = h.prevSibling;
47318         while(h){
47319             if(!cm.isHidden(v.getCellIndex(h))){
47320                 return h;
47321             }
47322             h = h.prevSibling;
47323         }
47324         return null;
47325     },
47326
47327     positionIndicator : function(h, n, e){
47328         var x = Roo.lib.Event.getPageX(e);
47329         var r = Roo.lib.Dom.getRegion(n.firstChild);
47330         var px, pt, py = r.top + this.proxyOffsets[1];
47331         if((r.right - x) <= (r.right-r.left)/2){
47332             px = r.right+this.view.borderWidth;
47333             pt = "after";
47334         }else{
47335             px = r.left;
47336             pt = "before";
47337         }
47338         var oldIndex = this.view.getCellIndex(h);
47339         var newIndex = this.view.getCellIndex(n);
47340
47341         if(this.grid.colModel.isFixed(newIndex)){
47342             return false;
47343         }
47344
47345         var locked = this.grid.colModel.isLocked(newIndex);
47346
47347         if(pt == "after"){
47348             newIndex++;
47349         }
47350         if(oldIndex < newIndex){
47351             newIndex--;
47352         }
47353         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47354             return false;
47355         }
47356         px +=  this.proxyOffsets[0];
47357         this.proxyTop.setLeftTop(px, py);
47358         this.proxyTop.show();
47359         if(!this.bottomOffset){
47360             this.bottomOffset = this.view.mainHd.getHeight();
47361         }
47362         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47363         this.proxyBottom.show();
47364         return pt;
47365     },
47366
47367     onNodeEnter : function(n, dd, e, data){
47368         if(data.header != n){
47369             this.positionIndicator(data.header, n, e);
47370         }
47371     },
47372
47373     onNodeOver : function(n, dd, e, data){
47374         var result = false;
47375         if(data.header != n){
47376             result = this.positionIndicator(data.header, n, e);
47377         }
47378         if(!result){
47379             this.proxyTop.hide();
47380             this.proxyBottom.hide();
47381         }
47382         return result ? this.dropAllowed : this.dropNotAllowed;
47383     },
47384
47385     onNodeOut : function(n, dd, e, data){
47386         this.proxyTop.hide();
47387         this.proxyBottom.hide();
47388     },
47389
47390     onNodeDrop : function(n, dd, e, data){
47391         var h = data.header;
47392         if(h != n){
47393             var cm = this.grid.colModel;
47394             var x = Roo.lib.Event.getPageX(e);
47395             var r = Roo.lib.Dom.getRegion(n.firstChild);
47396             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47397             var oldIndex = this.view.getCellIndex(h);
47398             var newIndex = this.view.getCellIndex(n);
47399             var locked = cm.isLocked(newIndex);
47400             if(pt == "after"){
47401                 newIndex++;
47402             }
47403             if(oldIndex < newIndex){
47404                 newIndex--;
47405             }
47406             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47407                 return false;
47408             }
47409             cm.setLocked(oldIndex, locked, true);
47410             cm.moveColumn(oldIndex, newIndex);
47411             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47412             return true;
47413         }
47414         return false;
47415     }
47416 });
47417 /*
47418  * Based on:
47419  * Ext JS Library 1.1.1
47420  * Copyright(c) 2006-2007, Ext JS, LLC.
47421  *
47422  * Originally Released Under LGPL - original licence link has changed is not relivant.
47423  *
47424  * Fork - LGPL
47425  * <script type="text/javascript">
47426  */
47427   
47428 /**
47429  * @class Roo.grid.GridView
47430  * @extends Roo.util.Observable
47431  *
47432  * @constructor
47433  * @param {Object} config
47434  */
47435 Roo.grid.GridView = function(config){
47436     Roo.grid.GridView.superclass.constructor.call(this);
47437     this.el = null;
47438
47439     Roo.apply(this, config);
47440 };
47441
47442 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47443
47444     /**
47445      * Override this function to apply custom css classes to rows during rendering
47446      * @param {Record} record The record
47447      * @param {Number} index
47448      * @method getRowClass
47449      */
47450     rowClass : "x-grid-row",
47451
47452     cellClass : "x-grid-col",
47453
47454     tdClass : "x-grid-td",
47455
47456     hdClass : "x-grid-hd",
47457
47458     splitClass : "x-grid-split",
47459
47460     sortClasses : ["sort-asc", "sort-desc"],
47461
47462     enableMoveAnim : false,
47463
47464     hlColor: "C3DAF9",
47465
47466     dh : Roo.DomHelper,
47467
47468     fly : Roo.Element.fly,
47469
47470     css : Roo.util.CSS,
47471
47472     borderWidth: 1,
47473
47474     splitOffset: 3,
47475
47476     scrollIncrement : 22,
47477
47478     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47479
47480     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47481
47482     bind : function(ds, cm){
47483         if(this.ds){
47484             this.ds.un("load", this.onLoad, this);
47485             this.ds.un("datachanged", this.onDataChange, this);
47486             this.ds.un("add", this.onAdd, this);
47487             this.ds.un("remove", this.onRemove, this);
47488             this.ds.un("update", this.onUpdate, this);
47489             this.ds.un("clear", this.onClear, this);
47490         }
47491         if(ds){
47492             ds.on("load", this.onLoad, this);
47493             ds.on("datachanged", this.onDataChange, this);
47494             ds.on("add", this.onAdd, this);
47495             ds.on("remove", this.onRemove, this);
47496             ds.on("update", this.onUpdate, this);
47497             ds.on("clear", this.onClear, this);
47498         }
47499         this.ds = ds;
47500
47501         if(this.cm){
47502             this.cm.un("widthchange", this.onColWidthChange, this);
47503             this.cm.un("headerchange", this.onHeaderChange, this);
47504             this.cm.un("hiddenchange", this.onHiddenChange, this);
47505             this.cm.un("columnmoved", this.onColumnMove, this);
47506             this.cm.un("columnlockchange", this.onColumnLock, this);
47507         }
47508         if(cm){
47509             this.generateRules(cm);
47510             cm.on("widthchange", this.onColWidthChange, this);
47511             cm.on("headerchange", this.onHeaderChange, this);
47512             cm.on("hiddenchange", this.onHiddenChange, this);
47513             cm.on("columnmoved", this.onColumnMove, this);
47514             cm.on("columnlockchange", this.onColumnLock, this);
47515         }
47516         this.cm = cm;
47517     },
47518
47519     init: function(grid){
47520         Roo.grid.GridView.superclass.init.call(this, grid);
47521
47522         this.bind(grid.dataSource, grid.colModel);
47523
47524         grid.on("headerclick", this.handleHeaderClick, this);
47525
47526         if(grid.trackMouseOver){
47527             grid.on("mouseover", this.onRowOver, this);
47528             grid.on("mouseout", this.onRowOut, this);
47529         }
47530         grid.cancelTextSelection = function(){};
47531         this.gridId = grid.id;
47532
47533         var tpls = this.templates || {};
47534
47535         if(!tpls.master){
47536             tpls.master = new Roo.Template(
47537                '<div class="x-grid" hidefocus="true">',
47538                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47539                   '<div class="x-grid-topbar"></div>',
47540                   '<div class="x-grid-scroller"><div></div></div>',
47541                   '<div class="x-grid-locked">',
47542                       '<div class="x-grid-header">{lockedHeader}</div>',
47543                       '<div class="x-grid-body">{lockedBody}</div>',
47544                   "</div>",
47545                   '<div class="x-grid-viewport">',
47546                       '<div class="x-grid-header">{header}</div>',
47547                       '<div class="x-grid-body">{body}</div>',
47548                   "</div>",
47549                   '<div class="x-grid-bottombar"></div>',
47550                  
47551                   '<div class="x-grid-resize-proxy">&#160;</div>',
47552                "</div>"
47553             );
47554             tpls.master.disableformats = true;
47555         }
47556
47557         if(!tpls.header){
47558             tpls.header = new Roo.Template(
47559                '<table border="0" cellspacing="0" cellpadding="0">',
47560                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47561                "</table>{splits}"
47562             );
47563             tpls.header.disableformats = true;
47564         }
47565         tpls.header.compile();
47566
47567         if(!tpls.hcell){
47568             tpls.hcell = new Roo.Template(
47569                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47570                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47571                 "</div></td>"
47572              );
47573              tpls.hcell.disableFormats = true;
47574         }
47575         tpls.hcell.compile();
47576
47577         if(!tpls.hsplit){
47578             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47579             tpls.hsplit.disableFormats = true;
47580         }
47581         tpls.hsplit.compile();
47582
47583         if(!tpls.body){
47584             tpls.body = new Roo.Template(
47585                '<table border="0" cellspacing="0" cellpadding="0">',
47586                "<tbody>{rows}</tbody>",
47587                "</table>"
47588             );
47589             tpls.body.disableFormats = true;
47590         }
47591         tpls.body.compile();
47592
47593         if(!tpls.row){
47594             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47595             tpls.row.disableFormats = true;
47596         }
47597         tpls.row.compile();
47598
47599         if(!tpls.cell){
47600             tpls.cell = new Roo.Template(
47601                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47602                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47603                 "</td>"
47604             );
47605             tpls.cell.disableFormats = true;
47606         }
47607         tpls.cell.compile();
47608
47609         this.templates = tpls;
47610     },
47611
47612     // remap these for backwards compat
47613     onColWidthChange : function(){
47614         this.updateColumns.apply(this, arguments);
47615     },
47616     onHeaderChange : function(){
47617         this.updateHeaders.apply(this, arguments);
47618     }, 
47619     onHiddenChange : function(){
47620         this.handleHiddenChange.apply(this, arguments);
47621     },
47622     onColumnMove : function(){
47623         this.handleColumnMove.apply(this, arguments);
47624     },
47625     onColumnLock : function(){
47626         this.handleLockChange.apply(this, arguments);
47627     },
47628
47629     onDataChange : function(){
47630         this.refresh();
47631         this.updateHeaderSortState();
47632     },
47633
47634     onClear : function(){
47635         this.refresh();
47636     },
47637
47638     onUpdate : function(ds, record){
47639         this.refreshRow(record);
47640     },
47641
47642     refreshRow : function(record){
47643         var ds = this.ds, index;
47644         if(typeof record == 'number'){
47645             index = record;
47646             record = ds.getAt(index);
47647         }else{
47648             index = ds.indexOf(record);
47649         }
47650         this.insertRows(ds, index, index, true);
47651         this.onRemove(ds, record, index+1, true);
47652         this.syncRowHeights(index, index);
47653         this.layout();
47654         this.fireEvent("rowupdated", this, index, record);
47655     },
47656
47657     onAdd : function(ds, records, index){
47658         this.insertRows(ds, index, index + (records.length-1));
47659     },
47660
47661     onRemove : function(ds, record, index, isUpdate){
47662         if(isUpdate !== true){
47663             this.fireEvent("beforerowremoved", this, index, record);
47664         }
47665         var bt = this.getBodyTable(), lt = this.getLockedTable();
47666         if(bt.rows[index]){
47667             bt.firstChild.removeChild(bt.rows[index]);
47668         }
47669         if(lt.rows[index]){
47670             lt.firstChild.removeChild(lt.rows[index]);
47671         }
47672         if(isUpdate !== true){
47673             this.stripeRows(index);
47674             this.syncRowHeights(index, index);
47675             this.layout();
47676             this.fireEvent("rowremoved", this, index, record);
47677         }
47678     },
47679
47680     onLoad : function(){
47681         this.scrollToTop();
47682     },
47683
47684     /**
47685      * Scrolls the grid to the top
47686      */
47687     scrollToTop : function(){
47688         if(this.scroller){
47689             this.scroller.dom.scrollTop = 0;
47690             this.syncScroll();
47691         }
47692     },
47693
47694     /**
47695      * Gets a panel in the header of the grid that can be used for toolbars etc.
47696      * After modifying the contents of this panel a call to grid.autoSize() may be
47697      * required to register any changes in size.
47698      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47699      * @return Roo.Element
47700      */
47701     getHeaderPanel : function(doShow){
47702         if(doShow){
47703             this.headerPanel.show();
47704         }
47705         return this.headerPanel;
47706     },
47707
47708     /**
47709      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47710      * After modifying the contents of this panel a call to grid.autoSize() may be
47711      * required to register any changes in size.
47712      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47713      * @return Roo.Element
47714      */
47715     getFooterPanel : function(doShow){
47716         if(doShow){
47717             this.footerPanel.show();
47718         }
47719         return this.footerPanel;
47720     },
47721
47722     initElements : function(){
47723         var E = Roo.Element;
47724         var el = this.grid.getGridEl().dom.firstChild;
47725         var cs = el.childNodes;
47726
47727         this.el = new E(el);
47728         
47729          this.focusEl = new E(el.firstChild);
47730         this.focusEl.swallowEvent("click", true);
47731         
47732         this.headerPanel = new E(cs[1]);
47733         this.headerPanel.enableDisplayMode("block");
47734
47735         this.scroller = new E(cs[2]);
47736         this.scrollSizer = new E(this.scroller.dom.firstChild);
47737
47738         this.lockedWrap = new E(cs[3]);
47739         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47740         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47741
47742         this.mainWrap = new E(cs[4]);
47743         this.mainHd = new E(this.mainWrap.dom.firstChild);
47744         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47745
47746         this.footerPanel = new E(cs[5]);
47747         this.footerPanel.enableDisplayMode("block");
47748
47749         this.resizeProxy = new E(cs[6]);
47750
47751         this.headerSelector = String.format(
47752            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47753            this.lockedHd.id, this.mainHd.id
47754         );
47755
47756         this.splitterSelector = String.format(
47757            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47758            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47759         );
47760     },
47761     idToCssName : function(s)
47762     {
47763         return s.replace(/[^a-z0-9]+/ig, '-');
47764     },
47765
47766     getHeaderCell : function(index){
47767         return Roo.DomQuery.select(this.headerSelector)[index];
47768     },
47769
47770     getHeaderCellMeasure : function(index){
47771         return this.getHeaderCell(index).firstChild;
47772     },
47773
47774     getHeaderCellText : function(index){
47775         return this.getHeaderCell(index).firstChild.firstChild;
47776     },
47777
47778     getLockedTable : function(){
47779         return this.lockedBody.dom.firstChild;
47780     },
47781
47782     getBodyTable : function(){
47783         return this.mainBody.dom.firstChild;
47784     },
47785
47786     getLockedRow : function(index){
47787         return this.getLockedTable().rows[index];
47788     },
47789
47790     getRow : function(index){
47791         return this.getBodyTable().rows[index];
47792     },
47793
47794     getRowComposite : function(index){
47795         if(!this.rowEl){
47796             this.rowEl = new Roo.CompositeElementLite();
47797         }
47798         var els = [], lrow, mrow;
47799         if(lrow = this.getLockedRow(index)){
47800             els.push(lrow);
47801         }
47802         if(mrow = this.getRow(index)){
47803             els.push(mrow);
47804         }
47805         this.rowEl.elements = els;
47806         return this.rowEl;
47807     },
47808
47809     getCell : function(rowIndex, colIndex){
47810         var locked = this.cm.getLockedCount();
47811         var source;
47812         if(colIndex < locked){
47813             source = this.lockedBody.dom.firstChild;
47814         }else{
47815             source = this.mainBody.dom.firstChild;
47816             colIndex -= locked;
47817         }
47818         return source.rows[rowIndex].childNodes[colIndex];
47819     },
47820
47821     getCellText : function(rowIndex, colIndex){
47822         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47823     },
47824
47825     getCellBox : function(cell){
47826         var b = this.fly(cell).getBox();
47827         if(Roo.isOpera){ // opera fails to report the Y
47828             b.y = cell.offsetTop + this.mainBody.getY();
47829         }
47830         return b;
47831     },
47832
47833     getCellIndex : function(cell){
47834         var id = String(cell.className).match(this.cellRE);
47835         if(id){
47836             return parseInt(id[1], 10);
47837         }
47838         return 0;
47839     },
47840
47841     findHeaderIndex : function(n){
47842         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47843         return r ? this.getCellIndex(r) : false;
47844     },
47845
47846     findHeaderCell : function(n){
47847         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47848         return r ? r : false;
47849     },
47850
47851     findRowIndex : function(n){
47852         if(!n){
47853             return false;
47854         }
47855         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47856         return r ? r.rowIndex : false;
47857     },
47858
47859     findCellIndex : function(node){
47860         var stop = this.el.dom;
47861         while(node && node != stop){
47862             if(this.findRE.test(node.className)){
47863                 return this.getCellIndex(node);
47864             }
47865             node = node.parentNode;
47866         }
47867         return false;
47868     },
47869
47870     getColumnId : function(index){
47871         return this.cm.getColumnId(index);
47872     },
47873
47874     getSplitters : function()
47875     {
47876         if(this.splitterSelector){
47877            return Roo.DomQuery.select(this.splitterSelector);
47878         }else{
47879             return null;
47880       }
47881     },
47882
47883     getSplitter : function(index){
47884         return this.getSplitters()[index];
47885     },
47886
47887     onRowOver : function(e, t){
47888         var row;
47889         if((row = this.findRowIndex(t)) !== false){
47890             this.getRowComposite(row).addClass("x-grid-row-over");
47891         }
47892     },
47893
47894     onRowOut : function(e, t){
47895         var row;
47896         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47897             this.getRowComposite(row).removeClass("x-grid-row-over");
47898         }
47899     },
47900
47901     renderHeaders : function(){
47902         var cm = this.cm;
47903         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47904         var cb = [], lb = [], sb = [], lsb = [], p = {};
47905         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47906             p.cellId = "x-grid-hd-0-" + i;
47907             p.splitId = "x-grid-csplit-0-" + i;
47908             p.id = cm.getColumnId(i);
47909             p.title = cm.getColumnTooltip(i) || "";
47910             p.value = cm.getColumnHeader(i) || "";
47911             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47912             if(!cm.isLocked(i)){
47913                 cb[cb.length] = ct.apply(p);
47914                 sb[sb.length] = st.apply(p);
47915             }else{
47916                 lb[lb.length] = ct.apply(p);
47917                 lsb[lsb.length] = st.apply(p);
47918             }
47919         }
47920         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47921                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47922     },
47923
47924     updateHeaders : function(){
47925         var html = this.renderHeaders();
47926         this.lockedHd.update(html[0]);
47927         this.mainHd.update(html[1]);
47928     },
47929
47930     /**
47931      * Focuses the specified row.
47932      * @param {Number} row The row index
47933      */
47934     focusRow : function(row)
47935     {
47936         //Roo.log('GridView.focusRow');
47937         var x = this.scroller.dom.scrollLeft;
47938         this.focusCell(row, 0, false);
47939         this.scroller.dom.scrollLeft = x;
47940     },
47941
47942     /**
47943      * Focuses the specified cell.
47944      * @param {Number} row The row index
47945      * @param {Number} col The column index
47946      * @param {Boolean} hscroll false to disable horizontal scrolling
47947      */
47948     focusCell : function(row, col, hscroll)
47949     {
47950         //Roo.log('GridView.focusCell');
47951         var el = this.ensureVisible(row, col, hscroll);
47952         this.focusEl.alignTo(el, "tl-tl");
47953         if(Roo.isGecko){
47954             this.focusEl.focus();
47955         }else{
47956             this.focusEl.focus.defer(1, this.focusEl);
47957         }
47958     },
47959
47960     /**
47961      * Scrolls the specified cell into view
47962      * @param {Number} row The row index
47963      * @param {Number} col The column index
47964      * @param {Boolean} hscroll false to disable horizontal scrolling
47965      */
47966     ensureVisible : function(row, col, hscroll)
47967     {
47968         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
47969         //return null; //disable for testing.
47970         if(typeof row != "number"){
47971             row = row.rowIndex;
47972         }
47973         if(row < 0 && row >= this.ds.getCount()){
47974             return  null;
47975         }
47976         col = (col !== undefined ? col : 0);
47977         var cm = this.grid.colModel;
47978         while(cm.isHidden(col)){
47979             col++;
47980         }
47981
47982         var el = this.getCell(row, col);
47983         if(!el){
47984             return null;
47985         }
47986         var c = this.scroller.dom;
47987
47988         var ctop = parseInt(el.offsetTop, 10);
47989         var cleft = parseInt(el.offsetLeft, 10);
47990         var cbot = ctop + el.offsetHeight;
47991         var cright = cleft + el.offsetWidth;
47992         
47993         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47994         var stop = parseInt(c.scrollTop, 10);
47995         var sleft = parseInt(c.scrollLeft, 10);
47996         var sbot = stop + ch;
47997         var sright = sleft + c.clientWidth;
47998         /*
47999         Roo.log('GridView.ensureVisible:' +
48000                 ' ctop:' + ctop +
48001                 ' c.clientHeight:' + c.clientHeight +
48002                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48003                 ' stop:' + stop +
48004                 ' cbot:' + cbot +
48005                 ' sbot:' + sbot +
48006                 ' ch:' + ch  
48007                 );
48008         */
48009         if(ctop < stop){
48010              c.scrollTop = ctop;
48011             //Roo.log("set scrolltop to ctop DISABLE?");
48012         }else if(cbot > sbot){
48013             //Roo.log("set scrolltop to cbot-ch");
48014             c.scrollTop = cbot-ch;
48015         }
48016         
48017         if(hscroll !== false){
48018             if(cleft < sleft){
48019                 c.scrollLeft = cleft;
48020             }else if(cright > sright){
48021                 c.scrollLeft = cright-c.clientWidth;
48022             }
48023         }
48024          
48025         return el;
48026     },
48027
48028     updateColumns : function(){
48029         this.grid.stopEditing();
48030         var cm = this.grid.colModel, colIds = this.getColumnIds();
48031         //var totalWidth = cm.getTotalWidth();
48032         var pos = 0;
48033         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48034             //if(cm.isHidden(i)) continue;
48035             var w = cm.getColumnWidth(i);
48036             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48037             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48038         }
48039         this.updateSplitters();
48040     },
48041
48042     generateRules : function(cm){
48043         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48044         Roo.util.CSS.removeStyleSheet(rulesId);
48045         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48046             var cid = cm.getColumnId(i);
48047             var align = '';
48048             if(cm.config[i].align){
48049                 align = 'text-align:'+cm.config[i].align+';';
48050             }
48051             var hidden = '';
48052             if(cm.isHidden(i)){
48053                 hidden = 'display:none;';
48054             }
48055             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48056             ruleBuf.push(
48057                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48058                     this.hdSelector, cid, " {\n", align, width, "}\n",
48059                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48060                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48061         }
48062         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48063     },
48064
48065     updateSplitters : function(){
48066         var cm = this.cm, s = this.getSplitters();
48067         if(s){ // splitters not created yet
48068             var pos = 0, locked = true;
48069             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48070                 if(cm.isHidden(i)) continue;
48071                 var w = cm.getColumnWidth(i); // make sure it's a number
48072                 if(!cm.isLocked(i) && locked){
48073                     pos = 0;
48074                     locked = false;
48075                 }
48076                 pos += w;
48077                 s[i].style.left = (pos-this.splitOffset) + "px";
48078             }
48079         }
48080     },
48081
48082     handleHiddenChange : function(colModel, colIndex, hidden){
48083         if(hidden){
48084             this.hideColumn(colIndex);
48085         }else{
48086             this.unhideColumn(colIndex);
48087         }
48088     },
48089
48090     hideColumn : function(colIndex){
48091         var cid = this.getColumnId(colIndex);
48092         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48093         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48094         if(Roo.isSafari){
48095             this.updateHeaders();
48096         }
48097         this.updateSplitters();
48098         this.layout();
48099     },
48100
48101     unhideColumn : function(colIndex){
48102         var cid = this.getColumnId(colIndex);
48103         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48104         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48105
48106         if(Roo.isSafari){
48107             this.updateHeaders();
48108         }
48109         this.updateSplitters();
48110         this.layout();
48111     },
48112
48113     insertRows : function(dm, firstRow, lastRow, isUpdate){
48114         if(firstRow == 0 && lastRow == dm.getCount()-1){
48115             this.refresh();
48116         }else{
48117             if(!isUpdate){
48118                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48119             }
48120             var s = this.getScrollState();
48121             var markup = this.renderRows(firstRow, lastRow);
48122             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48123             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48124             this.restoreScroll(s);
48125             if(!isUpdate){
48126                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48127                 this.syncRowHeights(firstRow, lastRow);
48128                 this.stripeRows(firstRow);
48129                 this.layout();
48130             }
48131         }
48132     },
48133
48134     bufferRows : function(markup, target, index){
48135         var before = null, trows = target.rows, tbody = target.tBodies[0];
48136         if(index < trows.length){
48137             before = trows[index];
48138         }
48139         var b = document.createElement("div");
48140         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48141         var rows = b.firstChild.rows;
48142         for(var i = 0, len = rows.length; i < len; i++){
48143             if(before){
48144                 tbody.insertBefore(rows[0], before);
48145             }else{
48146                 tbody.appendChild(rows[0]);
48147             }
48148         }
48149         b.innerHTML = "";
48150         b = null;
48151     },
48152
48153     deleteRows : function(dm, firstRow, lastRow){
48154         if(dm.getRowCount()<1){
48155             this.fireEvent("beforerefresh", this);
48156             this.mainBody.update("");
48157             this.lockedBody.update("");
48158             this.fireEvent("refresh", this);
48159         }else{
48160             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48161             var bt = this.getBodyTable();
48162             var tbody = bt.firstChild;
48163             var rows = bt.rows;
48164             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48165                 tbody.removeChild(rows[firstRow]);
48166             }
48167             this.stripeRows(firstRow);
48168             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48169         }
48170     },
48171
48172     updateRows : function(dataSource, firstRow, lastRow){
48173         var s = this.getScrollState();
48174         this.refresh();
48175         this.restoreScroll(s);
48176     },
48177
48178     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48179         if(!noRefresh){
48180            this.refresh();
48181         }
48182         this.updateHeaderSortState();
48183     },
48184
48185     getScrollState : function(){
48186         
48187         var sb = this.scroller.dom;
48188         return {left: sb.scrollLeft, top: sb.scrollTop};
48189     },
48190
48191     stripeRows : function(startRow){
48192         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48193             return;
48194         }
48195         startRow = startRow || 0;
48196         var rows = this.getBodyTable().rows;
48197         var lrows = this.getLockedTable().rows;
48198         var cls = ' x-grid-row-alt ';
48199         for(var i = startRow, len = rows.length; i < len; i++){
48200             var row = rows[i], lrow = lrows[i];
48201             var isAlt = ((i+1) % 2 == 0);
48202             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48203             if(isAlt == hasAlt){
48204                 continue;
48205             }
48206             if(isAlt){
48207                 row.className += " x-grid-row-alt";
48208             }else{
48209                 row.className = row.className.replace("x-grid-row-alt", "");
48210             }
48211             if(lrow){
48212                 lrow.className = row.className;
48213             }
48214         }
48215     },
48216
48217     restoreScroll : function(state){
48218         //Roo.log('GridView.restoreScroll');
48219         var sb = this.scroller.dom;
48220         sb.scrollLeft = state.left;
48221         sb.scrollTop = state.top;
48222         this.syncScroll();
48223     },
48224
48225     syncScroll : function(){
48226         //Roo.log('GridView.syncScroll');
48227         var sb = this.scroller.dom;
48228         var sh = this.mainHd.dom;
48229         var bs = this.mainBody.dom;
48230         var lv = this.lockedBody.dom;
48231         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48232         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48233     },
48234
48235     handleScroll : function(e){
48236         this.syncScroll();
48237         var sb = this.scroller.dom;
48238         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48239         e.stopEvent();
48240     },
48241
48242     handleWheel : function(e){
48243         var d = e.getWheelDelta();
48244         this.scroller.dom.scrollTop -= d*22;
48245         // set this here to prevent jumpy scrolling on large tables
48246         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48247         e.stopEvent();
48248     },
48249
48250     renderRows : function(startRow, endRow){
48251         // pull in all the crap needed to render rows
48252         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48253         var colCount = cm.getColumnCount();
48254
48255         if(ds.getCount() < 1){
48256             return ["", ""];
48257         }
48258
48259         // build a map for all the columns
48260         var cs = [];
48261         for(var i = 0; i < colCount; i++){
48262             var name = cm.getDataIndex(i);
48263             cs[i] = {
48264                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48265                 renderer : cm.getRenderer(i),
48266                 id : cm.getColumnId(i),
48267                 locked : cm.isLocked(i)
48268             };
48269         }
48270
48271         startRow = startRow || 0;
48272         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48273
48274         // records to render
48275         var rs = ds.getRange(startRow, endRow);
48276
48277         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48278     },
48279
48280     // As much as I hate to duplicate code, this was branched because FireFox really hates
48281     // [].join("") on strings. The performance difference was substantial enough to
48282     // branch this function
48283     doRender : Roo.isGecko ?
48284             function(cs, rs, ds, startRow, colCount, stripe){
48285                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48286                 // buffers
48287                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48288                 
48289                 var hasListener = this.grid.hasListener('rowclass');
48290                 var rowcfg = {};
48291                 for(var j = 0, len = rs.length; j < len; j++){
48292                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48293                     for(var i = 0; i < colCount; i++){
48294                         c = cs[i];
48295                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48296                         p.id = c.id;
48297                         p.css = p.attr = "";
48298                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48299                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48300                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48301                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48302                         }
48303                         var markup = ct.apply(p);
48304                         if(!c.locked){
48305                             cb+= markup;
48306                         }else{
48307                             lcb+= markup;
48308                         }
48309                     }
48310                     var alt = [];
48311                     if(stripe && ((rowIndex+1) % 2 == 0)){
48312                         alt.push("x-grid-row-alt")
48313                     }
48314                     if(r.dirty){
48315                         alt.push(  " x-grid-dirty-row");
48316                     }
48317                     rp.cells = lcb;
48318                     if(this.getRowClass){
48319                         alt.push(this.getRowClass(r, rowIndex));
48320                     }
48321                     if (hasListener) {
48322                         rowcfg = {
48323                              
48324                             record: r,
48325                             rowIndex : rowIndex,
48326                             rowClass : ''
48327                         }
48328                         this.grid.fireEvent('rowclass', this, rowcfg);
48329                         alt.push(rowcfg.rowClass);
48330                     }
48331                     rp.alt = alt.join(" ");
48332                     lbuf+= rt.apply(rp);
48333                     rp.cells = cb;
48334                     buf+=  rt.apply(rp);
48335                 }
48336                 return [lbuf, buf];
48337             } :
48338             function(cs, rs, ds, startRow, colCount, stripe){
48339                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48340                 // buffers
48341                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48342                 var hasListener = this.grid.hasListener('rowclass');
48343                 var rowcfg = {};
48344                 for(var j = 0, len = rs.length; j < len; j++){
48345                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48346                     for(var i = 0; i < colCount; i++){
48347                         c = cs[i];
48348                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48349                         p.id = c.id;
48350                         p.css = p.attr = "";
48351                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48352                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48353                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48354                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48355                         }
48356                         var markup = ct.apply(p);
48357                         if(!c.locked){
48358                             cb[cb.length] = markup;
48359                         }else{
48360                             lcb[lcb.length] = markup;
48361                         }
48362                     }
48363                     var alt = [];
48364                     if(stripe && ((rowIndex+1) % 2 == 0)){
48365                         alt.push( "x-grid-row-alt");
48366                     }
48367                     if(r.dirty){
48368                         alt.push(" x-grid-dirty-row");
48369                     }
48370                     rp.cells = lcb;
48371                     if(this.getRowClass){
48372                         alt.push( this.getRowClass(r, rowIndex));
48373                     }
48374                     if (hasListener) {
48375                         rowcfg = {
48376                              
48377                             record: r,
48378                             rowIndex : rowIndex,
48379                             rowClass : ''
48380                         }
48381                         this.grid.fireEvent('rowclass', this, rowcfg);
48382                         alt.push(rowcfg.rowClass);
48383                     }
48384                     rp.alt = alt.join(" ");
48385                     rp.cells = lcb.join("");
48386                     lbuf[lbuf.length] = rt.apply(rp);
48387                     rp.cells = cb.join("");
48388                     buf[buf.length] =  rt.apply(rp);
48389                 }
48390                 return [lbuf.join(""), buf.join("")];
48391             },
48392
48393     renderBody : function(){
48394         var markup = this.renderRows();
48395         var bt = this.templates.body;
48396         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48397     },
48398
48399     /**
48400      * Refreshes the grid
48401      * @param {Boolean} headersToo
48402      */
48403     refresh : function(headersToo){
48404         this.fireEvent("beforerefresh", this);
48405         this.grid.stopEditing();
48406         var result = this.renderBody();
48407         this.lockedBody.update(result[0]);
48408         this.mainBody.update(result[1]);
48409         if(headersToo === true){
48410             this.updateHeaders();
48411             this.updateColumns();
48412             this.updateSplitters();
48413             this.updateHeaderSortState();
48414         }
48415         this.syncRowHeights();
48416         this.layout();
48417         this.fireEvent("refresh", this);
48418     },
48419
48420     handleColumnMove : function(cm, oldIndex, newIndex){
48421         this.indexMap = null;
48422         var s = this.getScrollState();
48423         this.refresh(true);
48424         this.restoreScroll(s);
48425         this.afterMove(newIndex);
48426     },
48427
48428     afterMove : function(colIndex){
48429         if(this.enableMoveAnim && Roo.enableFx){
48430             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48431         }
48432         // if multisort - fix sortOrder, and reload..
48433         if (this.grid.dataSource.multiSort) {
48434             // the we can call sort again..
48435             var dm = this.grid.dataSource;
48436             var cm = this.grid.colModel;
48437             var so = [];
48438             for(var i = 0; i < cm.config.length; i++ ) {
48439                 
48440                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48441                     continue; // dont' bother, it's not in sort list or being set.
48442                 }
48443                 
48444                 so.push(cm.config[i].dataIndex);
48445             };
48446             dm.sortOrder = so;
48447             dm.load(dm.lastOptions);
48448             
48449             
48450         }
48451         
48452     },
48453
48454     updateCell : function(dm, rowIndex, dataIndex){
48455         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48456         if(typeof colIndex == "undefined"){ // not present in grid
48457             return;
48458         }
48459         var cm = this.grid.colModel;
48460         var cell = this.getCell(rowIndex, colIndex);
48461         var cellText = this.getCellText(rowIndex, colIndex);
48462
48463         var p = {
48464             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48465             id : cm.getColumnId(colIndex),
48466             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48467         };
48468         var renderer = cm.getRenderer(colIndex);
48469         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48470         if(typeof val == "undefined" || val === "") val = "&#160;";
48471         cellText.innerHTML = val;
48472         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48473         this.syncRowHeights(rowIndex, rowIndex);
48474     },
48475
48476     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48477         var maxWidth = 0;
48478         if(this.grid.autoSizeHeaders){
48479             var h = this.getHeaderCellMeasure(colIndex);
48480             maxWidth = Math.max(maxWidth, h.scrollWidth);
48481         }
48482         var tb, index;
48483         if(this.cm.isLocked(colIndex)){
48484             tb = this.getLockedTable();
48485             index = colIndex;
48486         }else{
48487             tb = this.getBodyTable();
48488             index = colIndex - this.cm.getLockedCount();
48489         }
48490         if(tb && tb.rows){
48491             var rows = tb.rows;
48492             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48493             for(var i = 0; i < stopIndex; i++){
48494                 var cell = rows[i].childNodes[index].firstChild;
48495                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48496             }
48497         }
48498         return maxWidth + /*margin for error in IE*/ 5;
48499     },
48500     /**
48501      * Autofit a column to its content.
48502      * @param {Number} colIndex
48503      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48504      */
48505      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48506          if(this.cm.isHidden(colIndex)){
48507              return; // can't calc a hidden column
48508          }
48509         if(forceMinSize){
48510             var cid = this.cm.getColumnId(colIndex);
48511             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48512            if(this.grid.autoSizeHeaders){
48513                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48514            }
48515         }
48516         var newWidth = this.calcColumnWidth(colIndex);
48517         this.cm.setColumnWidth(colIndex,
48518             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48519         if(!suppressEvent){
48520             this.grid.fireEvent("columnresize", colIndex, newWidth);
48521         }
48522     },
48523
48524     /**
48525      * Autofits all columns to their content and then expands to fit any extra space in the grid
48526      */
48527      autoSizeColumns : function(){
48528         var cm = this.grid.colModel;
48529         var colCount = cm.getColumnCount();
48530         for(var i = 0; i < colCount; i++){
48531             this.autoSizeColumn(i, true, true);
48532         }
48533         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48534             this.fitColumns();
48535         }else{
48536             this.updateColumns();
48537             this.layout();
48538         }
48539     },
48540
48541     /**
48542      * Autofits all columns to the grid's width proportionate with their current size
48543      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48544      */
48545     fitColumns : function(reserveScrollSpace){
48546         var cm = this.grid.colModel;
48547         var colCount = cm.getColumnCount();
48548         var cols = [];
48549         var width = 0;
48550         var i, w;
48551         for (i = 0; i < colCount; i++){
48552             if(!cm.isHidden(i) && !cm.isFixed(i)){
48553                 w = cm.getColumnWidth(i);
48554                 cols.push(i);
48555                 cols.push(w);
48556                 width += w;
48557             }
48558         }
48559         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48560         if(reserveScrollSpace){
48561             avail -= 17;
48562         }
48563         var frac = (avail - cm.getTotalWidth())/width;
48564         while (cols.length){
48565             w = cols.pop();
48566             i = cols.pop();
48567             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48568         }
48569         this.updateColumns();
48570         this.layout();
48571     },
48572
48573     onRowSelect : function(rowIndex){
48574         var row = this.getRowComposite(rowIndex);
48575         row.addClass("x-grid-row-selected");
48576     },
48577
48578     onRowDeselect : function(rowIndex){
48579         var row = this.getRowComposite(rowIndex);
48580         row.removeClass("x-grid-row-selected");
48581     },
48582
48583     onCellSelect : function(row, col){
48584         var cell = this.getCell(row, col);
48585         if(cell){
48586             Roo.fly(cell).addClass("x-grid-cell-selected");
48587         }
48588     },
48589
48590     onCellDeselect : function(row, col){
48591         var cell = this.getCell(row, col);
48592         if(cell){
48593             Roo.fly(cell).removeClass("x-grid-cell-selected");
48594         }
48595     },
48596
48597     updateHeaderSortState : function(){
48598         
48599         // sort state can be single { field: xxx, direction : yyy}
48600         // or   { xxx=>ASC , yyy : DESC ..... }
48601         
48602         var mstate = {};
48603         if (!this.ds.multiSort) { 
48604             var state = this.ds.getSortState();
48605             if(!state){
48606                 return;
48607             }
48608             mstate[state.field] = state.direction;
48609             // FIXME... - this is not used here.. but might be elsewhere..
48610             this.sortState = state;
48611             
48612         } else {
48613             mstate = this.ds.sortToggle;
48614         }
48615         //remove existing sort classes..
48616         
48617         var sc = this.sortClasses;
48618         var hds = this.el.select(this.headerSelector).removeClass(sc);
48619         
48620         for(var f in mstate) {
48621         
48622             var sortColumn = this.cm.findColumnIndex(f);
48623             
48624             if(sortColumn != -1){
48625                 var sortDir = mstate[f];        
48626                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48627             }
48628         }
48629         
48630          
48631         
48632     },
48633
48634
48635     handleHeaderClick : function(g, index){
48636         if(this.headersDisabled){
48637             return;
48638         }
48639         var dm = g.dataSource, cm = g.colModel;
48640         if(!cm.isSortable(index)){
48641             return;
48642         }
48643         g.stopEditing();
48644         
48645         if (dm.multiSort) {
48646             // update the sortOrder
48647             var so = [];
48648             for(var i = 0; i < cm.config.length; i++ ) {
48649                 
48650                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
48651                     continue; // dont' bother, it's not in sort list or being set.
48652                 }
48653                 
48654                 so.push(cm.config[i].dataIndex);
48655             };
48656             dm.sortOrder = so;
48657         }
48658         
48659         
48660         dm.sort(cm.getDataIndex(index));
48661     },
48662
48663
48664     destroy : function(){
48665         if(this.colMenu){
48666             this.colMenu.removeAll();
48667             Roo.menu.MenuMgr.unregister(this.colMenu);
48668             this.colMenu.getEl().remove();
48669             delete this.colMenu;
48670         }
48671         if(this.hmenu){
48672             this.hmenu.removeAll();
48673             Roo.menu.MenuMgr.unregister(this.hmenu);
48674             this.hmenu.getEl().remove();
48675             delete this.hmenu;
48676         }
48677         if(this.grid.enableColumnMove){
48678             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48679             if(dds){
48680                 for(var dd in dds){
48681                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48682                         var elid = dds[dd].dragElId;
48683                         dds[dd].unreg();
48684                         Roo.get(elid).remove();
48685                     } else if(dds[dd].config.isTarget){
48686                         dds[dd].proxyTop.remove();
48687                         dds[dd].proxyBottom.remove();
48688                         dds[dd].unreg();
48689                     }
48690                     if(Roo.dd.DDM.locationCache[dd]){
48691                         delete Roo.dd.DDM.locationCache[dd];
48692                     }
48693                 }
48694                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48695             }
48696         }
48697         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48698         this.bind(null, null);
48699         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48700     },
48701
48702     handleLockChange : function(){
48703         this.refresh(true);
48704     },
48705
48706     onDenyColumnLock : function(){
48707
48708     },
48709
48710     onDenyColumnHide : function(){
48711
48712     },
48713
48714     handleHdMenuClick : function(item){
48715         var index = this.hdCtxIndex;
48716         var cm = this.cm, ds = this.ds;
48717         switch(item.id){
48718             case "asc":
48719                 ds.sort(cm.getDataIndex(index), "ASC");
48720                 break;
48721             case "desc":
48722                 ds.sort(cm.getDataIndex(index), "DESC");
48723                 break;
48724             case "lock":
48725                 var lc = cm.getLockedCount();
48726                 if(cm.getColumnCount(true) <= lc+1){
48727                     this.onDenyColumnLock();
48728                     return;
48729                 }
48730                 if(lc != index){
48731                     cm.setLocked(index, true, true);
48732                     cm.moveColumn(index, lc);
48733                     this.grid.fireEvent("columnmove", index, lc);
48734                 }else{
48735                     cm.setLocked(index, true);
48736                 }
48737             break;
48738             case "unlock":
48739                 var lc = cm.getLockedCount();
48740                 if((lc-1) != index){
48741                     cm.setLocked(index, false, true);
48742                     cm.moveColumn(index, lc-1);
48743                     this.grid.fireEvent("columnmove", index, lc-1);
48744                 }else{
48745                     cm.setLocked(index, false);
48746                 }
48747             break;
48748             default:
48749                 index = cm.getIndexById(item.id.substr(4));
48750                 if(index != -1){
48751                     if(item.checked && cm.getColumnCount(true) <= 1){
48752                         this.onDenyColumnHide();
48753                         return false;
48754                     }
48755                     cm.setHidden(index, item.checked);
48756                 }
48757         }
48758         return true;
48759     },
48760
48761     beforeColMenuShow : function(){
48762         var cm = this.cm,  colCount = cm.getColumnCount();
48763         this.colMenu.removeAll();
48764         for(var i = 0; i < colCount; i++){
48765             this.colMenu.add(new Roo.menu.CheckItem({
48766                 id: "col-"+cm.getColumnId(i),
48767                 text: cm.getColumnHeader(i),
48768                 checked: !cm.isHidden(i),
48769                 hideOnClick:false
48770             }));
48771         }
48772     },
48773
48774     handleHdCtx : function(g, index, e){
48775         e.stopEvent();
48776         var hd = this.getHeaderCell(index);
48777         this.hdCtxIndex = index;
48778         var ms = this.hmenu.items, cm = this.cm;
48779         ms.get("asc").setDisabled(!cm.isSortable(index));
48780         ms.get("desc").setDisabled(!cm.isSortable(index));
48781         if(this.grid.enableColLock !== false){
48782             ms.get("lock").setDisabled(cm.isLocked(index));
48783             ms.get("unlock").setDisabled(!cm.isLocked(index));
48784         }
48785         this.hmenu.show(hd, "tl-bl");
48786     },
48787
48788     handleHdOver : function(e){
48789         var hd = this.findHeaderCell(e.getTarget());
48790         if(hd && !this.headersDisabled){
48791             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48792                this.fly(hd).addClass("x-grid-hd-over");
48793             }
48794         }
48795     },
48796
48797     handleHdOut : function(e){
48798         var hd = this.findHeaderCell(e.getTarget());
48799         if(hd){
48800             this.fly(hd).removeClass("x-grid-hd-over");
48801         }
48802     },
48803
48804     handleSplitDblClick : function(e, t){
48805         var i = this.getCellIndex(t);
48806         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48807             this.autoSizeColumn(i, true);
48808             this.layout();
48809         }
48810     },
48811
48812     render : function(){
48813
48814         var cm = this.cm;
48815         var colCount = cm.getColumnCount();
48816
48817         if(this.grid.monitorWindowResize === true){
48818             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48819         }
48820         var header = this.renderHeaders();
48821         var body = this.templates.body.apply({rows:""});
48822         var html = this.templates.master.apply({
48823             lockedBody: body,
48824             body: body,
48825             lockedHeader: header[0],
48826             header: header[1]
48827         });
48828
48829         //this.updateColumns();
48830
48831         this.grid.getGridEl().dom.innerHTML = html;
48832
48833         this.initElements();
48834         
48835         // a kludge to fix the random scolling effect in webkit
48836         this.el.on("scroll", function() {
48837             this.el.dom.scrollTop=0; // hopefully not recursive..
48838         },this);
48839
48840         this.scroller.on("scroll", this.handleScroll, this);
48841         this.lockedBody.on("mousewheel", this.handleWheel, this);
48842         this.mainBody.on("mousewheel", this.handleWheel, this);
48843
48844         this.mainHd.on("mouseover", this.handleHdOver, this);
48845         this.mainHd.on("mouseout", this.handleHdOut, this);
48846         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48847                 {delegate: "."+this.splitClass});
48848
48849         this.lockedHd.on("mouseover", this.handleHdOver, this);
48850         this.lockedHd.on("mouseout", this.handleHdOut, this);
48851         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48852                 {delegate: "."+this.splitClass});
48853
48854         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48855             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48856         }
48857
48858         this.updateSplitters();
48859
48860         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48861             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48862             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48863         }
48864
48865         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48866             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48867             this.hmenu.add(
48868                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48869                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48870             );
48871             if(this.grid.enableColLock !== false){
48872                 this.hmenu.add('-',
48873                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48874                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48875                 );
48876             }
48877             if(this.grid.enableColumnHide !== false){
48878
48879                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48880                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48881                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48882
48883                 this.hmenu.add('-',
48884                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48885                 );
48886             }
48887             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48888
48889             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48890         }
48891
48892         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48893             this.dd = new Roo.grid.GridDragZone(this.grid, {
48894                 ddGroup : this.grid.ddGroup || 'GridDD'
48895             });
48896         }
48897
48898         /*
48899         for(var i = 0; i < colCount; i++){
48900             if(cm.isHidden(i)){
48901                 this.hideColumn(i);
48902             }
48903             if(cm.config[i].align){
48904                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48905                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48906             }
48907         }*/
48908         
48909         this.updateHeaderSortState();
48910
48911         this.beforeInitialResize();
48912         this.layout(true);
48913
48914         // two part rendering gives faster view to the user
48915         this.renderPhase2.defer(1, this);
48916     },
48917
48918     renderPhase2 : function(){
48919         // render the rows now
48920         this.refresh();
48921         if(this.grid.autoSizeColumns){
48922             this.autoSizeColumns();
48923         }
48924     },
48925
48926     beforeInitialResize : function(){
48927
48928     },
48929
48930     onColumnSplitterMoved : function(i, w){
48931         this.userResized = true;
48932         var cm = this.grid.colModel;
48933         cm.setColumnWidth(i, w, true);
48934         var cid = cm.getColumnId(i);
48935         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48936         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48937         this.updateSplitters();
48938         this.layout();
48939         this.grid.fireEvent("columnresize", i, w);
48940     },
48941
48942     syncRowHeights : function(startIndex, endIndex){
48943         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48944             startIndex = startIndex || 0;
48945             var mrows = this.getBodyTable().rows;
48946             var lrows = this.getLockedTable().rows;
48947             var len = mrows.length-1;
48948             endIndex = Math.min(endIndex || len, len);
48949             for(var i = startIndex; i <= endIndex; i++){
48950                 var m = mrows[i], l = lrows[i];
48951                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48952                 m.style.height = l.style.height = h + "px";
48953             }
48954         }
48955     },
48956
48957     layout : function(initialRender, is2ndPass){
48958         var g = this.grid;
48959         var auto = g.autoHeight;
48960         var scrollOffset = 16;
48961         var c = g.getGridEl(), cm = this.cm,
48962                 expandCol = g.autoExpandColumn,
48963                 gv = this;
48964         //c.beginMeasure();
48965
48966         if(!c.dom.offsetWidth){ // display:none?
48967             if(initialRender){
48968                 this.lockedWrap.show();
48969                 this.mainWrap.show();
48970             }
48971             return;
48972         }
48973
48974         var hasLock = this.cm.isLocked(0);
48975
48976         var tbh = this.headerPanel.getHeight();
48977         var bbh = this.footerPanel.getHeight();
48978
48979         if(auto){
48980             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48981             var newHeight = ch + c.getBorderWidth("tb");
48982             if(g.maxHeight){
48983                 newHeight = Math.min(g.maxHeight, newHeight);
48984             }
48985             c.setHeight(newHeight);
48986         }
48987
48988         if(g.autoWidth){
48989             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48990         }
48991
48992         var s = this.scroller;
48993
48994         var csize = c.getSize(true);
48995
48996         this.el.setSize(csize.width, csize.height);
48997
48998         this.headerPanel.setWidth(csize.width);
48999         this.footerPanel.setWidth(csize.width);
49000
49001         var hdHeight = this.mainHd.getHeight();
49002         var vw = csize.width;
49003         var vh = csize.height - (tbh + bbh);
49004
49005         s.setSize(vw, vh);
49006
49007         var bt = this.getBodyTable();
49008         var ltWidth = hasLock ?
49009                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49010
49011         var scrollHeight = bt.offsetHeight;
49012         var scrollWidth = ltWidth + bt.offsetWidth;
49013         var vscroll = false, hscroll = false;
49014
49015         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49016
49017         var lw = this.lockedWrap, mw = this.mainWrap;
49018         var lb = this.lockedBody, mb = this.mainBody;
49019
49020         setTimeout(function(){
49021             var t = s.dom.offsetTop;
49022             var w = s.dom.clientWidth,
49023                 h = s.dom.clientHeight;
49024
49025             lw.setTop(t);
49026             lw.setSize(ltWidth, h);
49027
49028             mw.setLeftTop(ltWidth, t);
49029             mw.setSize(w-ltWidth, h);
49030
49031             lb.setHeight(h-hdHeight);
49032             mb.setHeight(h-hdHeight);
49033
49034             if(is2ndPass !== true && !gv.userResized && expandCol){
49035                 // high speed resize without full column calculation
49036                 
49037                 var ci = cm.getIndexById(expandCol);
49038                 if (ci < 0) {
49039                     ci = cm.findColumnIndex(expandCol);
49040                 }
49041                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49042                 var expandId = cm.getColumnId(ci);
49043                 var  tw = cm.getTotalWidth(false);
49044                 var currentWidth = cm.getColumnWidth(ci);
49045                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49046                 if(currentWidth != cw){
49047                     cm.setColumnWidth(ci, cw, true);
49048                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49049                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49050                     gv.updateSplitters();
49051                     gv.layout(false, true);
49052                 }
49053             }
49054
49055             if(initialRender){
49056                 lw.show();
49057                 mw.show();
49058             }
49059             //c.endMeasure();
49060         }, 10);
49061     },
49062
49063     onWindowResize : function(){
49064         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49065             return;
49066         }
49067         this.layout();
49068     },
49069
49070     appendFooter : function(parentEl){
49071         return null;
49072     },
49073
49074     sortAscText : "Sort Ascending",
49075     sortDescText : "Sort Descending",
49076     lockText : "Lock Column",
49077     unlockText : "Unlock Column",
49078     columnsText : "Columns"
49079 });
49080
49081
49082 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49083     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49084     this.proxy.el.addClass('x-grid3-col-dd');
49085 };
49086
49087 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49088     handleMouseDown : function(e){
49089
49090     },
49091
49092     callHandleMouseDown : function(e){
49093         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49094     }
49095 });
49096 /*
49097  * Based on:
49098  * Ext JS Library 1.1.1
49099  * Copyright(c) 2006-2007, Ext JS, LLC.
49100  *
49101  * Originally Released Under LGPL - original licence link has changed is not relivant.
49102  *
49103  * Fork - LGPL
49104  * <script type="text/javascript">
49105  */
49106  
49107 // private
49108 // This is a support class used internally by the Grid components
49109 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49110     this.grid = grid;
49111     this.view = grid.getView();
49112     this.proxy = this.view.resizeProxy;
49113     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49114         "gridSplitters" + this.grid.getGridEl().id, {
49115         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49116     });
49117     this.setHandleElId(Roo.id(hd));
49118     this.setOuterHandleElId(Roo.id(hd2));
49119     this.scroll = false;
49120 };
49121 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49122     fly: Roo.Element.fly,
49123
49124     b4StartDrag : function(x, y){
49125         this.view.headersDisabled = true;
49126         this.proxy.setHeight(this.view.mainWrap.getHeight());
49127         var w = this.cm.getColumnWidth(this.cellIndex);
49128         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49129         this.resetConstraints();
49130         this.setXConstraint(minw, 1000);
49131         this.setYConstraint(0, 0);
49132         this.minX = x - minw;
49133         this.maxX = x + 1000;
49134         this.startPos = x;
49135         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49136     },
49137
49138
49139     handleMouseDown : function(e){
49140         ev = Roo.EventObject.setEvent(e);
49141         var t = this.fly(ev.getTarget());
49142         if(t.hasClass("x-grid-split")){
49143             this.cellIndex = this.view.getCellIndex(t.dom);
49144             this.split = t.dom;
49145             this.cm = this.grid.colModel;
49146             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49147                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49148             }
49149         }
49150     },
49151
49152     endDrag : function(e){
49153         this.view.headersDisabled = false;
49154         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49155         var diff = endX - this.startPos;
49156         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49157     },
49158
49159     autoOffset : function(){
49160         this.setDelta(0,0);
49161     }
49162 });/*
49163  * Based on:
49164  * Ext JS Library 1.1.1
49165  * Copyright(c) 2006-2007, Ext JS, LLC.
49166  *
49167  * Originally Released Under LGPL - original licence link has changed is not relivant.
49168  *
49169  * Fork - LGPL
49170  * <script type="text/javascript">
49171  */
49172  
49173 // private
49174 // This is a support class used internally by the Grid components
49175 Roo.grid.GridDragZone = function(grid, config){
49176     this.view = grid.getView();
49177     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49178     if(this.view.lockedBody){
49179         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49180         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49181     }
49182     this.scroll = false;
49183     this.grid = grid;
49184     this.ddel = document.createElement('div');
49185     this.ddel.className = 'x-grid-dd-wrap';
49186 };
49187
49188 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49189     ddGroup : "GridDD",
49190
49191     getDragData : function(e){
49192         var t = Roo.lib.Event.getTarget(e);
49193         var rowIndex = this.view.findRowIndex(t);
49194         if(rowIndex !== false){
49195             var sm = this.grid.selModel;
49196             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49197               //  sm.mouseDown(e, t);
49198             //}
49199             if (e.hasModifier()){
49200                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49201             }
49202             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49203         }
49204         return false;
49205     },
49206
49207     onInitDrag : function(e){
49208         var data = this.dragData;
49209         this.ddel.innerHTML = this.grid.getDragDropText();
49210         this.proxy.update(this.ddel);
49211         // fire start drag?
49212     },
49213
49214     afterRepair : function(){
49215         this.dragging = false;
49216     },
49217
49218     getRepairXY : function(e, data){
49219         return false;
49220     },
49221
49222     onEndDrag : function(data, e){
49223         // fire end drag?
49224     },
49225
49226     onValidDrop : function(dd, e, id){
49227         // fire drag drop?
49228         this.hideProxy();
49229     },
49230
49231     beforeInvalidDrop : function(e, id){
49232
49233     }
49234 });/*
49235  * Based on:
49236  * Ext JS Library 1.1.1
49237  * Copyright(c) 2006-2007, Ext JS, LLC.
49238  *
49239  * Originally Released Under LGPL - original licence link has changed is not relivant.
49240  *
49241  * Fork - LGPL
49242  * <script type="text/javascript">
49243  */
49244  
49245
49246 /**
49247  * @class Roo.grid.ColumnModel
49248  * @extends Roo.util.Observable
49249  * This is the default implementation of a ColumnModel used by the Grid. It defines
49250  * the columns in the grid.
49251  * <br>Usage:<br>
49252  <pre><code>
49253  var colModel = new Roo.grid.ColumnModel([
49254         {header: "Ticker", width: 60, sortable: true, locked: true},
49255         {header: "Company Name", width: 150, sortable: true},
49256         {header: "Market Cap.", width: 100, sortable: true},
49257         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49258         {header: "Employees", width: 100, sortable: true, resizable: false}
49259  ]);
49260  </code></pre>
49261  * <p>
49262  
49263  * The config options listed for this class are options which may appear in each
49264  * individual column definition.
49265  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49266  * @constructor
49267  * @param {Object} config An Array of column config objects. See this class's
49268  * config objects for details.
49269 */
49270 Roo.grid.ColumnModel = function(config){
49271         /**
49272      * The config passed into the constructor
49273      */
49274     this.config = config;
49275     this.lookup = {};
49276
49277     // if no id, create one
49278     // if the column does not have a dataIndex mapping,
49279     // map it to the order it is in the config
49280     for(var i = 0, len = config.length; i < len; i++){
49281         var c = config[i];
49282         if(typeof c.dataIndex == "undefined"){
49283             c.dataIndex = i;
49284         }
49285         if(typeof c.renderer == "string"){
49286             c.renderer = Roo.util.Format[c.renderer];
49287         }
49288         if(typeof c.id == "undefined"){
49289             c.id = Roo.id();
49290         }
49291         if(c.editor && c.editor.xtype){
49292             c.editor  = Roo.factory(c.editor, Roo.grid);
49293         }
49294         if(c.editor && c.editor.isFormField){
49295             c.editor = new Roo.grid.GridEditor(c.editor);
49296         }
49297         this.lookup[c.id] = c;
49298     }
49299
49300     /**
49301      * The width of columns which have no width specified (defaults to 100)
49302      * @type Number
49303      */
49304     this.defaultWidth = 100;
49305
49306     /**
49307      * Default sortable of columns which have no sortable specified (defaults to false)
49308      * @type Boolean
49309      */
49310     this.defaultSortable = false;
49311
49312     this.addEvents({
49313         /**
49314              * @event widthchange
49315              * Fires when the width of a column changes.
49316              * @param {ColumnModel} this
49317              * @param {Number} columnIndex The column index
49318              * @param {Number} newWidth The new width
49319              */
49320             "widthchange": true,
49321         /**
49322              * @event headerchange
49323              * Fires when the text of a header changes.
49324              * @param {ColumnModel} this
49325              * @param {Number} columnIndex The column index
49326              * @param {Number} newText The new header text
49327              */
49328             "headerchange": true,
49329         /**
49330              * @event hiddenchange
49331              * Fires when a column is hidden or "unhidden".
49332              * @param {ColumnModel} this
49333              * @param {Number} columnIndex The column index
49334              * @param {Boolean} hidden true if hidden, false otherwise
49335              */
49336             "hiddenchange": true,
49337             /**
49338          * @event columnmoved
49339          * Fires when a column is moved.
49340          * @param {ColumnModel} this
49341          * @param {Number} oldIndex
49342          * @param {Number} newIndex
49343          */
49344         "columnmoved" : true,
49345         /**
49346          * @event columlockchange
49347          * Fires when a column's locked state is changed
49348          * @param {ColumnModel} this
49349          * @param {Number} colIndex
49350          * @param {Boolean} locked true if locked
49351          */
49352         "columnlockchange" : true
49353     });
49354     Roo.grid.ColumnModel.superclass.constructor.call(this);
49355 };
49356 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49357     /**
49358      * @cfg {String} header The header text to display in the Grid view.
49359      */
49360     /**
49361      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49362      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49363      * specified, the column's index is used as an index into the Record's data Array.
49364      */
49365     /**
49366      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49367      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49368      */
49369     /**
49370      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49371      * Defaults to the value of the {@link #defaultSortable} property.
49372      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49373      */
49374     /**
49375      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49376      */
49377     /**
49378      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49379      */
49380     /**
49381      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49382      */
49383     /**
49384      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49385      */
49386     /**
49387      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49388      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49389      * default renderer uses the raw data value.
49390      */
49391        /**
49392      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49393      */
49394     /**
49395      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49396      */
49397
49398     /**
49399      * Returns the id of the column at the specified index.
49400      * @param {Number} index The column index
49401      * @return {String} the id
49402      */
49403     getColumnId : function(index){
49404         return this.config[index].id;
49405     },
49406
49407     /**
49408      * Returns the column for a specified id.
49409      * @param {String} id The column id
49410      * @return {Object} the column
49411      */
49412     getColumnById : function(id){
49413         return this.lookup[id];
49414     },
49415
49416     
49417     /**
49418      * Returns the column for a specified dataIndex.
49419      * @param {String} dataIndex The column dataIndex
49420      * @return {Object|Boolean} the column or false if not found
49421      */
49422     getColumnByDataIndex: function(dataIndex){
49423         var index = this.findColumnIndex(dataIndex);
49424         return index > -1 ? this.config[index] : false;
49425     },
49426     
49427     /**
49428      * Returns the index for a specified column id.
49429      * @param {String} id The column id
49430      * @return {Number} the index, or -1 if not found
49431      */
49432     getIndexById : function(id){
49433         for(var i = 0, len = this.config.length; i < len; i++){
49434             if(this.config[i].id == id){
49435                 return i;
49436             }
49437         }
49438         return -1;
49439     },
49440     
49441     /**
49442      * Returns the index for a specified column dataIndex.
49443      * @param {String} dataIndex The column dataIndex
49444      * @return {Number} the index, or -1 if not found
49445      */
49446     
49447     findColumnIndex : function(dataIndex){
49448         for(var i = 0, len = this.config.length; i < len; i++){
49449             if(this.config[i].dataIndex == dataIndex){
49450                 return i;
49451             }
49452         }
49453         return -1;
49454     },
49455     
49456     
49457     moveColumn : function(oldIndex, newIndex){
49458         var c = this.config[oldIndex];
49459         this.config.splice(oldIndex, 1);
49460         this.config.splice(newIndex, 0, c);
49461         this.dataMap = null;
49462         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49463     },
49464
49465     isLocked : function(colIndex){
49466         return this.config[colIndex].locked === true;
49467     },
49468
49469     setLocked : function(colIndex, value, suppressEvent){
49470         if(this.isLocked(colIndex) == value){
49471             return;
49472         }
49473         this.config[colIndex].locked = value;
49474         if(!suppressEvent){
49475             this.fireEvent("columnlockchange", this, colIndex, value);
49476         }
49477     },
49478
49479     getTotalLockedWidth : function(){
49480         var totalWidth = 0;
49481         for(var i = 0; i < this.config.length; i++){
49482             if(this.isLocked(i) && !this.isHidden(i)){
49483                 this.totalWidth += this.getColumnWidth(i);
49484             }
49485         }
49486         return totalWidth;
49487     },
49488
49489     getLockedCount : function(){
49490         for(var i = 0, len = this.config.length; i < len; i++){
49491             if(!this.isLocked(i)){
49492                 return i;
49493             }
49494         }
49495     },
49496
49497     /**
49498      * Returns the number of columns.
49499      * @return {Number}
49500      */
49501     getColumnCount : function(visibleOnly){
49502         if(visibleOnly === true){
49503             var c = 0;
49504             for(var i = 0, len = this.config.length; i < len; i++){
49505                 if(!this.isHidden(i)){
49506                     c++;
49507                 }
49508             }
49509             return c;
49510         }
49511         return this.config.length;
49512     },
49513
49514     /**
49515      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49516      * @param {Function} fn
49517      * @param {Object} scope (optional)
49518      * @return {Array} result
49519      */
49520     getColumnsBy : function(fn, scope){
49521         var r = [];
49522         for(var i = 0, len = this.config.length; i < len; i++){
49523             var c = this.config[i];
49524             if(fn.call(scope||this, c, i) === true){
49525                 r[r.length] = c;
49526             }
49527         }
49528         return r;
49529     },
49530
49531     /**
49532      * Returns true if the specified column is sortable.
49533      * @param {Number} col The column index
49534      * @return {Boolean}
49535      */
49536     isSortable : function(col){
49537         if(typeof this.config[col].sortable == "undefined"){
49538             return this.defaultSortable;
49539         }
49540         return this.config[col].sortable;
49541     },
49542
49543     /**
49544      * Returns the rendering (formatting) function defined for the column.
49545      * @param {Number} col The column index.
49546      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49547      */
49548     getRenderer : function(col){
49549         if(!this.config[col].renderer){
49550             return Roo.grid.ColumnModel.defaultRenderer;
49551         }
49552         return this.config[col].renderer;
49553     },
49554
49555     /**
49556      * Sets the rendering (formatting) function for a column.
49557      * @param {Number} col The column index
49558      * @param {Function} fn The function to use to process the cell's raw data
49559      * to return HTML markup for the grid view. The render function is called with
49560      * the following parameters:<ul>
49561      * <li>Data value.</li>
49562      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49563      * <li>css A CSS style string to apply to the table cell.</li>
49564      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49565      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49566      * <li>Row index</li>
49567      * <li>Column index</li>
49568      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49569      */
49570     setRenderer : function(col, fn){
49571         this.config[col].renderer = fn;
49572     },
49573
49574     /**
49575      * Returns the width for the specified column.
49576      * @param {Number} col The column index
49577      * @return {Number}
49578      */
49579     getColumnWidth : function(col){
49580         return this.config[col].width * 1 || this.defaultWidth;
49581     },
49582
49583     /**
49584      * Sets the width for a column.
49585      * @param {Number} col The column index
49586      * @param {Number} width The new width
49587      */
49588     setColumnWidth : function(col, width, suppressEvent){
49589         this.config[col].width = width;
49590         this.totalWidth = null;
49591         if(!suppressEvent){
49592              this.fireEvent("widthchange", this, col, width);
49593         }
49594     },
49595
49596     /**
49597      * Returns the total width of all columns.
49598      * @param {Boolean} includeHidden True to include hidden column widths
49599      * @return {Number}
49600      */
49601     getTotalWidth : function(includeHidden){
49602         if(!this.totalWidth){
49603             this.totalWidth = 0;
49604             for(var i = 0, len = this.config.length; i < len; i++){
49605                 if(includeHidden || !this.isHidden(i)){
49606                     this.totalWidth += this.getColumnWidth(i);
49607                 }
49608             }
49609         }
49610         return this.totalWidth;
49611     },
49612
49613     /**
49614      * Returns the header for the specified column.
49615      * @param {Number} col The column index
49616      * @return {String}
49617      */
49618     getColumnHeader : function(col){
49619         return this.config[col].header;
49620     },
49621
49622     /**
49623      * Sets the header for a column.
49624      * @param {Number} col The column index
49625      * @param {String} header The new header
49626      */
49627     setColumnHeader : function(col, header){
49628         this.config[col].header = header;
49629         this.fireEvent("headerchange", this, col, header);
49630     },
49631
49632     /**
49633      * Returns the tooltip for the specified column.
49634      * @param {Number} col The column index
49635      * @return {String}
49636      */
49637     getColumnTooltip : function(col){
49638             return this.config[col].tooltip;
49639     },
49640     /**
49641      * Sets the tooltip for a column.
49642      * @param {Number} col The column index
49643      * @param {String} tooltip The new tooltip
49644      */
49645     setColumnTooltip : function(col, tooltip){
49646             this.config[col].tooltip = tooltip;
49647     },
49648
49649     /**
49650      * Returns the dataIndex for the specified column.
49651      * @param {Number} col The column index
49652      * @return {Number}
49653      */
49654     getDataIndex : function(col){
49655         return this.config[col].dataIndex;
49656     },
49657
49658     /**
49659      * Sets the dataIndex for a column.
49660      * @param {Number} col The column index
49661      * @param {Number} dataIndex The new dataIndex
49662      */
49663     setDataIndex : function(col, dataIndex){
49664         this.config[col].dataIndex = dataIndex;
49665     },
49666
49667     
49668     
49669     /**
49670      * Returns true if the cell is editable.
49671      * @param {Number} colIndex The column index
49672      * @param {Number} rowIndex The row index
49673      * @return {Boolean}
49674      */
49675     isCellEditable : function(colIndex, rowIndex){
49676         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49677     },
49678
49679     /**
49680      * Returns the editor defined for the cell/column.
49681      * return false or null to disable editing.
49682      * @param {Number} colIndex The column index
49683      * @param {Number} rowIndex The row index
49684      * @return {Object}
49685      */
49686     getCellEditor : function(colIndex, rowIndex){
49687         return this.config[colIndex].editor;
49688     },
49689
49690     /**
49691      * Sets if a column is editable.
49692      * @param {Number} col The column index
49693      * @param {Boolean} editable True if the column is editable
49694      */
49695     setEditable : function(col, editable){
49696         this.config[col].editable = editable;
49697     },
49698
49699
49700     /**
49701      * Returns true if the column is hidden.
49702      * @param {Number} colIndex The column index
49703      * @return {Boolean}
49704      */
49705     isHidden : function(colIndex){
49706         return this.config[colIndex].hidden;
49707     },
49708
49709
49710     /**
49711      * Returns true if the column width cannot be changed
49712      */
49713     isFixed : function(colIndex){
49714         return this.config[colIndex].fixed;
49715     },
49716
49717     /**
49718      * Returns true if the column can be resized
49719      * @return {Boolean}
49720      */
49721     isResizable : function(colIndex){
49722         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49723     },
49724     /**
49725      * Sets if a column is hidden.
49726      * @param {Number} colIndex The column index
49727      * @param {Boolean} hidden True if the column is hidden
49728      */
49729     setHidden : function(colIndex, hidden){
49730         this.config[colIndex].hidden = hidden;
49731         this.totalWidth = null;
49732         this.fireEvent("hiddenchange", this, colIndex, hidden);
49733     },
49734
49735     /**
49736      * Sets the editor for a column.
49737      * @param {Number} col The column index
49738      * @param {Object} editor The editor object
49739      */
49740     setEditor : function(col, editor){
49741         this.config[col].editor = editor;
49742     }
49743 });
49744
49745 Roo.grid.ColumnModel.defaultRenderer = function(value){
49746         if(typeof value == "string" && value.length < 1){
49747             return "&#160;";
49748         }
49749         return value;
49750 };
49751
49752 // Alias for backwards compatibility
49753 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49754 /*
49755  * Based on:
49756  * Ext JS Library 1.1.1
49757  * Copyright(c) 2006-2007, Ext JS, LLC.
49758  *
49759  * Originally Released Under LGPL - original licence link has changed is not relivant.
49760  *
49761  * Fork - LGPL
49762  * <script type="text/javascript">
49763  */
49764
49765 /**
49766  * @class Roo.grid.AbstractSelectionModel
49767  * @extends Roo.util.Observable
49768  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49769  * implemented by descendant classes.  This class should not be directly instantiated.
49770  * @constructor
49771  */
49772 Roo.grid.AbstractSelectionModel = function(){
49773     this.locked = false;
49774     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49775 };
49776
49777 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49778     /** @ignore Called by the grid automatically. Do not call directly. */
49779     init : function(grid){
49780         this.grid = grid;
49781         this.initEvents();
49782     },
49783
49784     /**
49785      * Locks the selections.
49786      */
49787     lock : function(){
49788         this.locked = true;
49789     },
49790
49791     /**
49792      * Unlocks the selections.
49793      */
49794     unlock : function(){
49795         this.locked = false;
49796     },
49797
49798     /**
49799      * Returns true if the selections are locked.
49800      * @return {Boolean}
49801      */
49802     isLocked : function(){
49803         return this.locked;
49804     }
49805 });/*
49806  * Based on:
49807  * Ext JS Library 1.1.1
49808  * Copyright(c) 2006-2007, Ext JS, LLC.
49809  *
49810  * Originally Released Under LGPL - original licence link has changed is not relivant.
49811  *
49812  * Fork - LGPL
49813  * <script type="text/javascript">
49814  */
49815 /**
49816  * @extends Roo.grid.AbstractSelectionModel
49817  * @class Roo.grid.RowSelectionModel
49818  * The default SelectionModel used by {@link Roo.grid.Grid}.
49819  * It supports multiple selections and keyboard selection/navigation. 
49820  * @constructor
49821  * @param {Object} config
49822  */
49823 Roo.grid.RowSelectionModel = function(config){
49824     Roo.apply(this, config);
49825     this.selections = new Roo.util.MixedCollection(false, function(o){
49826         return o.id;
49827     });
49828
49829     this.last = false;
49830     this.lastActive = false;
49831
49832     this.addEvents({
49833         /**
49834              * @event selectionchange
49835              * Fires when the selection changes
49836              * @param {SelectionModel} this
49837              */
49838             "selectionchange" : true,
49839         /**
49840              * @event afterselectionchange
49841              * Fires after the selection changes (eg. by key press or clicking)
49842              * @param {SelectionModel} this
49843              */
49844             "afterselectionchange" : true,
49845         /**
49846              * @event beforerowselect
49847              * Fires when a row is selected being selected, return false to cancel.
49848              * @param {SelectionModel} this
49849              * @param {Number} rowIndex The selected index
49850              * @param {Boolean} keepExisting False if other selections will be cleared
49851              */
49852             "beforerowselect" : true,
49853         /**
49854              * @event rowselect
49855              * Fires when a row is selected.
49856              * @param {SelectionModel} this
49857              * @param {Number} rowIndex The selected index
49858              * @param {Roo.data.Record} r The record
49859              */
49860             "rowselect" : true,
49861         /**
49862              * @event rowdeselect
49863              * Fires when a row is deselected.
49864              * @param {SelectionModel} this
49865              * @param {Number} rowIndex The selected index
49866              */
49867         "rowdeselect" : true
49868     });
49869     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49870     this.locked = false;
49871 };
49872
49873 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49874     /**
49875      * @cfg {Boolean} singleSelect
49876      * True to allow selection of only one row at a time (defaults to false)
49877      */
49878     singleSelect : false,
49879
49880     // private
49881     initEvents : function(){
49882
49883         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49884             this.grid.on("mousedown", this.handleMouseDown, this);
49885         }else{ // allow click to work like normal
49886             this.grid.on("rowclick", this.handleDragableRowClick, this);
49887         }
49888
49889         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49890             "up" : function(e){
49891                 if(!e.shiftKey){
49892                     this.selectPrevious(e.shiftKey);
49893                 }else if(this.last !== false && this.lastActive !== false){
49894                     var last = this.last;
49895                     this.selectRange(this.last,  this.lastActive-1);
49896                     this.grid.getView().focusRow(this.lastActive);
49897                     if(last !== false){
49898                         this.last = last;
49899                     }
49900                 }else{
49901                     this.selectFirstRow();
49902                 }
49903                 this.fireEvent("afterselectionchange", this);
49904             },
49905             "down" : function(e){
49906                 if(!e.shiftKey){
49907                     this.selectNext(e.shiftKey);
49908                 }else if(this.last !== false && this.lastActive !== false){
49909                     var last = this.last;
49910                     this.selectRange(this.last,  this.lastActive+1);
49911                     this.grid.getView().focusRow(this.lastActive);
49912                     if(last !== false){
49913                         this.last = last;
49914                     }
49915                 }else{
49916                     this.selectFirstRow();
49917                 }
49918                 this.fireEvent("afterselectionchange", this);
49919             },
49920             scope: this
49921         });
49922
49923         var view = this.grid.view;
49924         view.on("refresh", this.onRefresh, this);
49925         view.on("rowupdated", this.onRowUpdated, this);
49926         view.on("rowremoved", this.onRemove, this);
49927     },
49928
49929     // private
49930     onRefresh : function(){
49931         var ds = this.grid.dataSource, i, v = this.grid.view;
49932         var s = this.selections;
49933         s.each(function(r){
49934             if((i = ds.indexOfId(r.id)) != -1){
49935                 v.onRowSelect(i);
49936             }else{
49937                 s.remove(r);
49938             }
49939         });
49940     },
49941
49942     // private
49943     onRemove : function(v, index, r){
49944         this.selections.remove(r);
49945     },
49946
49947     // private
49948     onRowUpdated : function(v, index, r){
49949         if(this.isSelected(r)){
49950             v.onRowSelect(index);
49951         }
49952     },
49953
49954     /**
49955      * Select records.
49956      * @param {Array} records The records to select
49957      * @param {Boolean} keepExisting (optional) True to keep existing selections
49958      */
49959     selectRecords : function(records, keepExisting){
49960         if(!keepExisting){
49961             this.clearSelections();
49962         }
49963         var ds = this.grid.dataSource;
49964         for(var i = 0, len = records.length; i < len; i++){
49965             this.selectRow(ds.indexOf(records[i]), true);
49966         }
49967     },
49968
49969     /**
49970      * Gets the number of selected rows.
49971      * @return {Number}
49972      */
49973     getCount : function(){
49974         return this.selections.length;
49975     },
49976
49977     /**
49978      * Selects the first row in the grid.
49979      */
49980     selectFirstRow : function(){
49981         this.selectRow(0);
49982     },
49983
49984     /**
49985      * Select the last row.
49986      * @param {Boolean} keepExisting (optional) True to keep existing selections
49987      */
49988     selectLastRow : function(keepExisting){
49989         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49990     },
49991
49992     /**
49993      * Selects the row immediately following the last selected row.
49994      * @param {Boolean} keepExisting (optional) True to keep existing selections
49995      */
49996     selectNext : function(keepExisting){
49997         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49998             this.selectRow(this.last+1, keepExisting);
49999             this.grid.getView().focusRow(this.last);
50000         }
50001     },
50002
50003     /**
50004      * Selects the row that precedes the last selected row.
50005      * @param {Boolean} keepExisting (optional) True to keep existing selections
50006      */
50007     selectPrevious : function(keepExisting){
50008         if(this.last){
50009             this.selectRow(this.last-1, keepExisting);
50010             this.grid.getView().focusRow(this.last);
50011         }
50012     },
50013
50014     /**
50015      * Returns the selected records
50016      * @return {Array} Array of selected records
50017      */
50018     getSelections : function(){
50019         return [].concat(this.selections.items);
50020     },
50021
50022     /**
50023      * Returns the first selected record.
50024      * @return {Record}
50025      */
50026     getSelected : function(){
50027         return this.selections.itemAt(0);
50028     },
50029
50030
50031     /**
50032      * Clears all selections.
50033      */
50034     clearSelections : function(fast){
50035         if(this.locked) return;
50036         if(fast !== true){
50037             var ds = this.grid.dataSource;
50038             var s = this.selections;
50039             s.each(function(r){
50040                 this.deselectRow(ds.indexOfId(r.id));
50041             }, this);
50042             s.clear();
50043         }else{
50044             this.selections.clear();
50045         }
50046         this.last = false;
50047     },
50048
50049
50050     /**
50051      * Selects all rows.
50052      */
50053     selectAll : function(){
50054         if(this.locked) return;
50055         this.selections.clear();
50056         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50057             this.selectRow(i, true);
50058         }
50059     },
50060
50061     /**
50062      * Returns True if there is a selection.
50063      * @return {Boolean}
50064      */
50065     hasSelection : function(){
50066         return this.selections.length > 0;
50067     },
50068
50069     /**
50070      * Returns True if the specified row is selected.
50071      * @param {Number/Record} record The record or index of the record to check
50072      * @return {Boolean}
50073      */
50074     isSelected : function(index){
50075         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50076         return (r && this.selections.key(r.id) ? true : false);
50077     },
50078
50079     /**
50080      * Returns True if the specified record id is selected.
50081      * @param {String} id The id of record to check
50082      * @return {Boolean}
50083      */
50084     isIdSelected : function(id){
50085         return (this.selections.key(id) ? true : false);
50086     },
50087
50088     // private
50089     handleMouseDown : function(e, t){
50090         var view = this.grid.getView(), rowIndex;
50091         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50092             return;
50093         };
50094         if(e.shiftKey && this.last !== false){
50095             var last = this.last;
50096             this.selectRange(last, rowIndex, e.ctrlKey);
50097             this.last = last; // reset the last
50098             view.focusRow(rowIndex);
50099         }else{
50100             var isSelected = this.isSelected(rowIndex);
50101             if(e.button !== 0 && isSelected){
50102                 view.focusRow(rowIndex);
50103             }else if(e.ctrlKey && isSelected){
50104                 this.deselectRow(rowIndex);
50105             }else if(!isSelected){
50106                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50107                 view.focusRow(rowIndex);
50108             }
50109         }
50110         this.fireEvent("afterselectionchange", this);
50111     },
50112     // private
50113     handleDragableRowClick :  function(grid, rowIndex, e) 
50114     {
50115         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50116             this.selectRow(rowIndex, false);
50117             grid.view.focusRow(rowIndex);
50118              this.fireEvent("afterselectionchange", this);
50119         }
50120     },
50121     
50122     /**
50123      * Selects multiple rows.
50124      * @param {Array} rows Array of the indexes of the row to select
50125      * @param {Boolean} keepExisting (optional) True to keep existing selections
50126      */
50127     selectRows : function(rows, keepExisting){
50128         if(!keepExisting){
50129             this.clearSelections();
50130         }
50131         for(var i = 0, len = rows.length; i < len; i++){
50132             this.selectRow(rows[i], true);
50133         }
50134     },
50135
50136     /**
50137      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50138      * @param {Number} startRow The index of the first row in the range
50139      * @param {Number} endRow The index of the last row in the range
50140      * @param {Boolean} keepExisting (optional) True to retain existing selections
50141      */
50142     selectRange : function(startRow, endRow, keepExisting){
50143         if(this.locked) return;
50144         if(!keepExisting){
50145             this.clearSelections();
50146         }
50147         if(startRow <= endRow){
50148             for(var i = startRow; i <= endRow; i++){
50149                 this.selectRow(i, true);
50150             }
50151         }else{
50152             for(var i = startRow; i >= endRow; i--){
50153                 this.selectRow(i, true);
50154             }
50155         }
50156     },
50157
50158     /**
50159      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50160      * @param {Number} startRow The index of the first row in the range
50161      * @param {Number} endRow The index of the last row in the range
50162      */
50163     deselectRange : function(startRow, endRow, preventViewNotify){
50164         if(this.locked) return;
50165         for(var i = startRow; i <= endRow; i++){
50166             this.deselectRow(i, preventViewNotify);
50167         }
50168     },
50169
50170     /**
50171      * Selects a row.
50172      * @param {Number} row The index of the row to select
50173      * @param {Boolean} keepExisting (optional) True to keep existing selections
50174      */
50175     selectRow : function(index, keepExisting, preventViewNotify){
50176         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50177         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50178             if(!keepExisting || this.singleSelect){
50179                 this.clearSelections();
50180             }
50181             var r = this.grid.dataSource.getAt(index);
50182             this.selections.add(r);
50183             this.last = this.lastActive = index;
50184             if(!preventViewNotify){
50185                 this.grid.getView().onRowSelect(index);
50186             }
50187             this.fireEvent("rowselect", this, index, r);
50188             this.fireEvent("selectionchange", this);
50189         }
50190     },
50191
50192     /**
50193      * Deselects a row.
50194      * @param {Number} row The index of the row to deselect
50195      */
50196     deselectRow : function(index, preventViewNotify){
50197         if(this.locked) return;
50198         if(this.last == index){
50199             this.last = false;
50200         }
50201         if(this.lastActive == index){
50202             this.lastActive = false;
50203         }
50204         var r = this.grid.dataSource.getAt(index);
50205         this.selections.remove(r);
50206         if(!preventViewNotify){
50207             this.grid.getView().onRowDeselect(index);
50208         }
50209         this.fireEvent("rowdeselect", this, index);
50210         this.fireEvent("selectionchange", this);
50211     },
50212
50213     // private
50214     restoreLast : function(){
50215         if(this._last){
50216             this.last = this._last;
50217         }
50218     },
50219
50220     // private
50221     acceptsNav : function(row, col, cm){
50222         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50223     },
50224
50225     // private
50226     onEditorKey : function(field, e){
50227         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50228         if(k == e.TAB){
50229             e.stopEvent();
50230             ed.completeEdit();
50231             if(e.shiftKey){
50232                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50233             }else{
50234                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50235             }
50236         }else if(k == e.ENTER && !e.ctrlKey){
50237             e.stopEvent();
50238             ed.completeEdit();
50239             if(e.shiftKey){
50240                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50241             }else{
50242                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50243             }
50244         }else if(k == e.ESC){
50245             ed.cancelEdit();
50246         }
50247         if(newCell){
50248             g.startEditing(newCell[0], newCell[1]);
50249         }
50250     }
50251 });/*
50252  * Based on:
50253  * Ext JS Library 1.1.1
50254  * Copyright(c) 2006-2007, Ext JS, LLC.
50255  *
50256  * Originally Released Under LGPL - original licence link has changed is not relivant.
50257  *
50258  * Fork - LGPL
50259  * <script type="text/javascript">
50260  */
50261 /**
50262  * @class Roo.grid.CellSelectionModel
50263  * @extends Roo.grid.AbstractSelectionModel
50264  * This class provides the basic implementation for cell selection in a grid.
50265  * @constructor
50266  * @param {Object} config The object containing the configuration of this model.
50267  */
50268 Roo.grid.CellSelectionModel = function(config){
50269     Roo.apply(this, config);
50270
50271     this.selection = null;
50272
50273     this.addEvents({
50274         /**
50275              * @event beforerowselect
50276              * Fires before a cell is selected.
50277              * @param {SelectionModel} this
50278              * @param {Number} rowIndex The selected row index
50279              * @param {Number} colIndex The selected cell index
50280              */
50281             "beforecellselect" : true,
50282         /**
50283              * @event cellselect
50284              * Fires when a cell is selected.
50285              * @param {SelectionModel} this
50286              * @param {Number} rowIndex The selected row index
50287              * @param {Number} colIndex The selected cell index
50288              */
50289             "cellselect" : true,
50290         /**
50291              * @event selectionchange
50292              * Fires when the active selection changes.
50293              * @param {SelectionModel} this
50294              * @param {Object} selection null for no selection or an object (o) with two properties
50295                 <ul>
50296                 <li>o.record: the record object for the row the selection is in</li>
50297                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50298                 </ul>
50299              */
50300             "selectionchange" : true
50301     });
50302     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50303 };
50304
50305 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50306
50307     /** @ignore */
50308     initEvents : function(){
50309         this.grid.on("mousedown", this.handleMouseDown, this);
50310         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50311         var view = this.grid.view;
50312         view.on("refresh", this.onViewChange, this);
50313         view.on("rowupdated", this.onRowUpdated, this);
50314         view.on("beforerowremoved", this.clearSelections, this);
50315         view.on("beforerowsinserted", this.clearSelections, this);
50316         if(this.grid.isEditor){
50317             this.grid.on("beforeedit", this.beforeEdit,  this);
50318         }
50319     },
50320
50321         //private
50322     beforeEdit : function(e){
50323         this.select(e.row, e.column, false, true, e.record);
50324     },
50325
50326         //private
50327     onRowUpdated : function(v, index, r){
50328         if(this.selection && this.selection.record == r){
50329             v.onCellSelect(index, this.selection.cell[1]);
50330         }
50331     },
50332
50333         //private
50334     onViewChange : function(){
50335         this.clearSelections(true);
50336     },
50337
50338         /**
50339          * Returns the currently selected cell,.
50340          * @return {Array} The selected cell (row, column) or null if none selected.
50341          */
50342     getSelectedCell : function(){
50343         return this.selection ? this.selection.cell : null;
50344     },
50345
50346     /**
50347      * Clears all selections.
50348      * @param {Boolean} true to prevent the gridview from being notified about the change.
50349      */
50350     clearSelections : function(preventNotify){
50351         var s = this.selection;
50352         if(s){
50353             if(preventNotify !== true){
50354                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50355             }
50356             this.selection = null;
50357             this.fireEvent("selectionchange", this, null);
50358         }
50359     },
50360
50361     /**
50362      * Returns true if there is a selection.
50363      * @return {Boolean}
50364      */
50365     hasSelection : function(){
50366         return this.selection ? true : false;
50367     },
50368
50369     /** @ignore */
50370     handleMouseDown : function(e, t){
50371         var v = this.grid.getView();
50372         if(this.isLocked()){
50373             return;
50374         };
50375         var row = v.findRowIndex(t);
50376         var cell = v.findCellIndex(t);
50377         if(row !== false && cell !== false){
50378             this.select(row, cell);
50379         }
50380     },
50381
50382     /**
50383      * Selects a cell.
50384      * @param {Number} rowIndex
50385      * @param {Number} collIndex
50386      */
50387     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50388         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50389             this.clearSelections();
50390             r = r || this.grid.dataSource.getAt(rowIndex);
50391             this.selection = {
50392                 record : r,
50393                 cell : [rowIndex, colIndex]
50394             };
50395             if(!preventViewNotify){
50396                 var v = this.grid.getView();
50397                 v.onCellSelect(rowIndex, colIndex);
50398                 if(preventFocus !== true){
50399                     v.focusCell(rowIndex, colIndex);
50400                 }
50401             }
50402             this.fireEvent("cellselect", this, rowIndex, colIndex);
50403             this.fireEvent("selectionchange", this, this.selection);
50404         }
50405     },
50406
50407         //private
50408     isSelectable : function(rowIndex, colIndex, cm){
50409         return !cm.isHidden(colIndex);
50410     },
50411
50412     /** @ignore */
50413     handleKeyDown : function(e){
50414         Roo.log('Cell Sel Model handleKeyDown');
50415         if(!e.isNavKeyPress()){
50416             return;
50417         }
50418         var g = this.grid, s = this.selection;
50419         if(!s){
50420             e.stopEvent();
50421             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50422             if(cell){
50423                 this.select(cell[0], cell[1]);
50424             }
50425             return;
50426         }
50427         var sm = this;
50428         var walk = function(row, col, step){
50429             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50430         };
50431         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50432         var newCell;
50433
50434         switch(k){
50435             case e.TAB:
50436                 // handled by onEditorKey
50437                 if (g.isEditor && g.editing) {
50438                     return;
50439                 }
50440                 if(e.shiftKey){
50441                      newCell = walk(r, c-1, -1);
50442                 }else{
50443                      newCell = walk(r, c+1, 1);
50444                 }
50445              break;
50446              case e.DOWN:
50447                  newCell = walk(r+1, c, 1);
50448              break;
50449              case e.UP:
50450                  newCell = walk(r-1, c, -1);
50451              break;
50452              case e.RIGHT:
50453                  newCell = walk(r, c+1, 1);
50454              break;
50455              case e.LEFT:
50456                  newCell = walk(r, c-1, -1);
50457              break;
50458              case e.ENTER:
50459                  if(g.isEditor && !g.editing){
50460                     g.startEditing(r, c);
50461                     e.stopEvent();
50462                     return;
50463                 }
50464              break;
50465         };
50466         if(newCell){
50467             this.select(newCell[0], newCell[1]);
50468             e.stopEvent();
50469         }
50470     },
50471
50472     acceptsNav : function(row, col, cm){
50473         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50474     },
50475
50476     onEditorKey : function(field, e){
50477         
50478         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50479         ///Roo.log('onEditorKey' + k);
50480         
50481         if(k == e.TAB){
50482             if(e.shiftKey){
50483                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50484             }else{
50485                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50486             }
50487             e.stopEvent();
50488         }else if(k == e.ENTER && !e.ctrlKey){
50489             ed.completeEdit();
50490             e.stopEvent();
50491             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50492         }else if(k == e.ESC){
50493             ed.cancelEdit();
50494         }
50495         
50496         
50497         if(newCell){
50498             //Roo.log('next cell after edit');
50499             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50500         }
50501     }
50502 });/*
50503  * Based on:
50504  * Ext JS Library 1.1.1
50505  * Copyright(c) 2006-2007, Ext JS, LLC.
50506  *
50507  * Originally Released Under LGPL - original licence link has changed is not relivant.
50508  *
50509  * Fork - LGPL
50510  * <script type="text/javascript">
50511  */
50512  
50513 /**
50514  * @class Roo.grid.EditorGrid
50515  * @extends Roo.grid.Grid
50516  * Class for creating and editable grid.
50517  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50518  * The container MUST have some type of size defined for the grid to fill. The container will be 
50519  * automatically set to position relative if it isn't already.
50520  * @param {Object} dataSource The data model to bind to
50521  * @param {Object} colModel The column model with info about this grid's columns
50522  */
50523 Roo.grid.EditorGrid = function(container, config){
50524     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50525     this.getGridEl().addClass("xedit-grid");
50526
50527     if(!this.selModel){
50528         this.selModel = new Roo.grid.CellSelectionModel();
50529     }
50530
50531     this.activeEditor = null;
50532
50533         this.addEvents({
50534             /**
50535              * @event beforeedit
50536              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50537              * <ul style="padding:5px;padding-left:16px;">
50538              * <li>grid - This grid</li>
50539              * <li>record - The record being edited</li>
50540              * <li>field - The field name being edited</li>
50541              * <li>value - The value for the field being edited.</li>
50542              * <li>row - The grid row index</li>
50543              * <li>column - The grid column index</li>
50544              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50545              * </ul>
50546              * @param {Object} e An edit event (see above for description)
50547              */
50548             "beforeedit" : true,
50549             /**
50550              * @event afteredit
50551              * Fires after a cell is edited. <br />
50552              * <ul style="padding:5px;padding-left:16px;">
50553              * <li>grid - This grid</li>
50554              * <li>record - The record being edited</li>
50555              * <li>field - The field name being edited</li>
50556              * <li>value - The value being set</li>
50557              * <li>originalValue - The original value for the field, before the edit.</li>
50558              * <li>row - The grid row index</li>
50559              * <li>column - The grid column index</li>
50560              * </ul>
50561              * @param {Object} e An edit event (see above for description)
50562              */
50563             "afteredit" : true,
50564             /**
50565              * @event validateedit
50566              * Fires after a cell is edited, but before the value is set in the record. 
50567          * You can use this to modify the value being set in the field, Return false
50568              * to cancel the change. The edit event object has the following properties <br />
50569              * <ul style="padding:5px;padding-left:16px;">
50570          * <li>editor - This editor</li>
50571              * <li>grid - This grid</li>
50572              * <li>record - The record being edited</li>
50573              * <li>field - The field name being edited</li>
50574              * <li>value - The value being set</li>
50575              * <li>originalValue - The original value for the field, before the edit.</li>
50576              * <li>row - The grid row index</li>
50577              * <li>column - The grid column index</li>
50578              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50579              * </ul>
50580              * @param {Object} e An edit event (see above for description)
50581              */
50582             "validateedit" : true
50583         });
50584     this.on("bodyscroll", this.stopEditing,  this);
50585     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50586 };
50587
50588 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50589     /**
50590      * @cfg {Number} clicksToEdit
50591      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50592      */
50593     clicksToEdit: 2,
50594
50595     // private
50596     isEditor : true,
50597     // private
50598     trackMouseOver: false, // causes very odd FF errors
50599
50600     onCellDblClick : function(g, row, col){
50601         this.startEditing(row, col);
50602     },
50603
50604     onEditComplete : function(ed, value, startValue){
50605         this.editing = false;
50606         this.activeEditor = null;
50607         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50608         var r = ed.record;
50609         var field = this.colModel.getDataIndex(ed.col);
50610         var e = {
50611             grid: this,
50612             record: r,
50613             field: field,
50614             originalValue: startValue,
50615             value: value,
50616             row: ed.row,
50617             column: ed.col,
50618             cancel:false,
50619             editor: ed
50620         };
50621         if(String(value) !== String(startValue)){
50622             
50623             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50624                 r.set(field, e.value);
50625                 // if we are dealing with a combo box..
50626                 // then we also set the 'name' colum to be the displayField
50627                 if (ed.field.displayField && ed.field.name) {
50628                     r.set(ed.field.name, ed.field.el.dom.value);
50629                 }
50630                 
50631                 delete e.cancel; //?? why!!!
50632                 this.fireEvent("afteredit", e);
50633             }
50634         } else {
50635             this.fireEvent("afteredit", e); // always fire it!
50636         }
50637         this.view.focusCell(ed.row, ed.col);
50638     },
50639
50640     /**
50641      * Starts editing the specified for the specified row/column
50642      * @param {Number} rowIndex
50643      * @param {Number} colIndex
50644      */
50645     startEditing : function(row, col){
50646         this.stopEditing();
50647         if(this.colModel.isCellEditable(col, row)){
50648             this.view.ensureVisible(row, col, true);
50649             var r = this.dataSource.getAt(row);
50650             var field = this.colModel.getDataIndex(col);
50651             var e = {
50652                 grid: this,
50653                 record: r,
50654                 field: field,
50655                 value: r.data[field],
50656                 row: row,
50657                 column: col,
50658                 cancel:false
50659             };
50660             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50661                 this.editing = true;
50662                 var ed = this.colModel.getCellEditor(col, row);
50663                 
50664                 if (!ed) {
50665                     return;
50666                 }
50667                 if(!ed.rendered){
50668                     ed.render(ed.parentEl || document.body);
50669                 }
50670                 ed.field.reset();
50671                 (function(){ // complex but required for focus issues in safari, ie and opera
50672                     ed.row = row;
50673                     ed.col = col;
50674                     ed.record = r;
50675                     ed.on("complete", this.onEditComplete, this, {single: true});
50676                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50677                     this.activeEditor = ed;
50678                     var v = r.data[field];
50679                     ed.startEdit(this.view.getCell(row, col), v);
50680                     // combo's with 'displayField and name set
50681                     if (ed.field.displayField && ed.field.name) {
50682                         ed.field.el.dom.value = r.data[ed.field.name];
50683                     }
50684                     
50685                     
50686                 }).defer(50, this);
50687             }
50688         }
50689     },
50690         
50691     /**
50692      * Stops any active editing
50693      */
50694     stopEditing : function(){
50695         if(this.activeEditor){
50696             this.activeEditor.completeEdit();
50697         }
50698         this.activeEditor = null;
50699     }
50700 });/*
50701  * Based on:
50702  * Ext JS Library 1.1.1
50703  * Copyright(c) 2006-2007, Ext JS, LLC.
50704  *
50705  * Originally Released Under LGPL - original licence link has changed is not relivant.
50706  *
50707  * Fork - LGPL
50708  * <script type="text/javascript">
50709  */
50710
50711 // private - not really -- you end up using it !
50712 // This is a support class used internally by the Grid components
50713
50714 /**
50715  * @class Roo.grid.GridEditor
50716  * @extends Roo.Editor
50717  * Class for creating and editable grid elements.
50718  * @param {Object} config any settings (must include field)
50719  */
50720 Roo.grid.GridEditor = function(field, config){
50721     if (!config && field.field) {
50722         config = field;
50723         field = Roo.factory(config.field, Roo.form);
50724     }
50725     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50726     field.monitorTab = false;
50727 };
50728
50729 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50730     
50731     /**
50732      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50733      */
50734     
50735     alignment: "tl-tl",
50736     autoSize: "width",
50737     hideEl : false,
50738     cls: "x-small-editor x-grid-editor",
50739     shim:false,
50740     shadow:"frame"
50741 });/*
50742  * Based on:
50743  * Ext JS Library 1.1.1
50744  * Copyright(c) 2006-2007, Ext JS, LLC.
50745  *
50746  * Originally Released Under LGPL - original licence link has changed is not relivant.
50747  *
50748  * Fork - LGPL
50749  * <script type="text/javascript">
50750  */
50751   
50752
50753   
50754 Roo.grid.PropertyRecord = Roo.data.Record.create([
50755     {name:'name',type:'string'},  'value'
50756 ]);
50757
50758
50759 Roo.grid.PropertyStore = function(grid, source){
50760     this.grid = grid;
50761     this.store = new Roo.data.Store({
50762         recordType : Roo.grid.PropertyRecord
50763     });
50764     this.store.on('update', this.onUpdate,  this);
50765     if(source){
50766         this.setSource(source);
50767     }
50768     Roo.grid.PropertyStore.superclass.constructor.call(this);
50769 };
50770
50771
50772
50773 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50774     setSource : function(o){
50775         this.source = o;
50776         this.store.removeAll();
50777         var data = [];
50778         for(var k in o){
50779             if(this.isEditableValue(o[k])){
50780                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50781             }
50782         }
50783         this.store.loadRecords({records: data}, {}, true);
50784     },
50785
50786     onUpdate : function(ds, record, type){
50787         if(type == Roo.data.Record.EDIT){
50788             var v = record.data['value'];
50789             var oldValue = record.modified['value'];
50790             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50791                 this.source[record.id] = v;
50792                 record.commit();
50793                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50794             }else{
50795                 record.reject();
50796             }
50797         }
50798     },
50799
50800     getProperty : function(row){
50801        return this.store.getAt(row);
50802     },
50803
50804     isEditableValue: function(val){
50805         if(val && val instanceof Date){
50806             return true;
50807         }else if(typeof val == 'object' || typeof val == 'function'){
50808             return false;
50809         }
50810         return true;
50811     },
50812
50813     setValue : function(prop, value){
50814         this.source[prop] = value;
50815         this.store.getById(prop).set('value', value);
50816     },
50817
50818     getSource : function(){
50819         return this.source;
50820     }
50821 });
50822
50823 Roo.grid.PropertyColumnModel = function(grid, store){
50824     this.grid = grid;
50825     var g = Roo.grid;
50826     g.PropertyColumnModel.superclass.constructor.call(this, [
50827         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50828         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50829     ]);
50830     this.store = store;
50831     this.bselect = Roo.DomHelper.append(document.body, {
50832         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50833             {tag: 'option', value: 'true', html: 'true'},
50834             {tag: 'option', value: 'false', html: 'false'}
50835         ]
50836     });
50837     Roo.id(this.bselect);
50838     var f = Roo.form;
50839     this.editors = {
50840         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50841         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50842         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50843         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50844         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50845     };
50846     this.renderCellDelegate = this.renderCell.createDelegate(this);
50847     this.renderPropDelegate = this.renderProp.createDelegate(this);
50848 };
50849
50850 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50851     
50852     
50853     nameText : 'Name',
50854     valueText : 'Value',
50855     
50856     dateFormat : 'm/j/Y',
50857     
50858     
50859     renderDate : function(dateVal){
50860         return dateVal.dateFormat(this.dateFormat);
50861     },
50862
50863     renderBool : function(bVal){
50864         return bVal ? 'true' : 'false';
50865     },
50866
50867     isCellEditable : function(colIndex, rowIndex){
50868         return colIndex == 1;
50869     },
50870
50871     getRenderer : function(col){
50872         return col == 1 ?
50873             this.renderCellDelegate : this.renderPropDelegate;
50874     },
50875
50876     renderProp : function(v){
50877         return this.getPropertyName(v);
50878     },
50879
50880     renderCell : function(val){
50881         var rv = val;
50882         if(val instanceof Date){
50883             rv = this.renderDate(val);
50884         }else if(typeof val == 'boolean'){
50885             rv = this.renderBool(val);
50886         }
50887         return Roo.util.Format.htmlEncode(rv);
50888     },
50889
50890     getPropertyName : function(name){
50891         var pn = this.grid.propertyNames;
50892         return pn && pn[name] ? pn[name] : name;
50893     },
50894
50895     getCellEditor : function(colIndex, rowIndex){
50896         var p = this.store.getProperty(rowIndex);
50897         var n = p.data['name'], val = p.data['value'];
50898         
50899         if(typeof(this.grid.customEditors[n]) == 'string'){
50900             return this.editors[this.grid.customEditors[n]];
50901         }
50902         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50903             return this.grid.customEditors[n];
50904         }
50905         if(val instanceof Date){
50906             return this.editors['date'];
50907         }else if(typeof val == 'number'){
50908             return this.editors['number'];
50909         }else if(typeof val == 'boolean'){
50910             return this.editors['boolean'];
50911         }else{
50912             return this.editors['string'];
50913         }
50914     }
50915 });
50916
50917 /**
50918  * @class Roo.grid.PropertyGrid
50919  * @extends Roo.grid.EditorGrid
50920  * This class represents the  interface of a component based property grid control.
50921  * <br><br>Usage:<pre><code>
50922  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50923       
50924  });
50925  // set any options
50926  grid.render();
50927  * </code></pre>
50928   
50929  * @constructor
50930  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50931  * The container MUST have some type of size defined for the grid to fill. The container will be
50932  * automatically set to position relative if it isn't already.
50933  * @param {Object} config A config object that sets properties on this grid.
50934  */
50935 Roo.grid.PropertyGrid = function(container, config){
50936     config = config || {};
50937     var store = new Roo.grid.PropertyStore(this);
50938     this.store = store;
50939     var cm = new Roo.grid.PropertyColumnModel(this, store);
50940     store.store.sort('name', 'ASC');
50941     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50942         ds: store.store,
50943         cm: cm,
50944         enableColLock:false,
50945         enableColumnMove:false,
50946         stripeRows:false,
50947         trackMouseOver: false,
50948         clicksToEdit:1
50949     }, config));
50950     this.getGridEl().addClass('x-props-grid');
50951     this.lastEditRow = null;
50952     this.on('columnresize', this.onColumnResize, this);
50953     this.addEvents({
50954          /**
50955              * @event beforepropertychange
50956              * Fires before a property changes (return false to stop?)
50957              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50958              * @param {String} id Record Id
50959              * @param {String} newval New Value
50960          * @param {String} oldval Old Value
50961              */
50962         "beforepropertychange": true,
50963         /**
50964              * @event propertychange
50965              * Fires after a property changes
50966              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50967              * @param {String} id Record Id
50968              * @param {String} newval New Value
50969          * @param {String} oldval Old Value
50970              */
50971         "propertychange": true
50972     });
50973     this.customEditors = this.customEditors || {};
50974 };
50975 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50976     
50977      /**
50978      * @cfg {Object} customEditors map of colnames=> custom editors.
50979      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50980      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50981      * false disables editing of the field.
50982          */
50983     
50984       /**
50985      * @cfg {Object} propertyNames map of property Names to their displayed value
50986          */
50987     
50988     render : function(){
50989         Roo.grid.PropertyGrid.superclass.render.call(this);
50990         this.autoSize.defer(100, this);
50991     },
50992
50993     autoSize : function(){
50994         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50995         if(this.view){
50996             this.view.fitColumns();
50997         }
50998     },
50999
51000     onColumnResize : function(){
51001         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51002         this.autoSize();
51003     },
51004     /**
51005      * Sets the data for the Grid
51006      * accepts a Key => Value object of all the elements avaiable.
51007      * @param {Object} data  to appear in grid.
51008      */
51009     setSource : function(source){
51010         this.store.setSource(source);
51011         //this.autoSize();
51012     },
51013     /**
51014      * Gets all the data from the grid.
51015      * @return {Object} data  data stored in grid
51016      */
51017     getSource : function(){
51018         return this.store.getSource();
51019     }
51020 });/*
51021  * Based on:
51022  * Ext JS Library 1.1.1
51023  * Copyright(c) 2006-2007, Ext JS, LLC.
51024  *
51025  * Originally Released Under LGPL - original licence link has changed is not relivant.
51026  *
51027  * Fork - LGPL
51028  * <script type="text/javascript">
51029  */
51030  
51031 /**
51032  * @class Roo.LoadMask
51033  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51034  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51035  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51036  * element's UpdateManager load indicator and will be destroyed after the initial load.
51037  * @constructor
51038  * Create a new LoadMask
51039  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51040  * @param {Object} config The config object
51041  */
51042 Roo.LoadMask = function(el, config){
51043     this.el = Roo.get(el);
51044     Roo.apply(this, config);
51045     if(this.store){
51046         this.store.on('beforeload', this.onBeforeLoad, this);
51047         this.store.on('load', this.onLoad, this);
51048         this.store.on('loadexception', this.onLoad, this);
51049         this.removeMask = false;
51050     }else{
51051         var um = this.el.getUpdateManager();
51052         um.showLoadIndicator = false; // disable the default indicator
51053         um.on('beforeupdate', this.onBeforeLoad, this);
51054         um.on('update', this.onLoad, this);
51055         um.on('failure', this.onLoad, this);
51056         this.removeMask = true;
51057     }
51058 };
51059
51060 Roo.LoadMask.prototype = {
51061     /**
51062      * @cfg {Boolean} removeMask
51063      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51064      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51065      */
51066     /**
51067      * @cfg {String} msg
51068      * The text to display in a centered loading message box (defaults to 'Loading...')
51069      */
51070     msg : 'Loading...',
51071     /**
51072      * @cfg {String} msgCls
51073      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51074      */
51075     msgCls : 'x-mask-loading',
51076
51077     /**
51078      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51079      * @type Boolean
51080      */
51081     disabled: false,
51082
51083     /**
51084      * Disables the mask to prevent it from being displayed
51085      */
51086     disable : function(){
51087        this.disabled = true;
51088     },
51089
51090     /**
51091      * Enables the mask so that it can be displayed
51092      */
51093     enable : function(){
51094         this.disabled = false;
51095     },
51096
51097     // private
51098     onLoad : function(){
51099         this.el.unmask(this.removeMask);
51100     },
51101
51102     // private
51103     onBeforeLoad : function(){
51104         if(!this.disabled){
51105             this.el.mask(this.msg, this.msgCls);
51106         }
51107     },
51108
51109     // private
51110     destroy : function(){
51111         if(this.store){
51112             this.store.un('beforeload', this.onBeforeLoad, this);
51113             this.store.un('load', this.onLoad, this);
51114             this.store.un('loadexception', this.onLoad, this);
51115         }else{
51116             var um = this.el.getUpdateManager();
51117             um.un('beforeupdate', this.onBeforeLoad, this);
51118             um.un('update', this.onLoad, this);
51119             um.un('failure', this.onLoad, this);
51120         }
51121     }
51122 };/*
51123  * Based on:
51124  * Ext JS Library 1.1.1
51125  * Copyright(c) 2006-2007, Ext JS, LLC.
51126  *
51127  * Originally Released Under LGPL - original licence link has changed is not relivant.
51128  *
51129  * Fork - LGPL
51130  * <script type="text/javascript">
51131  */
51132 Roo.XTemplate = function(){
51133     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51134     var s = this.html;
51135
51136     s = ['<tpl>', s, '</tpl>'].join('');
51137
51138     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51139
51140     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51141     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51142     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51143     var m, id = 0;
51144     var tpls = [];
51145
51146     while(m = s.match(re)){
51147        var m2 = m[0].match(nameRe);
51148        var m3 = m[0].match(ifRe);
51149        var m4 = m[0].match(execRe);
51150        var exp = null, fn = null, exec = null;
51151        var name = m2 && m2[1] ? m2[1] : '';
51152        if(m3){
51153            exp = m3 && m3[1] ? m3[1] : null;
51154            if(exp){
51155                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51156            }
51157        }
51158        if(m4){
51159            exp = m4 && m4[1] ? m4[1] : null;
51160            if(exp){
51161                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51162            }
51163        }
51164        if(name){
51165            switch(name){
51166                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51167                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51168                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51169            }
51170        }
51171        tpls.push({
51172             id: id,
51173             target: name,
51174             exec: exec,
51175             test: fn,
51176             body: m[1]||''
51177         });
51178        s = s.replace(m[0], '{xtpl'+ id + '}');
51179        ++id;
51180     }
51181     for(var i = tpls.length-1; i >= 0; --i){
51182         this.compileTpl(tpls[i]);
51183     }
51184     this.master = tpls[tpls.length-1];
51185     this.tpls = tpls;
51186 };
51187 Roo.extend(Roo.XTemplate, Roo.Template, {
51188
51189     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51190
51191     applySubTemplate : function(id, values, parent){
51192         var t = this.tpls[id];
51193         if(t.test && !t.test.call(this, values, parent)){
51194             return '';
51195         }
51196         if(t.exec && t.exec.call(this, values, parent)){
51197             return '';
51198         }
51199         var vs = t.target ? t.target.call(this, values, parent) : values;
51200         parent = t.target ? values : parent;
51201         if(t.target && vs instanceof Array){
51202             var buf = [];
51203             for(var i = 0, len = vs.length; i < len; i++){
51204                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51205             }
51206             return buf.join('');
51207         }
51208         return t.compiled.call(this, vs, parent);
51209     },
51210
51211     compileTpl : function(tpl){
51212         var fm = Roo.util.Format;
51213         var useF = this.disableFormats !== true;
51214         var sep = Roo.isGecko ? "+" : ",";
51215         var fn = function(m, name, format, args){
51216             if(name.substr(0, 4) == 'xtpl'){
51217                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51218             }
51219             var v;
51220             if(name.indexOf('.') != -1){
51221                 v = name;
51222             }else{
51223                 v = "values['" + name + "']";
51224             }
51225             if(format && useF){
51226                 args = args ? ',' + args : "";
51227                 if(format.substr(0, 5) != "this."){
51228                     format = "fm." + format + '(';
51229                 }else{
51230                     format = 'this.call("'+ format.substr(5) + '", ';
51231                     args = ", values";
51232                 }
51233             }else{
51234                 args= ''; format = "("+v+" === undefined ? '' : ";
51235             }
51236             return "'"+ sep + format + v + args + ")"+sep+"'";
51237         };
51238         var body;
51239         // branched to use + in gecko and [].join() in others
51240         if(Roo.isGecko){
51241             body = "tpl.compiled = function(values, parent){ return '" +
51242                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51243                     "';};";
51244         }else{
51245             body = ["tpl.compiled = function(values, parent){ return ['"];
51246             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51247             body.push("'].join('');};");
51248             body = body.join('');
51249         }
51250         /** eval:var:zzzzzzz */
51251         eval(body);
51252         return this;
51253     },
51254
51255     applyTemplate : function(values){
51256         return this.master.compiled.call(this, values, {});
51257         var s = this.subs;
51258     },
51259
51260     apply : function(){
51261         return this.applyTemplate.apply(this, arguments);
51262     },
51263
51264     compile : function(){return this;}
51265 });
51266
51267 Roo.XTemplate.from = function(el){
51268     el = Roo.getDom(el);
51269     return new Roo.XTemplate(el.value || el.innerHTML);
51270 };/*
51271  * Original code for Roojs - LGPL
51272  * <script type="text/javascript">
51273  */
51274  
51275 /**
51276  * @class Roo.XComponent
51277  * A delayed Element creator...
51278  * Or a way to group chunks of interface together.
51279  * 
51280  * Mypart.xyx = new Roo.XComponent({
51281
51282     parent : 'Mypart.xyz', // empty == document.element.!!
51283     order : '001',
51284     name : 'xxxx'
51285     region : 'xxxx'
51286     disabled : function() {} 
51287      
51288     tree : function() { // return an tree of xtype declared components
51289         var MODULE = this;
51290         return 
51291         {
51292             xtype : 'NestedLayoutPanel',
51293             // technicall
51294         }
51295      ]
51296  *})
51297  *
51298  *
51299  * It can be used to build a big heiracy, with parent etc.
51300  * or you can just use this to render a single compoent to a dom element
51301  * MYPART.render(Roo.Element | String(id) | dom_element )
51302  * 
51303  * @extends Roo.util.Observable
51304  * @constructor
51305  * @param cfg {Object} configuration of component
51306  * 
51307  */
51308 Roo.XComponent = function(cfg) {
51309     Roo.apply(this, cfg);
51310     this.addEvents({ 
51311         /**
51312              * @event built
51313              * Fires when this the componnt is built
51314              * @param {Roo.XComponent} c the component
51315              */
51316         'built' : true,
51317         /**
51318              * @event buildcomplete
51319              * Fires on the top level element when all elements have been built
51320              * @param {Roo.XComponent} c the top level component.
51321          */
51322         'buildcomplete' : true
51323         
51324     });
51325     this.region = this.region || 'center'; // default..
51326     Roo.XComponent.register(this);
51327     this.modules = false;
51328     this.el = false; // where the layout goes..
51329     
51330     
51331 }
51332 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51333     /**
51334      * @property el
51335      * The created element (with Roo.factory())
51336      * @type {Roo.Layout}
51337      */
51338     el  : false,
51339     
51340     /**
51341      * @property el
51342      * for BC  - use el in new code
51343      * @type {Roo.Layout}
51344      */
51345     panel : false,
51346     
51347     /**
51348      * @property layout
51349      * for BC  - use el in new code
51350      * @type {Roo.Layout}
51351      */
51352     layout : false,
51353     
51354      /**
51355      * @cfg {Function|boolean} disabled
51356      * If this module is disabled by some rule, return true from the funtion
51357      */
51358     disabled : false,
51359     
51360     /**
51361      * @cfg {String} parent 
51362      * Name of parent element which it get xtype added to..
51363      */
51364     parent: false,
51365     
51366     /**
51367      * @cfg {String} order
51368      * Used to set the order in which elements are created (usefull for multiple tabs)
51369      */
51370     
51371     order : false,
51372     /**
51373      * @cfg {String} name
51374      * String to display while loading.
51375      */
51376     name : false,
51377     /**
51378      * @cfg {String} region
51379      * Region to render component to (defaults to center)
51380      */
51381     region : 'center',
51382     
51383     /**
51384      * @cfg {Array} items
51385      * A single item array - the first element is the root of the tree..
51386      * It's done this way to stay compatible with the Xtype system...
51387      */
51388     items : false,
51389     
51390     
51391      /**
51392      * render
51393      * render element to dom or tree
51394      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51395      */
51396     
51397     render : function(el)
51398     {
51399         
51400         el = el || false;
51401         
51402         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
51403             // if parent is a '#.....' string, then let's use that..
51404             var ename = this.parent.substr(1)
51405             this.parent = false;
51406             el = Roo.get(ename);
51407             if (!el) {
51408                 Roo.log("Warning - element can not be found :#" + ename );
51409                 return;
51410             }
51411         }
51412         if (!this.parent) {
51413             
51414             el = el ? Roo.get(el) : false;
51415             
51416             // it's a top level one..
51417             this.parent =  {
51418                 el : new Roo.BorderLayout(el || document.body, {
51419                 
51420                      center: {
51421                          titlebar: false,
51422                          autoScroll:false,
51423                          closeOnTab: true,
51424                          tabPosition: 'top',
51425                           //resizeTabs: true,
51426                          alwaysShowTabs: el ? false :  true,
51427                          hideTabs: el ? true :  false,
51428                          minTabWidth: 140
51429                      }
51430                  })
51431             }
51432         }
51433         
51434         
51435             
51436         var tree = this.tree();
51437         tree.region = tree.region || this.region;
51438         this.el = this.parent.el.addxtype(tree);
51439         this.fireEvent('built', this);
51440         
51441         this.panel = this.el;
51442         this.layout = this.panel.layout;    
51443          
51444     }
51445     
51446 });
51447
51448 Roo.apply(Roo.XComponent, {
51449     
51450     /**
51451      * @property  buildCompleted
51452      * True when the builder has completed building the interface.
51453      * @type Boolean
51454      */
51455     buildCompleted : false,
51456      
51457     /**
51458      * @property  topModule
51459      * the upper most module - uses document.element as it's constructor.
51460      * @type Object
51461      */
51462      
51463     topModule  : false,
51464       
51465     /**
51466      * @property  modules
51467      * array of modules to be created by registration system.
51468      * @type {Array} of Roo.XComponent
51469      */
51470     
51471     modules : [],
51472     /**
51473      * @property  elmodules
51474      * array of modules to be created by which use #ID 
51475      * @type {Array} of Roo.XComponent
51476      */
51477      
51478     elmodules : [],
51479
51480     
51481     /**
51482      * Register components to be built later.
51483      *
51484      * This solves the following issues
51485      * - Building is not done on page load, but after an authentication process has occured.
51486      * - Interface elements are registered on page load
51487      * - Parent Interface elements may not be loaded before child, so this handles that..
51488      * 
51489      *
51490      * example:
51491      * 
51492      * MyApp.register({
51493           order : '000001',
51494           module : 'Pman.Tab.projectMgr',
51495           region : 'center',
51496           parent : 'Pman.layout',
51497           disabled : false,  // or use a function..
51498         })
51499      
51500      * * @param {Object} details about module
51501      */
51502     register : function(obj) {
51503         this.modules.push(obj);
51504          
51505     },
51506     /**
51507      * convert a string to an object..
51508      * eg. 'AAA.BBB' -> finds AAA.BBB
51509
51510      */
51511     
51512     toObject : function(str)
51513     {
51514         if (!str || typeof(str) == 'object') {
51515             return str;
51516         }
51517         if (str[0]=='#') {
51518             return str;
51519         }
51520
51521         var ar = str.split('.');
51522         var rt, o;
51523         rt = ar.shift();
51524             /** eval:var:o */
51525         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51526         if (o === false) {
51527             throw "Module not found : " + str;
51528         }
51529         Roo.each(ar, function(e) {
51530             if (typeof(o[e]) == 'undefined') {
51531                 throw "Module not found : " + str;
51532             }
51533             o = o[e];
51534         });
51535         return o;
51536         
51537     },
51538     
51539     
51540     /**
51541      * move modules into their correct place in the tree..
51542      * 
51543      */
51544     preBuild : function ()
51545     {
51546         
51547         Roo.each(this.modules , function (obj)
51548         {
51549             obj.parent = this.toObject(obj.parent);
51550             
51551             if (!obj.parent) {
51552                 this.topModule = obj;
51553                 return;
51554             }
51555             if (typeof(obj.parent) == 'string') {
51556                 this.elmodules.push(obj);
51557                 return;
51558             }
51559             
51560             if (!obj.parent.modules) {
51561                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51562                     function(o) { return o.order + '' }
51563                 );
51564             }
51565             
51566             obj.parent.modules.add(obj);
51567         }, this);
51568     },
51569     
51570      /**
51571      * make a list of modules to build.
51572      * @return {Array} list of modules. 
51573      */ 
51574     
51575     buildOrder : function()
51576     {
51577         var _this = this;
51578         var cmp = function(a,b) {   
51579             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51580         };
51581         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51582             throw "No top level modules to build";
51583         }
51584         
51585         // make a flat list in order of modules to build.
51586         var mods = this.topModule ? [ this.topModule ] : [];
51587         Roo.each(this.elmodules,function(e) { mods.push(e) });
51588
51589         
51590         // add modules to their parents..
51591         var addMod = function(m) {
51592            // Roo.debug && Roo.log(m.modKey);
51593             
51594             mods.push(m);
51595             if (m.modules) {
51596                 m.modules.keySort('ASC',  cmp );
51597                 m.modules.each(addMod);
51598             }
51599             // not sure if this is used any more..
51600             if (m.finalize) {
51601                 m.finalize.name = m.name + " (clean up) ";
51602                 mods.push(m.finalize);
51603             }
51604             
51605         }
51606         if (this.topModule) { 
51607             this.topModule.modules.keySort('ASC',  cmp );
51608             this.topModule.modules.each(addMod);
51609         }
51610         return mods;
51611     },
51612     
51613      /**
51614      * Build the registered modules.
51615      * @param {Object} parent element.
51616      * @param {Function} optional method to call after module has been added.
51617      * 
51618      */ 
51619    
51620     build : function() 
51621     {
51622         
51623         this.preBuild();
51624         var mods = this.buildOrder();
51625       
51626         //this.allmods = mods;
51627         //Roo.debug && Roo.log(mods);
51628         //return;
51629         if (!mods.length) { // should not happen
51630             throw "NO modules!!!";
51631         }
51632         
51633         
51634         
51635         // flash it up as modal - so we store the mask!?
51636         Roo.MessageBox.show({ title: 'loading' });
51637         Roo.MessageBox.show({
51638            title: "Please wait...",
51639            msg: "Building Interface...",
51640            width:450,
51641            progress:true,
51642            closable:false,
51643            modal: false
51644           
51645         });
51646         var total = mods.length;
51647         
51648         var _this = this;
51649         var progressRun = function() {
51650             if (!mods.length) {
51651                 Roo.debug && Roo.log('hide?');
51652                 Roo.MessageBox.hide();
51653                 if (_this.topModule) { 
51654                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51655                 }
51656                 // THE END...
51657                 return false;   
51658             }
51659             
51660             var m = mods.shift();
51661             
51662             
51663             Roo.debug && Roo.log(m);
51664             // not sure if this is supported any more.. - modules that are are just function
51665             if (typeof(m) == 'function') { 
51666                 m.call(this);
51667                 return progressRun.defer(10, _this);
51668             } 
51669             
51670             
51671             
51672             Roo.MessageBox.updateProgress(
51673                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51674                     " of " + total + 
51675                     (m.name ? (' - ' + m.name) : '')
51676                     );
51677             
51678          
51679             // is the module disabled?
51680             var disabled = (typeof(m.disabled) == 'function') ?
51681                 m.disabled.call(m.module.disabled) : m.disabled;    
51682             
51683             
51684             if (disabled) {
51685                 return progressRun(); // we do not update the display!
51686             }
51687             
51688             // now build 
51689             
51690             m.render();
51691             // it's 10 on top level, and 1 on others??? why...
51692             return progressRun.defer(10, _this);
51693              
51694         }
51695         progressRun.defer(1, _this);
51696      
51697         
51698         
51699     }
51700     
51701      
51702    
51703     
51704     
51705 });
51706  //<script type="text/javascript">
51707
51708
51709 /**
51710  * @class Roo.Login
51711  * @extends Roo.LayoutDialog
51712  * A generic Login Dialog..... - only one needed in theory!?!?
51713  *
51714  * Fires XComponent builder on success...
51715  * 
51716  * Sends 
51717  *    username,password, lang = for login actions.
51718  *    check = 1 for periodic checking that sesion is valid.
51719  *    passwordRequest = email request password
51720  *    logout = 1 = to logout
51721  * 
51722  * Affects: (this id="????" elements)
51723  *   loading  (removed) (used to indicate application is loading)
51724  *   loading-mask (hides) (used to hide application when it's building loading)
51725  *   
51726  * 
51727  * Usage: 
51728  *    
51729  * 
51730  * Myapp.login = Roo.Login({
51731      url: xxxx,
51732    
51733      realm : 'Myapp', 
51734      
51735      
51736      method : 'POST',
51737      
51738      
51739      * 
51740  })
51741  * 
51742  * 
51743  * 
51744  **/
51745  
51746 Roo.Login = function(cfg)
51747 {
51748     this.addEvents({
51749         'refreshed' : true
51750     });
51751     
51752     Roo.apply(this,cfg);
51753     
51754     Roo.onReady(function() {
51755         this.onLoad();
51756     }, this);
51757     // call parent..
51758     
51759    
51760     Roo.Login.superclass.constructor.call(this, this);
51761     //this.addxtype(this.items[0]);
51762     
51763     
51764 }
51765
51766
51767 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51768     
51769     /**
51770      * @cfg {String} method
51771      * Method used to query for login details.
51772      */
51773     
51774     method : 'POST',
51775     /**
51776      * @cfg {String} url
51777      * URL to query login data. - eg. baseURL + '/Login.php'
51778      */
51779     url : '',
51780     
51781     /**
51782      * @property user
51783      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51784      * @type {Object} 
51785      */
51786     user : false,
51787     /**
51788      * @property checkFails
51789      * Number of times we have attempted to get authentication check, and failed.
51790      * @type {Number} 
51791      */
51792     checkFails : 0,
51793       /**
51794      * @property intervalID
51795      * The window interval that does the constant login checking.
51796      * @type {Number} 
51797      */
51798     intervalID : 0,
51799     
51800     
51801     onLoad : function() // called on page load...
51802     {
51803         // load 
51804          
51805         if (Roo.get('loading')) { // clear any loading indicator..
51806             Roo.get('loading').remove();
51807         }
51808         
51809         //this.switchLang('en'); // set the language to english..
51810        
51811         this.check({
51812             success:  function(response, opts)  {  // check successfull...
51813             
51814                 var res = this.processResponse(response);
51815                 this.checkFails =0;
51816                 if (!res.success) { // error!
51817                     this.checkFails = 5;
51818                     //console.log('call failure');
51819                     return this.failure(response,opts);
51820                 }
51821                 
51822                 if (!res.data.id) { // id=0 == login failure.
51823                     return this.show();
51824                 }
51825                 
51826                               
51827                         //console.log(success);
51828                 this.fillAuth(res.data);   
51829                 this.checkFails =0;
51830                 Roo.XComponent.build();
51831             },
51832             failure : this.show
51833         });
51834         
51835     }, 
51836     
51837     
51838     check: function(cfg) // called every so often to refresh cookie etc..
51839     {
51840         if (cfg.again) { // could be undefined..
51841             this.checkFails++;
51842         } else {
51843             this.checkFails = 0;
51844         }
51845         var _this = this;
51846         if (this.sending) {
51847             if ( this.checkFails > 4) {
51848                 Roo.MessageBox.alert("Error",  
51849                     "Error getting authentication status. - try reloading, or wait a while", function() {
51850                         _this.sending = false;
51851                     }); 
51852                 return;
51853             }
51854             cfg.again = true;
51855             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51856             return;
51857         }
51858         this.sending = true;
51859         
51860         Roo.Ajax.request({  
51861             url: this.url,
51862             params: {
51863                 getAuthUser: true
51864             },  
51865             method: this.method,
51866             success:  cfg.success || this.success,
51867             failure : cfg.failure || this.failure,
51868             scope : this,
51869             callCfg : cfg
51870               
51871         });  
51872     }, 
51873     
51874     
51875     logout: function()
51876     {
51877         window.onbeforeunload = function() { }; // false does not work for IE..
51878         this.user = false;
51879         var _this = this;
51880         
51881         Roo.Ajax.request({  
51882             url: this.url,
51883             params: {
51884                 logout: 1
51885             },  
51886             method: 'GET',
51887             failure : function() {
51888                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51889                     document.location = document.location.toString() + '?ts=' + Math.random();
51890                 });
51891                 
51892             },
51893             success : function() {
51894                 _this.user = false;
51895                 this.checkFails =0;
51896                 // fixme..
51897                 document.location = document.location.toString() + '?ts=' + Math.random();
51898             }
51899               
51900               
51901         }); 
51902     },
51903     
51904     processResponse : function (response)
51905     {
51906         var res = '';
51907         try {
51908             res = Roo.decode(response.responseText);
51909             // oops...
51910             if (typeof(res) != 'object') {
51911                 res = { success : false, errorMsg : res, errors : true };
51912             }
51913             if (typeof(res.success) == 'undefined') {
51914                 res.success = false;
51915             }
51916             
51917         } catch(e) {
51918             res = { success : false,  errorMsg : response.responseText, errors : true };
51919         }
51920         return res;
51921     },
51922     
51923     success : function(response, opts)  // check successfull...
51924     {  
51925         this.sending = false;
51926         var res = this.processResponse(response);
51927         if (!res.success) {
51928             return this.failure(response, opts);
51929         }
51930         if (!res.data || !res.data.id) {
51931             return this.failure(response,opts);
51932         }
51933         //console.log(res);
51934         this.fillAuth(res.data);
51935         
51936         this.checkFails =0;
51937         
51938     },
51939     
51940     
51941     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51942     {
51943         this.authUser = -1;
51944         this.sending = false;
51945         var res = this.processResponse(response);
51946         //console.log(res);
51947         if ( this.checkFails > 2) {
51948         
51949             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51950                 "Error getting authentication status. - try reloading"); 
51951             return;
51952         }
51953         opts.callCfg.again = true;
51954         this.check.defer(1000, this, [ opts.callCfg ]);
51955         return;  
51956     },
51957     
51958     
51959     
51960     fillAuth: function(au) {
51961         this.startAuthCheck();
51962         this.authUserId = au.id;
51963         this.authUser = au;
51964         this.lastChecked = new Date();
51965         this.fireEvent('refreshed', au);
51966         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51967         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51968         au.lang = au.lang || 'en';
51969         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51970         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51971         this.switchLang(au.lang );
51972         
51973      
51974         // open system... - -on setyp..
51975         if (this.authUserId  < 0) {
51976             Roo.MessageBox.alert("Warning", 
51977                 "This is an open system - please set up a admin user with a password.");  
51978         }
51979          
51980         //Pman.onload(); // which should do nothing if it's a re-auth result...
51981         
51982              
51983     },
51984     
51985     startAuthCheck : function() // starter for timeout checking..
51986     {
51987         if (this.intervalID) { // timer already in place...
51988             return false;
51989         }
51990         var _this = this;
51991         this.intervalID =  window.setInterval(function() {
51992               _this.check(false);
51993             }, 120000); // every 120 secs = 2mins..
51994         
51995         
51996     },
51997          
51998     
51999     switchLang : function (lang) 
52000     {
52001         _T = typeof(_T) == 'undefined' ? false : _T;
52002           if (!_T || !lang.length) {
52003             return;
52004         }
52005         
52006         if (!_T && lang != 'en') {
52007             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52008             return;
52009         }
52010         
52011         if (typeof(_T.en) == 'undefined') {
52012             _T.en = {};
52013             Roo.apply(_T.en, _T);
52014         }
52015         
52016         if (typeof(_T[lang]) == 'undefined') {
52017             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52018             return;
52019         }
52020         
52021         
52022         Roo.apply(_T, _T[lang]);
52023         // just need to set the text values for everything...
52024         var _this = this;
52025         /* this will not work ...
52026         if (this.form) { 
52027             
52028                
52029             function formLabel(name, val) {
52030                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52031             }
52032             
52033             formLabel('password', "Password"+':');
52034             formLabel('username', "Email Address"+':');
52035             formLabel('lang', "Language"+':');
52036             this.dialog.setTitle("Login");
52037             this.dialog.buttons[0].setText("Forgot Password");
52038             this.dialog.buttons[1].setText("Login");
52039         }
52040         */
52041         
52042         
52043     },
52044     
52045     
52046     title: "Login",
52047     modal: true,
52048     width:  350,
52049     //height: 230,
52050     height: 180,
52051     shadow: true,
52052     minWidth:200,
52053     minHeight:180,
52054     //proxyDrag: true,
52055     closable: false,
52056     draggable: false,
52057     collapsible: false,
52058     resizable: false,
52059     center: {  // needed??
52060         autoScroll:false,
52061         titlebar: false,
52062        // tabPosition: 'top',
52063         hideTabs: true,
52064         closeOnTab: true,
52065         alwaysShowTabs: false
52066     } ,
52067     listeners : {
52068         
52069         show  : function(dlg)
52070         {
52071             //console.log(this);
52072             this.form = this.layout.getRegion('center').activePanel.form;
52073             this.form.dialog = dlg;
52074             this.buttons[0].form = this.form;
52075             this.buttons[0].dialog = dlg;
52076             this.buttons[1].form = this.form;
52077             this.buttons[1].dialog = dlg;
52078            
52079            //this.resizeToLogo.defer(1000,this);
52080             // this is all related to resizing for logos..
52081             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52082            //// if (!sz) {
52083              //   this.resizeToLogo.defer(1000,this);
52084              //   return;
52085            // }
52086             //var w = Ext.lib.Dom.getViewWidth() - 100;
52087             //var h = Ext.lib.Dom.getViewHeight() - 100;
52088             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52089             //this.center();
52090             if (this.disabled) {
52091                 this.hide();
52092                 return;
52093             }
52094             
52095             if (this.user.id < 0) { // used for inital setup situations.
52096                 return;
52097             }
52098             
52099             if (this.intervalID) {
52100                 // remove the timer
52101                 window.clearInterval(this.intervalID);
52102                 this.intervalID = false;
52103             }
52104             
52105             
52106             if (Roo.get('loading')) {
52107                 Roo.get('loading').remove();
52108             }
52109             if (Roo.get('loading-mask')) {
52110                 Roo.get('loading-mask').hide();
52111             }
52112             
52113             //incomming._node = tnode;
52114             this.form.reset();
52115             //this.dialog.modal = !modal;
52116             //this.dialog.show();
52117             this.el.unmask(); 
52118             
52119             
52120             this.form.setValues({
52121                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52122                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52123             });
52124             
52125             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52126             if (this.form.findField('username').getValue().length > 0 ){
52127                 this.form.findField('password').focus();
52128             } else {
52129                this.form.findField('username').focus();
52130             }
52131     
52132         }
52133     },
52134     items : [
52135          {
52136        
52137             xtype : 'ContentPanel',
52138             xns : Roo,
52139             region: 'center',
52140             fitToFrame : true,
52141             
52142             items : [
52143     
52144                 {
52145                
52146                     xtype : 'Form',
52147                     xns : Roo.form,
52148                     labelWidth: 100,
52149                     style : 'margin: 10px;',
52150                     
52151                     listeners : {
52152                         actionfailed : function(f, act) {
52153                             // form can return { errors: .... }
52154                                 
52155                             //act.result.errors // invalid form element list...
52156                             //act.result.errorMsg// invalid form element list...
52157                             
52158                             this.dialog.el.unmask();
52159                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52160                                         "Login failed - communication error - try again.");
52161                                       
52162                         },
52163                         actioncomplete: function(re, act) {
52164                              
52165                             Roo.state.Manager.set(
52166                                 this.dialog.realm + '.username',  
52167                                     this.findField('username').getValue()
52168                             );
52169                             Roo.state.Manager.set(
52170                                 this.dialog.realm + '.lang',  
52171                                 this.findField('lang').getValue() 
52172                             );
52173                             
52174                             this.dialog.fillAuth(act.result.data);
52175                               
52176                             this.dialog.hide();
52177                             
52178                             if (Roo.get('loading-mask')) {
52179                                 Roo.get('loading-mask').show();
52180                             }
52181                             Roo.XComponent.build();
52182                             
52183                              
52184                             
52185                         }
52186                     },
52187                     items : [
52188                         {
52189                             xtype : 'TextField',
52190                             xns : Roo.form,
52191                             fieldLabel: "Email Address",
52192                             name: 'username',
52193                             width:200,
52194                             autoCreate : {tag: "input", type: "text", size: "20"}
52195                         },
52196                         {
52197                             xtype : 'TextField',
52198                             xns : Roo.form,
52199                             fieldLabel: "Password",
52200                             inputType: 'password',
52201                             name: 'password',
52202                             width:200,
52203                             autoCreate : {tag: "input", type: "text", size: "20"},
52204                             listeners : {
52205                                 specialkey : function(e,ev) {
52206                                     if (ev.keyCode == 13) {
52207                                         this.form.dialog.el.mask("Logging in");
52208                                         this.form.doAction('submit', {
52209                                             url: this.form.dialog.url,
52210                                             method: this.form.dialog.method
52211                                         });
52212                                     }
52213                                 }
52214                             }  
52215                         },
52216                         {
52217                             xtype : 'ComboBox',
52218                             xns : Roo.form,
52219                             fieldLabel: "Language",
52220                             name : 'langdisp',
52221                             store: {
52222                                 xtype : 'SimpleStore',
52223                                 fields: ['lang', 'ldisp'],
52224                                 data : [
52225                                     [ 'en', 'English' ],
52226                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52227                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52228                                 ]
52229                             },
52230                             
52231                             valueField : 'lang',
52232                             hiddenName:  'lang',
52233                             width: 200,
52234                             displayField:'ldisp',
52235                             typeAhead: false,
52236                             editable: false,
52237                             mode: 'local',
52238                             triggerAction: 'all',
52239                             emptyText:'Select a Language...',
52240                             selectOnFocus:true,
52241                             listeners : {
52242                                 select :  function(cb, rec, ix) {
52243                                     this.form.switchLang(rec.data.lang);
52244                                 }
52245                             }
52246                         
52247                         }
52248                     ]
52249                 }
52250                   
52251                 
52252             ]
52253         }
52254     ],
52255     buttons : [
52256         {
52257             xtype : 'Button',
52258             xns : 'Roo',
52259             text : "Forgot Password",
52260             listeners : {
52261                 click : function() {
52262                     //console.log(this);
52263                     var n = this.form.findField('username').getValue();
52264                     if (!n.length) {
52265                         Roo.MessageBox.alert("Error", "Fill in your email address");
52266                         return;
52267                     }
52268                     Roo.Ajax.request({
52269                         url: this.dialog.url,
52270                         params: {
52271                             passwordRequest: n
52272                         },
52273                         method: this.dialog.method,
52274                         success:  function(response, opts)  {  // check successfull...
52275                         
52276                             var res = this.dialog.processResponse(response);
52277                             if (!res.success) { // error!
52278                                Roo.MessageBox.alert("Error" ,
52279                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52280                                return;
52281                             }
52282                             Roo.MessageBox.alert("Notice" ,
52283                                 "Please check you email for the Password Reset message");
52284                         },
52285                         failure : function() {
52286                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52287                         }
52288                         
52289                     });
52290                 }
52291             }
52292         },
52293         {
52294             xtype : 'Button',
52295             xns : 'Roo',
52296             text : "Login",
52297             listeners : {
52298                 
52299                 click : function () {
52300                         
52301                     this.dialog.el.mask("Logging in");
52302                     this.form.doAction('submit', {
52303                             url: this.dialog.url,
52304                             method: this.dialog.method
52305                     });
52306                 }
52307             }
52308         }
52309     ]
52310   
52311   
52312 })
52313  
52314
52315
52316