roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   T      CST        Timezone setting of the machine running the code
1011   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1012 </pre>
1013  *
1014  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1015  * <pre><code>
1016 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1017 document.write(dt.format('Y-m-d'));                         //2007-01-10
1018 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1019 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1020  </code></pre>
1021  *
1022  * Here are some standard date/time patterns that you might find helpful.  They
1023  * are not part of the source of Date.js, but to use them you can simply copy this
1024  * block of code into any script that is included after Date.js and they will also become
1025  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1026  * <pre><code>
1027 Date.patterns = {
1028     ISO8601Long:"Y-m-d H:i:s",
1029     ISO8601Short:"Y-m-d",
1030     ShortDate: "n/j/Y",
1031     LongDate: "l, F d, Y",
1032     FullDateTime: "l, F d, Y g:i:s A",
1033     MonthDay: "F d",
1034     ShortTime: "g:i A",
1035     LongTime: "g:i:s A",
1036     SortableDateTime: "Y-m-d\\TH:i:s",
1037     UniversalSortableDateTime: "Y-m-d H:i:sO",
1038     YearMonth: "F, Y"
1039 };
1040 </code></pre>
1041  *
1042  * Example usage:
1043  * <pre><code>
1044 var dt = new Date();
1045 document.write(dt.format(Date.patterns.ShortDate));
1046  </code></pre>
1047  */
1048
1049 /*
1050  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1051  * They generate precompiled functions from date formats instead of parsing and
1052  * processing the pattern every time you format a date.  These functions are available
1053  * on every Date object (any javascript function).
1054  *
1055  * The original article and download are here:
1056  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1057  *
1058  */
1059  
1060  
1061  // was in core
1062 /**
1063  Returns the number of milliseconds between this date and date
1064  @param {Date} date (optional) Defaults to now
1065  @return {Number} The diff in milliseconds
1066  @member Date getElapsed
1067  */
1068 Date.prototype.getElapsed = function(date) {
1069         return Math.abs((date || new Date()).getTime()-this.getTime());
1070 };
1071 // was in date file..
1072
1073
1074 // private
1075 Date.parseFunctions = {count:0};
1076 // private
1077 Date.parseRegexes = [];
1078 // private
1079 Date.formatFunctions = {count:0};
1080
1081 // private
1082 Date.prototype.dateFormat = function(format) {
1083     if (Date.formatFunctions[format] == null) {
1084         Date.createNewFormat(format);
1085     }
1086     var func = Date.formatFunctions[format];
1087     return this[func]();
1088 };
1089
1090
1091 /**
1092  * Formats a date given the supplied format string
1093  * @param {String} format The format string
1094  * @return {String} The formatted date
1095  * @method
1096  */
1097 Date.prototype.format = Date.prototype.dateFormat;
1098
1099 // private
1100 Date.createNewFormat = function(format) {
1101     var funcName = "format" + Date.formatFunctions.count++;
1102     Date.formatFunctions[format] = funcName;
1103     var code = "Date.prototype." + funcName + " = function(){return ";
1104     var special = false;
1105     var ch = '';
1106     for (var i = 0; i < format.length; ++i) {
1107         ch = format.charAt(i);
1108         if (!special && ch == "\\") {
1109             special = true;
1110         }
1111         else if (special) {
1112             special = false;
1113             code += "'" + String.escape(ch) + "' + ";
1114         }
1115         else {
1116             code += Date.getFormatCode(ch);
1117         }
1118     }
1119     /** eval:var:zzzzzzzzzzzzz */
1120     eval(code.substring(0, code.length - 3) + ";}");
1121 };
1122
1123 // private
1124 Date.getFormatCode = function(character) {
1125     switch (character) {
1126     case "d":
1127         return "String.leftPad(this.getDate(), 2, '0') + ";
1128     case "D":
1129         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1130     case "j":
1131         return "this.getDate() + ";
1132     case "l":
1133         return "Date.dayNames[this.getDay()] + ";
1134     case "S":
1135         return "this.getSuffix() + ";
1136     case "w":
1137         return "this.getDay() + ";
1138     case "z":
1139         return "this.getDayOfYear() + ";
1140     case "W":
1141         return "this.getWeekOfYear() + ";
1142     case "F":
1143         return "Date.monthNames[this.getMonth()] + ";
1144     case "m":
1145         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1146     case "M":
1147         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1148     case "n":
1149         return "(this.getMonth() + 1) + ";
1150     case "t":
1151         return "this.getDaysInMonth() + ";
1152     case "L":
1153         return "(this.isLeapYear() ? 1 : 0) + ";
1154     case "Y":
1155         return "this.getFullYear() + ";
1156     case "y":
1157         return "('' + this.getFullYear()).substring(2, 4) + ";
1158     case "a":
1159         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1160     case "A":
1161         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1162     case "g":
1163         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1164     case "G":
1165         return "this.getHours() + ";
1166     case "h":
1167         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1168     case "H":
1169         return "String.leftPad(this.getHours(), 2, '0') + ";
1170     case "i":
1171         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1172     case "s":
1173         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1174     case "O":
1175         return "this.getGMTOffset() + ";
1176     case "T":
1177         return "this.getTimezone() + ";
1178     case "Z":
1179         return "(this.getTimezoneOffset() * -60) + ";
1180     default:
1181         return "'" + String.escape(character) + "' + ";
1182     }
1183 };
1184
1185 /**
1186  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1187  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1188  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1189  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1190  * string or the parse operation will fail.
1191  * Example Usage:
1192 <pre><code>
1193 //dt = Fri May 25 2007 (current date)
1194 var dt = new Date();
1195
1196 //dt = Thu May 25 2006 (today's month/day in 2006)
1197 dt = Date.parseDate("2006", "Y");
1198
1199 //dt = Sun Jan 15 2006 (all date parts specified)
1200 dt = Date.parseDate("2006-1-15", "Y-m-d");
1201
1202 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1203 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1204 </code></pre>
1205  * @param {String} input The unparsed date as a string
1206  * @param {String} format The format the date is in
1207  * @return {Date} The parsed date
1208  * @static
1209  */
1210 Date.parseDate = function(input, format) {
1211     if (Date.parseFunctions[format] == null) {
1212         Date.createParser(format);
1213     }
1214     var func = Date.parseFunctions[format];
1215     return Date[func](input);
1216 };
1217 /**
1218  * @private
1219  */
1220 Date.createParser = function(format) {
1221     var funcName = "parse" + Date.parseFunctions.count++;
1222     var regexNum = Date.parseRegexes.length;
1223     var currentGroup = 1;
1224     Date.parseFunctions[format] = funcName;
1225
1226     var code = "Date." + funcName + " = function(input){\n"
1227         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1228         + "var d = new Date();\n"
1229         + "y = d.getFullYear();\n"
1230         + "m = d.getMonth();\n"
1231         + "d = d.getDate();\n"
1232         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1233         + "if (results && results.length > 0) {";
1234     var regex = "";
1235
1236     var special = false;
1237     var ch = '';
1238     for (var i = 0; i < format.length; ++i) {
1239         ch = format.charAt(i);
1240         if (!special && ch == "\\") {
1241             special = true;
1242         }
1243         else if (special) {
1244             special = false;
1245             regex += String.escape(ch);
1246         }
1247         else {
1248             var obj = Date.formatCodeToRegex(ch, currentGroup);
1249             currentGroup += obj.g;
1250             regex += obj.s;
1251             if (obj.g && obj.c) {
1252                 code += obj.c;
1253             }
1254         }
1255     }
1256
1257     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1258         + "{v = new Date(y, m, d, h, i, s);}\n"
1259         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1260         + "{v = new Date(y, m, d, h, i);}\n"
1261         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1262         + "{v = new Date(y, m, d, h);}\n"
1263         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1264         + "{v = new Date(y, m, d);}\n"
1265         + "else if (y >= 0 && m >= 0)\n"
1266         + "{v = new Date(y, m);}\n"
1267         + "else if (y >= 0)\n"
1268         + "{v = new Date(y);}\n"
1269         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1270         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1271         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1272         + ";}";
1273
1274     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1275     /** eval:var:zzzzzzzzzzzzz */
1276     eval(code);
1277 };
1278
1279 // private
1280 Date.formatCodeToRegex = function(character, currentGroup) {
1281     switch (character) {
1282     case "D":
1283         return {g:0,
1284         c:null,
1285         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1286     case "j":
1287         return {g:1,
1288             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1289             s:"(\\d{1,2})"}; // day of month without leading zeroes
1290     case "d":
1291         return {g:1,
1292             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{2})"}; // day of month with leading zeroes
1294     case "l":
1295         return {g:0,
1296             c:null,
1297             s:"(?:" + Date.dayNames.join("|") + ")"};
1298     case "S":
1299         return {g:0,
1300             c:null,
1301             s:"(?:st|nd|rd|th)"};
1302     case "w":
1303         return {g:0,
1304             c:null,
1305             s:"\\d"};
1306     case "z":
1307         return {g:0,
1308             c:null,
1309             s:"(?:\\d{1,3})"};
1310     case "W":
1311         return {g:0,
1312             c:null,
1313             s:"(?:\\d{2})"};
1314     case "F":
1315         return {g:1,
1316             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1317             s:"(" + Date.monthNames.join("|") + ")"};
1318     case "M":
1319         return {g:1,
1320             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1321             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1322     case "n":
1323         return {g:1,
1324             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1325             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1326     case "m":
1327         return {g:1,
1328             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1329             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1330     case "t":
1331         return {g:0,
1332             c:null,
1333             s:"\\d{1,2}"};
1334     case "L":
1335         return {g:0,
1336             c:null,
1337             s:"(?:1|0)"};
1338     case "Y":
1339         return {g:1,
1340             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1341             s:"(\\d{4})"};
1342     case "y":
1343         return {g:1,
1344             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1345                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1346             s:"(\\d{1,2})"};
1347     case "a":
1348         return {g:1,
1349             c:"if (results[" + currentGroup + "] == 'am') {\n"
1350                 + "if (h == 12) { h = 0; }\n"
1351                 + "} else { if (h < 12) { h += 12; }}",
1352             s:"(am|pm)"};
1353     case "A":
1354         return {g:1,
1355             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1356                 + "if (h == 12) { h = 0; }\n"
1357                 + "} else { if (h < 12) { h += 12; }}",
1358             s:"(AM|PM)"};
1359     case "g":
1360     case "G":
1361         return {g:1,
1362             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1364     case "h":
1365     case "H":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1369     case "i":
1370         return {g:1,
1371             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{2})"};
1373     case "s":
1374         return {g:1,
1375             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"};
1377     case "O":
1378         return {g:1,
1379             c:[
1380                 "o = results[", currentGroup, "];\n",
1381                 "var sn = o.substring(0,1);\n", // get + / - sign
1382                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1383                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1384                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1385                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1386             ].join(""),
1387             s:"([+\-]\\d{4})"};
1388     case "T":
1389         return {g:0,
1390             c:null,
1391             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1392     case "Z":
1393         return {g:1,
1394             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1395                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1396             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1397     default:
1398         return {g:0,
1399             c:null,
1400             s:String.escape(character)};
1401     }
1402 };
1403
1404 /**
1405  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1406  * @return {String} The abbreviated timezone name (e.g. 'CST')
1407  */
1408 Date.prototype.getTimezone = function() {
1409     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1410 };
1411
1412 /**
1413  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1414  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1415  */
1416 Date.prototype.getGMTOffset = function() {
1417     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1418         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1419         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1420 };
1421
1422 /**
1423  * Get the numeric day number of the year, adjusted for leap year.
1424  * @return {Number} 0 through 364 (365 in leap years)
1425  */
1426 Date.prototype.getDayOfYear = function() {
1427     var num = 0;
1428     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1429     for (var i = 0; i < this.getMonth(); ++i) {
1430         num += Date.daysInMonth[i];
1431     }
1432     return num + this.getDate() - 1;
1433 };
1434
1435 /**
1436  * Get the string representation of the numeric week number of the year
1437  * (equivalent to the format specifier 'W').
1438  * @return {String} '00' through '52'
1439  */
1440 Date.prototype.getWeekOfYear = function() {
1441     // Skip to Thursday of this week
1442     var now = this.getDayOfYear() + (4 - this.getDay());
1443     // Find the first Thursday of the year
1444     var jan1 = new Date(this.getFullYear(), 0, 1);
1445     var then = (7 - jan1.getDay() + 4);
1446     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1447 };
1448
1449 /**
1450  * Whether or not the current date is in a leap year.
1451  * @return {Boolean} True if the current date is in a leap year, else false
1452  */
1453 Date.prototype.isLeapYear = function() {
1454     var year = this.getFullYear();
1455     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1456 };
1457
1458 /**
1459  * Get the first day of the current month, adjusted for leap year.  The returned value
1460  * is the numeric day index within the week (0-6) which can be used in conjunction with
1461  * the {@link #monthNames} array to retrieve the textual day name.
1462  * Example:
1463  *<pre><code>
1464 var dt = new Date('1/10/2007');
1465 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1466 </code></pre>
1467  * @return {Number} The day number (0-6)
1468  */
1469 Date.prototype.getFirstDayOfMonth = function() {
1470     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1471     return (day < 0) ? (day + 7) : day;
1472 };
1473
1474 /**
1475  * Get the last day of the current month, adjusted for leap year.  The returned value
1476  * is the numeric day index within the week (0-6) which can be used in conjunction with
1477  * the {@link #monthNames} array to retrieve the textual day name.
1478  * Example:
1479  *<pre><code>
1480 var dt = new Date('1/10/2007');
1481 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1482 </code></pre>
1483  * @return {Number} The day number (0-6)
1484  */
1485 Date.prototype.getLastDayOfMonth = function() {
1486     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1487     return (day < 0) ? (day + 7) : day;
1488 };
1489
1490
1491 /**
1492  * Get the first date of this date's month
1493  * @return {Date}
1494  */
1495 Date.prototype.getFirstDateOfMonth = function() {
1496     return new Date(this.getFullYear(), this.getMonth(), 1);
1497 };
1498
1499 /**
1500  * Get the last date of this date's month
1501  * @return {Date}
1502  */
1503 Date.prototype.getLastDateOfMonth = function() {
1504     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1505 };
1506 /**
1507  * Get the number of days in the current month, adjusted for leap year.
1508  * @return {Number} The number of days in the month
1509  */
1510 Date.prototype.getDaysInMonth = function() {
1511     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1512     return Date.daysInMonth[this.getMonth()];
1513 };
1514
1515 /**
1516  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1517  * @return {String} 'st, 'nd', 'rd' or 'th'
1518  */
1519 Date.prototype.getSuffix = function() {
1520     switch (this.getDate()) {
1521         case 1:
1522         case 21:
1523         case 31:
1524             return "st";
1525         case 2:
1526         case 22:
1527             return "nd";
1528         case 3:
1529         case 23:
1530             return "rd";
1531         default:
1532             return "th";
1533     }
1534 };
1535
1536 // private
1537 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1538
1539 /**
1540  * An array of textual month names.
1541  * Override these values for international dates, for example...
1542  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1543  * @type Array
1544  * @static
1545  */
1546 Date.monthNames =
1547    ["January",
1548     "February",
1549     "March",
1550     "April",
1551     "May",
1552     "June",
1553     "July",
1554     "August",
1555     "September",
1556     "October",
1557     "November",
1558     "December"];
1559
1560 /**
1561  * An array of textual day names.
1562  * Override these values for international dates, for example...
1563  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1564  * @type Array
1565  * @static
1566  */
1567 Date.dayNames =
1568    ["Sunday",
1569     "Monday",
1570     "Tuesday",
1571     "Wednesday",
1572     "Thursday",
1573     "Friday",
1574     "Saturday"];
1575
1576 // private
1577 Date.y2kYear = 50;
1578 // private
1579 Date.monthNumbers = {
1580     Jan:0,
1581     Feb:1,
1582     Mar:2,
1583     Apr:3,
1584     May:4,
1585     Jun:5,
1586     Jul:6,
1587     Aug:7,
1588     Sep:8,
1589     Oct:9,
1590     Nov:10,
1591     Dec:11};
1592
1593 /**
1594  * Creates and returns a new Date instance with the exact same date value as the called instance.
1595  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1596  * variable will also be changed.  When the intention is to create a new variable that will not
1597  * modify the original instance, you should create a clone.
1598  *
1599  * Example of correctly cloning a date:
1600  * <pre><code>
1601 //wrong way:
1602 var orig = new Date('10/1/2006');
1603 var copy = orig;
1604 copy.setDate(5);
1605 document.write(orig);  //returns 'Thu Oct 05 2006'!
1606
1607 //correct way:
1608 var orig = new Date('10/1/2006');
1609 var copy = orig.clone();
1610 copy.setDate(5);
1611 document.write(orig);  //returns 'Thu Oct 01 2006'
1612 </code></pre>
1613  * @return {Date} The new Date instance
1614  */
1615 Date.prototype.clone = function() {
1616         return new Date(this.getTime());
1617 };
1618
1619 /**
1620  * Clears any time information from this date
1621  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1622  @return {Date} this or the clone
1623  */
1624 Date.prototype.clearTime = function(clone){
1625     if(clone){
1626         return this.clone().clearTime();
1627     }
1628     this.setHours(0);
1629     this.setMinutes(0);
1630     this.setSeconds(0);
1631     this.setMilliseconds(0);
1632     return this;
1633 };
1634
1635 // private
1636 // safari setMonth is broken
1637 if(Roo.isSafari){
1638     Date.brokenSetMonth = Date.prototype.setMonth;
1639         Date.prototype.setMonth = function(num){
1640                 if(num <= -1){
1641                         var n = Math.ceil(-num);
1642                         var back_year = Math.ceil(n/12);
1643                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1644                         this.setFullYear(this.getFullYear() - back_year);
1645                         return Date.brokenSetMonth.call(this, month);
1646                 } else {
1647                         return Date.brokenSetMonth.apply(this, arguments);
1648                 }
1649         };
1650 }
1651
1652 /** Date interval constant 
1653 * @static 
1654 * @type String */
1655 Date.MILLI = "ms";
1656 /** Date interval constant 
1657 * @static 
1658 * @type String */
1659 Date.SECOND = "s";
1660 /** Date interval constant 
1661 * @static 
1662 * @type String */
1663 Date.MINUTE = "mi";
1664 /** Date interval constant 
1665 * @static 
1666 * @type String */
1667 Date.HOUR = "h";
1668 /** Date interval constant 
1669 * @static 
1670 * @type String */
1671 Date.DAY = "d";
1672 /** Date interval constant 
1673 * @static 
1674 * @type String */
1675 Date.MONTH = "mo";
1676 /** Date interval constant 
1677 * @static 
1678 * @type String */
1679 Date.YEAR = "y";
1680
1681 /**
1682  * Provides a convenient method of performing basic date arithmetic.  This method
1683  * does not modify the Date instance being called - it creates and returns
1684  * a new Date instance containing the resulting date value.
1685  *
1686  * Examples:
1687  * <pre><code>
1688 //Basic usage:
1689 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1690 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1691
1692 //Negative values will subtract correctly:
1693 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1694 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1695
1696 //You can even chain several calls together in one line!
1697 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1698 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1699  </code></pre>
1700  *
1701  * @param {String} interval   A valid date interval enum value
1702  * @param {Number} value      The amount to add to the current date
1703  * @return {Date} The new Date instance
1704  */
1705 Date.prototype.add = function(interval, value){
1706   var d = this.clone();
1707   if (!interval || value === 0) return d;
1708   switch(interval.toLowerCase()){
1709     case Date.MILLI:
1710       d.setMilliseconds(this.getMilliseconds() + value);
1711       break;
1712     case Date.SECOND:
1713       d.setSeconds(this.getSeconds() + value);
1714       break;
1715     case Date.MINUTE:
1716       d.setMinutes(this.getMinutes() + value);
1717       break;
1718     case Date.HOUR:
1719       d.setHours(this.getHours() + value);
1720       break;
1721     case Date.DAY:
1722       d.setDate(this.getDate() + value);
1723       break;
1724     case Date.MONTH:
1725       var day = this.getDate();
1726       if(day > 28){
1727           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1728       }
1729       d.setDate(day);
1730       d.setMonth(this.getMonth() + value);
1731       break;
1732     case Date.YEAR:
1733       d.setFullYear(this.getFullYear() + value);
1734       break;
1735   }
1736   return d;
1737 };/*
1738  * Based on:
1739  * Ext JS Library 1.1.1
1740  * Copyright(c) 2006-2007, Ext JS, LLC.
1741  *
1742  * Originally Released Under LGPL - original licence link has changed is not relivant.
1743  *
1744  * Fork - LGPL
1745  * <script type="text/javascript">
1746  */
1747
1748 Roo.lib.Dom = {
1749     getViewWidth : function(full) {
1750         return full ? this.getDocumentWidth() : this.getViewportWidth();
1751     },
1752
1753     getViewHeight : function(full) {
1754         return full ? this.getDocumentHeight() : this.getViewportHeight();
1755     },
1756
1757     getDocumentHeight: function() {
1758         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1759         return Math.max(scrollHeight, this.getViewportHeight());
1760     },
1761
1762     getDocumentWidth: function() {
1763         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1764         return Math.max(scrollWidth, this.getViewportWidth());
1765     },
1766
1767     getViewportHeight: function() {
1768         var height = self.innerHeight;
1769         var mode = document.compatMode;
1770
1771         if ((mode || Roo.isIE) && !Roo.isOpera) {
1772             height = (mode == "CSS1Compat") ?
1773                      document.documentElement.clientHeight :
1774                      document.body.clientHeight;
1775         }
1776
1777         return height;
1778     },
1779
1780     getViewportWidth: function() {
1781         var width = self.innerWidth;
1782         var mode = document.compatMode;
1783
1784         if (mode || Roo.isIE) {
1785             width = (mode == "CSS1Compat") ?
1786                     document.documentElement.clientWidth :
1787                     document.body.clientWidth;
1788         }
1789         return width;
1790     },
1791
1792     isAncestor : function(p, c) {
1793         p = Roo.getDom(p);
1794         c = Roo.getDom(c);
1795         if (!p || !c) {
1796             return false;
1797         }
1798
1799         if (p.contains && !Roo.isSafari) {
1800             return p.contains(c);
1801         } else if (p.compareDocumentPosition) {
1802             return !!(p.compareDocumentPosition(c) & 16);
1803         } else {
1804             var parent = c.parentNode;
1805             while (parent) {
1806                 if (parent == p) {
1807                     return true;
1808                 }
1809                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1810                     return false;
1811                 }
1812                 parent = parent.parentNode;
1813             }
1814             return false;
1815         }
1816     },
1817
1818     getRegion : function(el) {
1819         return Roo.lib.Region.getRegion(el);
1820     },
1821
1822     getY : function(el) {
1823         return this.getXY(el)[1];
1824     },
1825
1826     getX : function(el) {
1827         return this.getXY(el)[0];
1828     },
1829
1830     getXY : function(el) {
1831         var p, pe, b, scroll, bd = document.body;
1832         el = Roo.getDom(el);
1833         var fly = Roo.lib.AnimBase.fly;
1834         if (el.getBoundingClientRect) {
1835             b = el.getBoundingClientRect();
1836             scroll = fly(document).getScroll();
1837             return [b.left + scroll.left, b.top + scroll.top];
1838         }
1839         var x = 0, y = 0;
1840
1841         p = el;
1842
1843         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1844
1845         while (p) {
1846
1847             x += p.offsetLeft;
1848             y += p.offsetTop;
1849
1850             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1851                 hasAbsolute = true;
1852             }
1853
1854             if (Roo.isGecko) {
1855                 pe = fly(p);
1856
1857                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1858                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1859
1860
1861                 x += bl;
1862                 y += bt;
1863
1864
1865                 if (p != el && pe.getStyle('overflow') != 'visible') {
1866                     x += bl;
1867                     y += bt;
1868                 }
1869             }
1870             p = p.offsetParent;
1871         }
1872
1873         if (Roo.isSafari && hasAbsolute) {
1874             x -= bd.offsetLeft;
1875             y -= bd.offsetTop;
1876         }
1877
1878         if (Roo.isGecko && !hasAbsolute) {
1879             var dbd = fly(bd);
1880             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1881             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1882         }
1883
1884         p = el.parentNode;
1885         while (p && p != bd) {
1886             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1887                 x -= p.scrollLeft;
1888                 y -= p.scrollTop;
1889             }
1890             p = p.parentNode;
1891         }
1892         return [x, y];
1893     },
1894  
1895   
1896
1897
1898     setXY : function(el, xy) {
1899         el = Roo.fly(el, '_setXY');
1900         el.position();
1901         var pts = el.translatePoints(xy);
1902         if (xy[0] !== false) {
1903             el.dom.style.left = pts.left + "px";
1904         }
1905         if (xy[1] !== false) {
1906             el.dom.style.top = pts.top + "px";
1907         }
1908     },
1909
1910     setX : function(el, x) {
1911         this.setXY(el, [x, false]);
1912     },
1913
1914     setY : function(el, y) {
1915         this.setXY(el, [false, y]);
1916     }
1917 };
1918 /*
1919  * Portions of this file are based on pieces of Yahoo User Interface Library
1920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1921  * YUI licensed under the BSD License:
1922  * http://developer.yahoo.net/yui/license.txt
1923  * <script type="text/javascript">
1924  *
1925  */
1926
1927 Roo.lib.Event = function() {
1928     var loadComplete = false;
1929     var listeners = [];
1930     var unloadListeners = [];
1931     var retryCount = 0;
1932     var onAvailStack = [];
1933     var counter = 0;
1934     var lastError = null;
1935
1936     return {
1937         POLL_RETRYS: 200,
1938         POLL_INTERVAL: 20,
1939         EL: 0,
1940         TYPE: 1,
1941         FN: 2,
1942         WFN: 3,
1943         OBJ: 3,
1944         ADJ_SCOPE: 4,
1945         _interval: null,
1946
1947         startInterval: function() {
1948             if (!this._interval) {
1949                 var self = this;
1950                 var callback = function() {
1951                     self._tryPreloadAttach();
1952                 };
1953                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1954
1955             }
1956         },
1957
1958         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1959             onAvailStack.push({ id:         p_id,
1960                 fn:         p_fn,
1961                 obj:        p_obj,
1962                 override:   p_override,
1963                 checkReady: false    });
1964
1965             retryCount = this.POLL_RETRYS;
1966             this.startInterval();
1967         },
1968
1969
1970         addListener: function(el, eventName, fn) {
1971             el = Roo.getDom(el);
1972             if (!el || !fn) {
1973                 return false;
1974             }
1975
1976             if ("unload" == eventName) {
1977                 unloadListeners[unloadListeners.length] =
1978                 [el, eventName, fn];
1979                 return true;
1980             }
1981
1982             var wrappedFn = function(e) {
1983                 return fn(Roo.lib.Event.getEvent(e));
1984             };
1985
1986             var li = [el, eventName, fn, wrappedFn];
1987
1988             var index = listeners.length;
1989             listeners[index] = li;
1990
1991             this.doAdd(el, eventName, wrappedFn, false);
1992             return true;
1993
1994         },
1995
1996
1997         removeListener: function(el, eventName, fn) {
1998             var i, len;
1999
2000             el = Roo.getDom(el);
2001
2002             if(!fn) {
2003                 return this.purgeElement(el, false, eventName);
2004             }
2005
2006
2007             if ("unload" == eventName) {
2008
2009                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2010                     var li = unloadListeners[i];
2011                     if (li &&
2012                         li[0] == el &&
2013                         li[1] == eventName &&
2014                         li[2] == fn) {
2015                         unloadListeners.splice(i, 1);
2016                         return true;
2017                     }
2018                 }
2019
2020                 return false;
2021             }
2022
2023             var cacheItem = null;
2024
2025
2026             var index = arguments[3];
2027
2028             if ("undefined" == typeof index) {
2029                 index = this._getCacheIndex(el, eventName, fn);
2030             }
2031
2032             if (index >= 0) {
2033                 cacheItem = listeners[index];
2034             }
2035
2036             if (!el || !cacheItem) {
2037                 return false;
2038             }
2039
2040             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2041
2042             delete listeners[index][this.WFN];
2043             delete listeners[index][this.FN];
2044             listeners.splice(index, 1);
2045
2046             return true;
2047
2048         },
2049
2050
2051         getTarget: function(ev, resolveTextNode) {
2052             ev = ev.browserEvent || ev;
2053             var t = ev.target || ev.srcElement;
2054             return this.resolveTextNode(t);
2055         },
2056
2057
2058         resolveTextNode: function(node) {
2059             if (Roo.isSafari && node && 3 == node.nodeType) {
2060                 return node.parentNode;
2061             } else {
2062                 return node;
2063             }
2064         },
2065
2066
2067         getPageX: function(ev) {
2068             ev = ev.browserEvent || ev;
2069             var x = ev.pageX;
2070             if (!x && 0 !== x) {
2071                 x = ev.clientX || 0;
2072
2073                 if (Roo.isIE) {
2074                     x += this.getScroll()[1];
2075                 }
2076             }
2077
2078             return x;
2079         },
2080
2081
2082         getPageY: function(ev) {
2083             ev = ev.browserEvent || ev;
2084             var y = ev.pageY;
2085             if (!y && 0 !== y) {
2086                 y = ev.clientY || 0;
2087
2088                 if (Roo.isIE) {
2089                     y += this.getScroll()[0];
2090                 }
2091             }
2092
2093
2094             return y;
2095         },
2096
2097
2098         getXY: function(ev) {
2099             ev = ev.browserEvent || ev;
2100             return [this.getPageX(ev), this.getPageY(ev)];
2101         },
2102
2103
2104         getRelatedTarget: function(ev) {
2105             ev = ev.browserEvent || ev;
2106             var t = ev.relatedTarget;
2107             if (!t) {
2108                 if (ev.type == "mouseout") {
2109                     t = ev.toElement;
2110                 } else if (ev.type == "mouseover") {
2111                     t = ev.fromElement;
2112                 }
2113             }
2114
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         getTime: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             if (!ev.time) {
2122                 var t = new Date().getTime();
2123                 try {
2124                     ev.time = t;
2125                 } catch(ex) {
2126                     this.lastError = ex;
2127                     return t;
2128                 }
2129             }
2130
2131             return ev.time;
2132         },
2133
2134
2135         stopEvent: function(ev) {
2136             this.stopPropagation(ev);
2137             this.preventDefault(ev);
2138         },
2139
2140
2141         stopPropagation: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             if (ev.stopPropagation) {
2144                 ev.stopPropagation();
2145             } else {
2146                 ev.cancelBubble = true;
2147             }
2148         },
2149
2150
2151         preventDefault: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             if(ev.preventDefault) {
2154                 ev.preventDefault();
2155             } else {
2156                 ev.returnValue = false;
2157             }
2158         },
2159
2160
2161         getEvent: function(e) {
2162             var ev = e || window.event;
2163             if (!ev) {
2164                 var c = this.getEvent.caller;
2165                 while (c) {
2166                     ev = c.arguments[0];
2167                     if (ev && Event == ev.constructor) {
2168                         break;
2169                     }
2170                     c = c.caller;
2171                 }
2172             }
2173             return ev;
2174         },
2175
2176
2177         getCharCode: function(ev) {
2178             ev = ev.browserEvent || ev;
2179             return ev.charCode || ev.keyCode || 0;
2180         },
2181
2182
2183         _getCacheIndex: function(el, eventName, fn) {
2184             for (var i = 0,len = listeners.length; i < len; ++i) {
2185                 var li = listeners[i];
2186                 if (li &&
2187                     li[this.FN] == fn &&
2188                     li[this.EL] == el &&
2189                     li[this.TYPE] == eventName) {
2190                     return i;
2191                 }
2192             }
2193
2194             return -1;
2195         },
2196
2197
2198         elCache: {},
2199
2200
2201         getEl: function(id) {
2202             return document.getElementById(id);
2203         },
2204
2205
2206         clearCache: function() {
2207         },
2208
2209
2210         _load: function(e) {
2211             loadComplete = true;
2212             var EU = Roo.lib.Event;
2213
2214
2215             if (Roo.isIE) {
2216                 EU.doRemove(window, "load", EU._load);
2217             }
2218         },
2219
2220
2221         _tryPreloadAttach: function() {
2222
2223             if (this.locked) {
2224                 return false;
2225             }
2226
2227             this.locked = true;
2228
2229
2230             var tryAgain = !loadComplete;
2231             if (!tryAgain) {
2232                 tryAgain = (retryCount > 0);
2233             }
2234
2235
2236             var notAvail = [];
2237             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2238                 var item = onAvailStack[i];
2239                 if (item) {
2240                     var el = this.getEl(item.id);
2241
2242                     if (el) {
2243                         if (!item.checkReady ||
2244                             loadComplete ||
2245                             el.nextSibling ||
2246                             (document && document.body)) {
2247
2248                             var scope = el;
2249                             if (item.override) {
2250                                 if (item.override === true) {
2251                                     scope = item.obj;
2252                                 } else {
2253                                     scope = item.override;
2254                                 }
2255                             }
2256                             item.fn.call(scope, item.obj);
2257                             onAvailStack[i] = null;
2258                         }
2259                     } else {
2260                         notAvail.push(item);
2261                     }
2262                 }
2263             }
2264
2265             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2266
2267             if (tryAgain) {
2268
2269                 this.startInterval();
2270             } else {
2271                 clearInterval(this._interval);
2272                 this._interval = null;
2273             }
2274
2275             this.locked = false;
2276
2277             return true;
2278
2279         },
2280
2281
2282         purgeElement: function(el, recurse, eventName) {
2283             var elListeners = this.getListeners(el, eventName);
2284             if (elListeners) {
2285                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2286                     var l = elListeners[i];
2287                     this.removeListener(el, l.type, l.fn);
2288                 }
2289             }
2290
2291             if (recurse && el && el.childNodes) {
2292                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2293                     this.purgeElement(el.childNodes[i], recurse, eventName);
2294                 }
2295             }
2296         },
2297
2298
2299         getListeners: function(el, eventName) {
2300             var results = [], searchLists;
2301             if (!eventName) {
2302                 searchLists = [listeners, unloadListeners];
2303             } else if (eventName == "unload") {
2304                 searchLists = [unloadListeners];
2305             } else {
2306                 searchLists = [listeners];
2307             }
2308
2309             for (var j = 0; j < searchLists.length; ++j) {
2310                 var searchList = searchLists[j];
2311                 if (searchList && searchList.length > 0) {
2312                     for (var i = 0,len = searchList.length; i < len; ++i) {
2313                         var l = searchList[i];
2314                         if (l && l[this.EL] === el &&
2315                             (!eventName || eventName === l[this.TYPE])) {
2316                             results.push({
2317                                 type:   l[this.TYPE],
2318                                 fn:     l[this.FN],
2319                                 obj:    l[this.OBJ],
2320                                 adjust: l[this.ADJ_SCOPE],
2321                                 index:  i
2322                             });
2323                         }
2324                     }
2325                 }
2326             }
2327
2328             return (results.length) ? results : null;
2329         },
2330
2331
2332         _unload: function(e) {
2333
2334             var EU = Roo.lib.Event, i, j, l, len, index;
2335
2336             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2337                 l = unloadListeners[i];
2338                 if (l) {
2339                     var scope = window;
2340                     if (l[EU.ADJ_SCOPE]) {
2341                         if (l[EU.ADJ_SCOPE] === true) {
2342                             scope = l[EU.OBJ];
2343                         } else {
2344                             scope = l[EU.ADJ_SCOPE];
2345                         }
2346                     }
2347                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2348                     unloadListeners[i] = null;
2349                     l = null;
2350                     scope = null;
2351                 }
2352             }
2353
2354             unloadListeners = null;
2355
2356             if (listeners && listeners.length > 0) {
2357                 j = listeners.length;
2358                 while (j) {
2359                     index = j - 1;
2360                     l = listeners[index];
2361                     if (l) {
2362                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2363                                 l[EU.FN], index);
2364                     }
2365                     j = j - 1;
2366                 }
2367                 l = null;
2368
2369                 EU.clearCache();
2370             }
2371
2372             EU.doRemove(window, "unload", EU._unload);
2373
2374         },
2375
2376
2377         getScroll: function() {
2378             var dd = document.documentElement, db = document.body;
2379             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2380                 return [dd.scrollTop, dd.scrollLeft];
2381             } else if (db) {
2382                 return [db.scrollTop, db.scrollLeft];
2383             } else {
2384                 return [0, 0];
2385             }
2386         },
2387
2388
2389         doAdd: function () {
2390             if (window.addEventListener) {
2391                 return function(el, eventName, fn, capture) {
2392                     el.addEventListener(eventName, fn, (capture));
2393                 };
2394             } else if (window.attachEvent) {
2395                 return function(el, eventName, fn, capture) {
2396                     el.attachEvent("on" + eventName, fn);
2397                 };
2398             } else {
2399                 return function() {
2400                 };
2401             }
2402         }(),
2403
2404
2405         doRemove: function() {
2406             if (window.removeEventListener) {
2407                 return function (el, eventName, fn, capture) {
2408                     el.removeEventListener(eventName, fn, (capture));
2409                 };
2410             } else if (window.detachEvent) {
2411                 return function (el, eventName, fn) {
2412                     el.detachEvent("on" + eventName, fn);
2413                 };
2414             } else {
2415                 return function() {
2416                 };
2417             }
2418         }()
2419     };
2420     
2421 }();
2422 (function() {     
2423    
2424     var E = Roo.lib.Event;
2425     E.on = E.addListener;
2426     E.un = E.removeListener;
2427
2428     if (document && document.body) {
2429         E._load();
2430     } else {
2431         E.doAdd(window, "load", E._load);
2432     }
2433     E.doAdd(window, "unload", E._unload);
2434     E._tryPreloadAttach();
2435 })();
2436
2437 /*
2438  * Portions of this file are based on pieces of Yahoo User Interface Library
2439  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2440  * YUI licensed under the BSD License:
2441  * http://developer.yahoo.net/yui/license.txt
2442  * <script type="text/javascript">
2443  *
2444  */
2445
2446 (function() {
2447     
2448     Roo.lib.Ajax = {
2449         request : function(method, uri, cb, data, options) {
2450             if(options){
2451                 var hs = options.headers;
2452                 if(hs){
2453                     for(var h in hs){
2454                         if(hs.hasOwnProperty(h)){
2455                             this.initHeader(h, hs[h], false);
2456                         }
2457                     }
2458                 }
2459                 if(options.xmlData){
2460                     this.initHeader('Content-Type', 'text/xml', false);
2461                     method = 'POST';
2462                     data = options.xmlData;
2463                 }
2464             }
2465
2466             return this.asyncRequest(method, uri, cb, data);
2467         },
2468
2469         serializeForm : function(form) {
2470             if(typeof form == 'string') {
2471                 form = (document.getElementById(form) || document.forms[form]);
2472             }
2473
2474             var el, name, val, disabled, data = '', hasSubmit = false;
2475             for (var i = 0; i < form.elements.length; i++) {
2476                 el = form.elements[i];
2477                 disabled = form.elements[i].disabled;
2478                 name = form.elements[i].name;
2479                 val = form.elements[i].value;
2480
2481                 if (!disabled && name){
2482                     switch (el.type)
2483                             {
2484                         case 'select-one':
2485                         case 'select-multiple':
2486                             for (var j = 0; j < el.options.length; j++) {
2487                                 if (el.options[j].selected) {
2488                                     if (Roo.isIE) {
2489                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2490                                     }
2491                                     else {
2492                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2493                                     }
2494                                 }
2495                             }
2496                             break;
2497                         case 'radio':
2498                         case 'checkbox':
2499                             if (el.checked) {
2500                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2501                             }
2502                             break;
2503                         case 'file':
2504
2505                         case undefined:
2506
2507                         case 'reset':
2508
2509                         case 'button':
2510
2511                             break;
2512                         case 'submit':
2513                             if(hasSubmit == false) {
2514                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2515                                 hasSubmit = true;
2516                             }
2517                             break;
2518                         default:
2519                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2520                             break;
2521                     }
2522                 }
2523             }
2524             data = data.substr(0, data.length - 1);
2525             return data;
2526         },
2527
2528         headers:{},
2529
2530         hasHeaders:false,
2531
2532         useDefaultHeader:true,
2533
2534         defaultPostHeader:'application/x-www-form-urlencoded',
2535
2536         useDefaultXhrHeader:true,
2537
2538         defaultXhrHeader:'XMLHttpRequest',
2539
2540         hasDefaultHeaders:true,
2541
2542         defaultHeaders:{},
2543
2544         poll:{},
2545
2546         timeout:{},
2547
2548         pollInterval:50,
2549
2550         transactionId:0,
2551
2552         setProgId:function(id)
2553         {
2554             this.activeX.unshift(id);
2555         },
2556
2557         setDefaultPostHeader:function(b)
2558         {
2559             this.useDefaultHeader = b;
2560         },
2561
2562         setDefaultXhrHeader:function(b)
2563         {
2564             this.useDefaultXhrHeader = b;
2565         },
2566
2567         setPollingInterval:function(i)
2568         {
2569             if (typeof i == 'number' && isFinite(i)) {
2570                 this.pollInterval = i;
2571             }
2572         },
2573
2574         createXhrObject:function(transactionId)
2575         {
2576             var obj,http;
2577             try
2578             {
2579
2580                 http = new XMLHttpRequest();
2581
2582                 obj = { conn:http, tId:transactionId };
2583             }
2584             catch(e)
2585             {
2586                 for (var i = 0; i < this.activeX.length; ++i) {
2587                     try
2588                     {
2589
2590                         http = new ActiveXObject(this.activeX[i]);
2591
2592                         obj = { conn:http, tId:transactionId };
2593                         break;
2594                     }
2595                     catch(e) {
2596                     }
2597                 }
2598             }
2599             finally
2600             {
2601                 return obj;
2602             }
2603         },
2604
2605         getConnectionObject:function()
2606         {
2607             var o;
2608             var tId = this.transactionId;
2609
2610             try
2611             {
2612                 o = this.createXhrObject(tId);
2613                 if (o) {
2614                     this.transactionId++;
2615                 }
2616             }
2617             catch(e) {
2618             }
2619             finally
2620             {
2621                 return o;
2622             }
2623         },
2624
2625         asyncRequest:function(method, uri, callback, postData)
2626         {
2627             var o = this.getConnectionObject();
2628
2629             if (!o) {
2630                 return null;
2631             }
2632             else {
2633                 o.conn.open(method, uri, true);
2634
2635                 if (this.useDefaultXhrHeader) {
2636                     if (!this.defaultHeaders['X-Requested-With']) {
2637                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2638                     }
2639                 }
2640
2641                 if(postData && this.useDefaultHeader){
2642                     this.initHeader('Content-Type', this.defaultPostHeader);
2643                 }
2644
2645                  if (this.hasDefaultHeaders || this.hasHeaders) {
2646                     this.setHeader(o);
2647                 }
2648
2649                 this.handleReadyState(o, callback);
2650                 o.conn.send(postData || null);
2651
2652                 return o;
2653             }
2654         },
2655
2656         handleReadyState:function(o, callback)
2657         {
2658             var oConn = this;
2659
2660             if (callback && callback.timeout) {
2661                 this.timeout[o.tId] = window.setTimeout(function() {
2662                     oConn.abort(o, callback, true);
2663                 }, callback.timeout);
2664             }
2665
2666             this.poll[o.tId] = window.setInterval(
2667                     function() {
2668                         if (o.conn && o.conn.readyState == 4) {
2669                             window.clearInterval(oConn.poll[o.tId]);
2670                             delete oConn.poll[o.tId];
2671
2672                             if(callback && callback.timeout) {
2673                                 window.clearTimeout(oConn.timeout[o.tId]);
2674                                 delete oConn.timeout[o.tId];
2675                             }
2676
2677                             oConn.handleTransactionResponse(o, callback);
2678                         }
2679                     }
2680                     , this.pollInterval);
2681         },
2682
2683         handleTransactionResponse:function(o, callback, isAbort)
2684         {
2685
2686             if (!callback) {
2687                 this.releaseObject(o);
2688                 return;
2689             }
2690
2691             var httpStatus, responseObject;
2692
2693             try
2694             {
2695                 if (o.conn.status !== undefined && o.conn.status != 0) {
2696                     httpStatus = o.conn.status;
2697                 }
2698                 else {
2699                     httpStatus = 13030;
2700                 }
2701             }
2702             catch(e) {
2703
2704
2705                 httpStatus = 13030;
2706             }
2707
2708             if (httpStatus >= 200 && httpStatus < 300) {
2709                 responseObject = this.createResponseObject(o, callback.argument);
2710                 if (callback.success) {
2711                     if (!callback.scope) {
2712                         callback.success(responseObject);
2713                     }
2714                     else {
2715
2716
2717                         callback.success.apply(callback.scope, [responseObject]);
2718                     }
2719                 }
2720             }
2721             else {
2722                 switch (httpStatus) {
2723
2724                     case 12002:
2725                     case 12029:
2726                     case 12030:
2727                     case 12031:
2728                     case 12152:
2729                     case 13030:
2730                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2731                         if (callback.failure) {
2732                             if (!callback.scope) {
2733                                 callback.failure(responseObject);
2734                             }
2735                             else {
2736                                 callback.failure.apply(callback.scope, [responseObject]);
2737                             }
2738                         }
2739                         break;
2740                     default:
2741                         responseObject = this.createResponseObject(o, callback.argument);
2742                         if (callback.failure) {
2743                             if (!callback.scope) {
2744                                 callback.failure(responseObject);
2745                             }
2746                             else {
2747                                 callback.failure.apply(callback.scope, [responseObject]);
2748                             }
2749                         }
2750                 }
2751             }
2752
2753             this.releaseObject(o);
2754             responseObject = null;
2755         },
2756
2757         createResponseObject:function(o, callbackArg)
2758         {
2759             var obj = {};
2760             var headerObj = {};
2761
2762             try
2763             {
2764                 var headerStr = o.conn.getAllResponseHeaders();
2765                 var header = headerStr.split('\n');
2766                 for (var i = 0; i < header.length; i++) {
2767                     var delimitPos = header[i].indexOf(':');
2768                     if (delimitPos != -1) {
2769                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2770                     }
2771                 }
2772             }
2773             catch(e) {
2774             }
2775
2776             obj.tId = o.tId;
2777             obj.status = o.conn.status;
2778             obj.statusText = o.conn.statusText;
2779             obj.getResponseHeader = headerObj;
2780             obj.getAllResponseHeaders = headerStr;
2781             obj.responseText = o.conn.responseText;
2782             obj.responseXML = o.conn.responseXML;
2783
2784             if (typeof callbackArg !== undefined) {
2785                 obj.argument = callbackArg;
2786             }
2787
2788             return obj;
2789         },
2790
2791         createExceptionObject:function(tId, callbackArg, isAbort)
2792         {
2793             var COMM_CODE = 0;
2794             var COMM_ERROR = 'communication failure';
2795             var ABORT_CODE = -1;
2796             var ABORT_ERROR = 'transaction aborted';
2797
2798             var obj = {};
2799
2800             obj.tId = tId;
2801             if (isAbort) {
2802                 obj.status = ABORT_CODE;
2803                 obj.statusText = ABORT_ERROR;
2804             }
2805             else {
2806                 obj.status = COMM_CODE;
2807                 obj.statusText = COMM_ERROR;
2808             }
2809
2810             if (callbackArg) {
2811                 obj.argument = callbackArg;
2812             }
2813
2814             return obj;
2815         },
2816
2817         initHeader:function(label, value, isDefault)
2818         {
2819             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2820
2821             if (headerObj[label] === undefined) {
2822                 headerObj[label] = value;
2823             }
2824             else {
2825
2826
2827                 headerObj[label] = value + "," + headerObj[label];
2828             }
2829
2830             if (isDefault) {
2831                 this.hasDefaultHeaders = true;
2832             }
2833             else {
2834                 this.hasHeaders = true;
2835             }
2836         },
2837
2838
2839         setHeader:function(o)
2840         {
2841             if (this.hasDefaultHeaders) {
2842                 for (var prop in this.defaultHeaders) {
2843                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2844                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2845                     }
2846                 }
2847             }
2848
2849             if (this.hasHeaders) {
2850                 for (var prop in this.headers) {
2851                     if (this.headers.hasOwnProperty(prop)) {
2852                         o.conn.setRequestHeader(prop, this.headers[prop]);
2853                     }
2854                 }
2855                 this.headers = {};
2856                 this.hasHeaders = false;
2857             }
2858         },
2859
2860         resetDefaultHeaders:function() {
2861             delete this.defaultHeaders;
2862             this.defaultHeaders = {};
2863             this.hasDefaultHeaders = false;
2864         },
2865
2866         abort:function(o, callback, isTimeout)
2867         {
2868             if(this.isCallInProgress(o)) {
2869                 o.conn.abort();
2870                 window.clearInterval(this.poll[o.tId]);
2871                 delete this.poll[o.tId];
2872                 if (isTimeout) {
2873                     delete this.timeout[o.tId];
2874                 }
2875
2876                 this.handleTransactionResponse(o, callback, true);
2877
2878                 return true;
2879             }
2880             else {
2881                 return false;
2882             }
2883         },
2884
2885
2886         isCallInProgress:function(o)
2887         {
2888             if (o && o.conn) {
2889                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2890             }
2891             else {
2892
2893                 return false;
2894             }
2895         },
2896
2897
2898         releaseObject:function(o)
2899         {
2900
2901             o.conn = null;
2902
2903             o = null;
2904         },
2905
2906         activeX:[
2907         'MSXML2.XMLHTTP.3.0',
2908         'MSXML2.XMLHTTP',
2909         'Microsoft.XMLHTTP'
2910         ]
2911
2912
2913     };
2914 })();/*
2915  * Portions of this file are based on pieces of Yahoo User Interface Library
2916  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2917  * YUI licensed under the BSD License:
2918  * http://developer.yahoo.net/yui/license.txt
2919  * <script type="text/javascript">
2920  *
2921  */
2922
2923 Roo.lib.Region = function(t, r, b, l) {
2924     this.top = t;
2925     this[1] = t;
2926     this.right = r;
2927     this.bottom = b;
2928     this.left = l;
2929     this[0] = l;
2930 };
2931
2932
2933 Roo.lib.Region.prototype = {
2934     contains : function(region) {
2935         return ( region.left >= this.left &&
2936                  region.right <= this.right &&
2937                  region.top >= this.top &&
2938                  region.bottom <= this.bottom    );
2939
2940     },
2941
2942     getArea : function() {
2943         return ( (this.bottom - this.top) * (this.right - this.left) );
2944     },
2945
2946     intersect : function(region) {
2947         var t = Math.max(this.top, region.top);
2948         var r = Math.min(this.right, region.right);
2949         var b = Math.min(this.bottom, region.bottom);
2950         var l = Math.max(this.left, region.left);
2951
2952         if (b >= t && r >= l) {
2953             return new Roo.lib.Region(t, r, b, l);
2954         } else {
2955             return null;
2956         }
2957     },
2958     union : function(region) {
2959         var t = Math.min(this.top, region.top);
2960         var r = Math.max(this.right, region.right);
2961         var b = Math.max(this.bottom, region.bottom);
2962         var l = Math.min(this.left, region.left);
2963
2964         return new Roo.lib.Region(t, r, b, l);
2965     },
2966
2967     adjust : function(t, l, b, r) {
2968         this.top += t;
2969         this.left += l;
2970         this.right += r;
2971         this.bottom += b;
2972         return this;
2973     }
2974 };
2975
2976 Roo.lib.Region.getRegion = function(el) {
2977     var p = Roo.lib.Dom.getXY(el);
2978
2979     var t = p[1];
2980     var r = p[0] + el.offsetWidth;
2981     var b = p[1] + el.offsetHeight;
2982     var l = p[0];
2983
2984     return new Roo.lib.Region(t, r, b, l);
2985 };
2986 /*
2987  * Portions of this file are based on pieces of Yahoo User Interface Library
2988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2989  * YUI licensed under the BSD License:
2990  * http://developer.yahoo.net/yui/license.txt
2991  * <script type="text/javascript">
2992  *
2993  */
2994 //@@dep Roo.lib.Region
2995
2996
2997 Roo.lib.Point = function(x, y) {
2998     if (x instanceof Array) {
2999         y = x[1];
3000         x = x[0];
3001     }
3002     this.x = this.right = this.left = this[0] = x;
3003     this.y = this.top = this.bottom = this[1] = y;
3004 };
3005
3006 Roo.lib.Point.prototype = new Roo.lib.Region();
3007 /*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015  
3016 (function() {   
3017
3018     Roo.lib.Anim = {
3019         scroll : function(el, args, duration, easing, cb, scope) {
3020             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3021         },
3022
3023         motion : function(el, args, duration, easing, cb, scope) {
3024             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3025         },
3026
3027         color : function(el, args, duration, easing, cb, scope) {
3028             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3029         },
3030
3031         run : function(el, args, duration, easing, cb, scope, type) {
3032             type = type || Roo.lib.AnimBase;
3033             if (typeof easing == "string") {
3034                 easing = Roo.lib.Easing[easing];
3035             }
3036             var anim = new type(el, args, duration, easing);
3037             anim.animateX(function() {
3038                 Roo.callback(cb, scope);
3039             });
3040             return anim;
3041         }
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 (function() {    
3053     var libFlyweight;
3054     
3055     function fly(el) {
3056         if (!libFlyweight) {
3057             libFlyweight = new Roo.Element.Flyweight();
3058         }
3059         libFlyweight.dom = el;
3060         return libFlyweight;
3061     }
3062
3063     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3064     
3065    
3066     
3067     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3068         if (el) {
3069             this.init(el, attributes, duration, method);
3070         }
3071     };
3072
3073     Roo.lib.AnimBase.fly = fly;
3074     
3075     
3076     
3077     Roo.lib.AnimBase.prototype = {
3078
3079         toString: function() {
3080             var el = this.getEl();
3081             var id = el.id || el.tagName;
3082             return ("Anim " + id);
3083         },
3084
3085         patterns: {
3086             noNegatives:        /width|height|opacity|padding/i,
3087             offsetAttribute:  /^((width|height)|(top|left))$/,
3088             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3089             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3090         },
3091
3092
3093         doMethod: function(attr, start, end) {
3094             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3095         },
3096
3097
3098         setAttribute: function(attr, val, unit) {
3099             if (this.patterns.noNegatives.test(attr)) {
3100                 val = (val > 0) ? val : 0;
3101             }
3102
3103             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3104         },
3105
3106
3107         getAttribute: function(attr) {
3108             var el = this.getEl();
3109             var val = fly(el).getStyle(attr);
3110
3111             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3112                 return parseFloat(val);
3113             }
3114
3115             var a = this.patterns.offsetAttribute.exec(attr) || [];
3116             var pos = !!( a[3] );
3117             var box = !!( a[2] );
3118
3119
3120             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3121                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3122             } else {
3123                 val = 0;
3124             }
3125
3126             return val;
3127         },
3128
3129
3130         getDefaultUnit: function(attr) {
3131             if (this.patterns.defaultUnit.test(attr)) {
3132                 return 'px';
3133             }
3134
3135             return '';
3136         },
3137
3138         animateX : function(callback, scope) {
3139             var f = function() {
3140                 this.onComplete.removeListener(f);
3141                 if (typeof callback == "function") {
3142                     callback.call(scope || this, this);
3143                 }
3144             };
3145             this.onComplete.addListener(f, this);
3146             this.animate();
3147         },
3148
3149
3150         setRuntimeAttribute: function(attr) {
3151             var start;
3152             var end;
3153             var attributes = this.attributes;
3154
3155             this.runtimeAttributes[attr] = {};
3156
3157             var isset = function(prop) {
3158                 return (typeof prop !== 'undefined');
3159             };
3160
3161             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3162                 return false;
3163             }
3164
3165             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3166
3167
3168             if (isset(attributes[attr]['to'])) {
3169                 end = attributes[attr]['to'];
3170             } else if (isset(attributes[attr]['by'])) {
3171                 if (start.constructor == Array) {
3172                     end = [];
3173                     for (var i = 0, len = start.length; i < len; ++i) {
3174                         end[i] = start[i] + attributes[attr]['by'][i];
3175                     }
3176                 } else {
3177                     end = start + attributes[attr]['by'];
3178                 }
3179             }
3180
3181             this.runtimeAttributes[attr].start = start;
3182             this.runtimeAttributes[attr].end = end;
3183
3184
3185             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3186         },
3187
3188
3189         init: function(el, attributes, duration, method) {
3190
3191             var isAnimated = false;
3192
3193
3194             var startTime = null;
3195
3196
3197             var actualFrames = 0;
3198
3199
3200             el = Roo.getDom(el);
3201
3202
3203             this.attributes = attributes || {};
3204
3205
3206             this.duration = duration || 1;
3207
3208
3209             this.method = method || Roo.lib.Easing.easeNone;
3210
3211
3212             this.useSeconds = true;
3213
3214
3215             this.currentFrame = 0;
3216
3217
3218             this.totalFrames = Roo.lib.AnimMgr.fps;
3219
3220
3221             this.getEl = function() {
3222                 return el;
3223             };
3224
3225
3226             this.isAnimated = function() {
3227                 return isAnimated;
3228             };
3229
3230
3231             this.getStartTime = function() {
3232                 return startTime;
3233             };
3234
3235             this.runtimeAttributes = {};
3236
3237
3238             this.animate = function() {
3239                 if (this.isAnimated()) {
3240                     return false;
3241                 }
3242
3243                 this.currentFrame = 0;
3244
3245                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3246
3247                 Roo.lib.AnimMgr.registerElement(this);
3248             };
3249
3250
3251             this.stop = function(finish) {
3252                 if (finish) {
3253                     this.currentFrame = this.totalFrames;
3254                     this._onTween.fire();
3255                 }
3256                 Roo.lib.AnimMgr.stop(this);
3257             };
3258
3259             var onStart = function() {
3260                 this.onStart.fire();
3261
3262                 this.runtimeAttributes = {};
3263                 for (var attr in this.attributes) {
3264                     this.setRuntimeAttribute(attr);
3265                 }
3266
3267                 isAnimated = true;
3268                 actualFrames = 0;
3269                 startTime = new Date();
3270             };
3271
3272
3273             var onTween = function() {
3274                 var data = {
3275                     duration: new Date() - this.getStartTime(),
3276                     currentFrame: this.currentFrame
3277                 };
3278
3279                 data.toString = function() {
3280                     return (
3281                             'duration: ' + data.duration +
3282                             ', currentFrame: ' + data.currentFrame
3283                             );
3284                 };
3285
3286                 this.onTween.fire(data);
3287
3288                 var runtimeAttributes = this.runtimeAttributes;
3289
3290                 for (var attr in runtimeAttributes) {
3291                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3292                 }
3293
3294                 actualFrames += 1;
3295             };
3296
3297             var onComplete = function() {
3298                 var actual_duration = (new Date() - startTime) / 1000 ;
3299
3300                 var data = {
3301                     duration: actual_duration,
3302                     frames: actualFrames,
3303                     fps: actualFrames / actual_duration
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', frames: ' + data.frames +
3310                             ', fps: ' + data.fps
3311                             );
3312                 };
3313
3314                 isAnimated = false;
3315                 actualFrames = 0;
3316                 this.onComplete.fire(data);
3317             };
3318
3319
3320             this._onStart = new Roo.util.Event(this);
3321             this.onStart = new Roo.util.Event(this);
3322             this.onTween = new Roo.util.Event(this);
3323             this._onTween = new Roo.util.Event(this);
3324             this.onComplete = new Roo.util.Event(this);
3325             this._onComplete = new Roo.util.Event(this);
3326             this._onStart.addListener(onStart);
3327             this._onTween.addListener(onTween);
3328             this._onComplete.addListener(onComplete);
3329         }
3330     };
3331 })();
3332 /*
3333  * Portions of this file are based on pieces of Yahoo User Interface Library
3334  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3335  * YUI licensed under the BSD License:
3336  * http://developer.yahoo.net/yui/license.txt
3337  * <script type="text/javascript">
3338  *
3339  */
3340
3341 Roo.lib.AnimMgr = new function() {
3342
3343         var thread = null;
3344
3345
3346         var queue = [];
3347
3348
3349         var tweenCount = 0;
3350
3351
3352         this.fps = 1000;
3353
3354
3355         this.delay = 1;
3356
3357
3358         this.registerElement = function(tween) {
3359             queue[queue.length] = tween;
3360             tweenCount += 1;
3361             tween._onStart.fire();
3362             this.start();
3363         };
3364
3365
3366         this.unRegister = function(tween, index) {
3367             tween._onComplete.fire();
3368             index = index || getIndex(tween);
3369             if (index != -1) {
3370                 queue.splice(index, 1);
3371             }
3372
3373             tweenCount -= 1;
3374             if (tweenCount <= 0) {
3375                 this.stop();
3376             }
3377         };
3378
3379
3380         this.start = function() {
3381             if (thread === null) {
3382                 thread = setInterval(this.run, this.delay);
3383             }
3384         };
3385
3386
3387         this.stop = function(tween) {
3388             if (!tween) {
3389                 clearInterval(thread);
3390
3391                 for (var i = 0, len = queue.length; i < len; ++i) {
3392                     if (queue[0].isAnimated()) {
3393                         this.unRegister(queue[0], 0);
3394                     }
3395                 }
3396
3397                 queue = [];
3398                 thread = null;
3399                 tweenCount = 0;
3400             }
3401             else {
3402                 this.unRegister(tween);
3403             }
3404         };
3405
3406
3407         this.run = function() {
3408             for (var i = 0, len = queue.length; i < len; ++i) {
3409                 var tween = queue[i];
3410                 if (!tween || !tween.isAnimated()) {
3411                     continue;
3412                 }
3413
3414                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3415                 {
3416                     tween.currentFrame += 1;
3417
3418                     if (tween.useSeconds) {
3419                         correctFrame(tween);
3420                     }
3421                     tween._onTween.fire();
3422                 }
3423                 else {
3424                     Roo.lib.AnimMgr.stop(tween, i);
3425                 }
3426             }
3427         };
3428
3429         var getIndex = function(anim) {
3430             for (var i = 0, len = queue.length; i < len; ++i) {
3431                 if (queue[i] == anim) {
3432                     return i;
3433                 }
3434             }
3435             return -1;
3436         };
3437
3438
3439         var correctFrame = function(tween) {
3440             var frames = tween.totalFrames;
3441             var frame = tween.currentFrame;
3442             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3443             var elapsed = (new Date() - tween.getStartTime());
3444             var tweak = 0;
3445
3446             if (elapsed < tween.duration * 1000) {
3447                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3448             } else {
3449                 tweak = frames - (frame + 1);
3450             }
3451             if (tweak > 0 && isFinite(tweak)) {
3452                 if (tween.currentFrame + tweak >= frames) {
3453                     tweak = frames - (frame + 1);
3454                 }
3455
3456                 tween.currentFrame += tweak;
3457             }
3458         };
3459     };/*
3460  * Portions of this file are based on pieces of Yahoo User Interface Library
3461  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3462  * YUI licensed under the BSD License:
3463  * http://developer.yahoo.net/yui/license.txt
3464  * <script type="text/javascript">
3465  *
3466  */
3467 Roo.lib.Bezier = new function() {
3468
3469         this.getPosition = function(points, t) {
3470             var n = points.length;
3471             var tmp = [];
3472
3473             for (var i = 0; i < n; ++i) {
3474                 tmp[i] = [points[i][0], points[i][1]];
3475             }
3476
3477             for (var j = 1; j < n; ++j) {
3478                 for (i = 0; i < n - j; ++i) {
3479                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3480                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3481                 }
3482             }
3483
3484             return [ tmp[0][0], tmp[0][1] ];
3485
3486         };
3487     };/*
3488  * Portions of this file are based on pieces of Yahoo User Interface Library
3489  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3490  * YUI licensed under the BSD License:
3491  * http://developer.yahoo.net/yui/license.txt
3492  * <script type="text/javascript">
3493  *
3494  */
3495 (function() {
3496
3497     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3498         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3499     };
3500
3501     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3502
3503     var fly = Roo.lib.AnimBase.fly;
3504     var Y = Roo.lib;
3505     var superclass = Y.ColorAnim.superclass;
3506     var proto = Y.ColorAnim.prototype;
3507
3508     proto.toString = function() {
3509         var el = this.getEl();
3510         var id = el.id || el.tagName;
3511         return ("ColorAnim " + id);
3512     };
3513
3514     proto.patterns.color = /color$/i;
3515     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3516     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3517     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3518     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3519
3520
3521     proto.parseColor = function(s) {
3522         if (s.length == 3) {
3523             return s;
3524         }
3525
3526         var c = this.patterns.hex.exec(s);
3527         if (c && c.length == 4) {
3528             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3529         }
3530
3531         c = this.patterns.rgb.exec(s);
3532         if (c && c.length == 4) {
3533             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3534         }
3535
3536         c = this.patterns.hex3.exec(s);
3537         if (c && c.length == 4) {
3538             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3539         }
3540
3541         return null;
3542     };
3543     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3544     proto.getAttribute = function(attr) {
3545         var el = this.getEl();
3546         if (this.patterns.color.test(attr)) {
3547             var val = fly(el).getStyle(attr);
3548
3549             if (this.patterns.transparent.test(val)) {
3550                 var parent = el.parentNode;
3551                 val = fly(parent).getStyle(attr);
3552
3553                 while (parent && this.patterns.transparent.test(val)) {
3554                     parent = parent.parentNode;
3555                     val = fly(parent).getStyle(attr);
3556                     if (parent.tagName.toUpperCase() == 'HTML') {
3557                         val = '#fff';
3558                     }
3559                 }
3560             }
3561         } else {
3562             val = superclass.getAttribute.call(this, attr);
3563         }
3564
3565         return val;
3566     };
3567     proto.getAttribute = function(attr) {
3568         var el = this.getEl();
3569         if (this.patterns.color.test(attr)) {
3570             var val = fly(el).getStyle(attr);
3571
3572             if (this.patterns.transparent.test(val)) {
3573                 var parent = el.parentNode;
3574                 val = fly(parent).getStyle(attr);
3575
3576                 while (parent && this.patterns.transparent.test(val)) {
3577                     parent = parent.parentNode;
3578                     val = fly(parent).getStyle(attr);
3579                     if (parent.tagName.toUpperCase() == 'HTML') {
3580                         val = '#fff';
3581                     }
3582                 }
3583             }
3584         } else {
3585             val = superclass.getAttribute.call(this, attr);
3586         }
3587
3588         return val;
3589     };
3590
3591     proto.doMethod = function(attr, start, end) {
3592         var val;
3593
3594         if (this.patterns.color.test(attr)) {
3595             val = [];
3596             for (var i = 0, len = start.length; i < len; ++i) {
3597                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3598             }
3599
3600             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3601         }
3602         else {
3603             val = superclass.doMethod.call(this, attr, start, end);
3604         }
3605
3606         return val;
3607     };
3608
3609     proto.setRuntimeAttribute = function(attr) {
3610         superclass.setRuntimeAttribute.call(this, attr);
3611
3612         if (this.patterns.color.test(attr)) {
3613             var attributes = this.attributes;
3614             var start = this.parseColor(this.runtimeAttributes[attr].start);
3615             var end = this.parseColor(this.runtimeAttributes[attr].end);
3616
3617             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3618                 end = this.parseColor(attributes[attr].by);
3619
3620                 for (var i = 0, len = start.length; i < len; ++i) {
3621                     end[i] = start[i] + end[i];
3622                 }
3623             }
3624
3625             this.runtimeAttributes[attr].start = start;
3626             this.runtimeAttributes[attr].end = end;
3627         }
3628     };
3629 })();
3630
3631 /*
3632  * Portions of this file are based on pieces of Yahoo User Interface Library
3633  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3634  * YUI licensed under the BSD License:
3635  * http://developer.yahoo.net/yui/license.txt
3636  * <script type="text/javascript">
3637  *
3638  */
3639 Roo.lib.Easing = {
3640
3641
3642     easeNone: function (t, b, c, d) {
3643         return c * t / d + b;
3644     },
3645
3646
3647     easeIn: function (t, b, c, d) {
3648         return c * (t /= d) * t + b;
3649     },
3650
3651
3652     easeOut: function (t, b, c, d) {
3653         return -c * (t /= d) * (t - 2) + b;
3654     },
3655
3656
3657     easeBoth: function (t, b, c, d) {
3658         if ((t /= d / 2) < 1) {
3659             return c / 2 * t * t + b;
3660         }
3661
3662         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3663     },
3664
3665
3666     easeInStrong: function (t, b, c, d) {
3667         return c * (t /= d) * t * t * t + b;
3668     },
3669
3670
3671     easeOutStrong: function (t, b, c, d) {
3672         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3673     },
3674
3675
3676     easeBothStrong: function (t, b, c, d) {
3677         if ((t /= d / 2) < 1) {
3678             return c / 2 * t * t * t * t + b;
3679         }
3680
3681         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3682     },
3683
3684
3685
3686     elasticIn: function (t, b, c, d, a, p) {
3687         if (t == 0) {
3688             return b;
3689         }
3690         if ((t /= d) == 1) {
3691             return b + c;
3692         }
3693         if (!p) {
3694             p = d * .3;
3695         }
3696
3697         if (!a || a < Math.abs(c)) {
3698             a = c;
3699             var s = p / 4;
3700         }
3701         else {
3702             var s = p / (2 * Math.PI) * Math.asin(c / a);
3703         }
3704
3705         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3706     },
3707
3708
3709     elasticOut: function (t, b, c, d, a, p) {
3710         if (t == 0) {
3711             return b;
3712         }
3713         if ((t /= d) == 1) {
3714             return b + c;
3715         }
3716         if (!p) {
3717             p = d * .3;
3718         }
3719
3720         if (!a || a < Math.abs(c)) {
3721             a = c;
3722             var s = p / 4;
3723         }
3724         else {
3725             var s = p / (2 * Math.PI) * Math.asin(c / a);
3726         }
3727
3728         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3729     },
3730
3731
3732     elasticBoth: function (t, b, c, d, a, p) {
3733         if (t == 0) {
3734             return b;
3735         }
3736
3737         if ((t /= d / 2) == 2) {
3738             return b + c;
3739         }
3740
3741         if (!p) {
3742             p = d * (.3 * 1.5);
3743         }
3744
3745         if (!a || a < Math.abs(c)) {
3746             a = c;
3747             var s = p / 4;
3748         }
3749         else {
3750             var s = p / (2 * Math.PI) * Math.asin(c / a);
3751         }
3752
3753         if (t < 1) {
3754             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3755                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3756         }
3757         return a * Math.pow(2, -10 * (t -= 1)) *
3758                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3759     },
3760
3761
3762
3763     backIn: function (t, b, c, d, s) {
3764         if (typeof s == 'undefined') {
3765             s = 1.70158;
3766         }
3767         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3768     },
3769
3770
3771     backOut: function (t, b, c, d, s) {
3772         if (typeof s == 'undefined') {
3773             s = 1.70158;
3774         }
3775         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3776     },
3777
3778
3779     backBoth: function (t, b, c, d, s) {
3780         if (typeof s == 'undefined') {
3781             s = 1.70158;
3782         }
3783
3784         if ((t /= d / 2 ) < 1) {
3785             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3786         }
3787         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3788     },
3789
3790
3791     bounceIn: function (t, b, c, d) {
3792         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3793     },
3794
3795
3796     bounceOut: function (t, b, c, d) {
3797         if ((t /= d) < (1 / 2.75)) {
3798             return c * (7.5625 * t * t) + b;
3799         } else if (t < (2 / 2.75)) {
3800             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3801         } else if (t < (2.5 / 2.75)) {
3802             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3803         }
3804         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3805     },
3806
3807
3808     bounceBoth: function (t, b, c, d) {
3809         if (t < d / 2) {
3810             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3811         }
3812         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3813     }
3814 };/*
3815  * Portions of this file are based on pieces of Yahoo User Interface Library
3816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3817  * YUI licensed under the BSD License:
3818  * http://developer.yahoo.net/yui/license.txt
3819  * <script type="text/javascript">
3820  *
3821  */
3822     (function() {
3823         Roo.lib.Motion = function(el, attributes, duration, method) {
3824             if (el) {
3825                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3826             }
3827         };
3828
3829         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3830
3831
3832         var Y = Roo.lib;
3833         var superclass = Y.Motion.superclass;
3834         var proto = Y.Motion.prototype;
3835
3836         proto.toString = function() {
3837             var el = this.getEl();
3838             var id = el.id || el.tagName;
3839             return ("Motion " + id);
3840         };
3841
3842         proto.patterns.points = /^points$/i;
3843
3844         proto.setAttribute = function(attr, val, unit) {
3845             if (this.patterns.points.test(attr)) {
3846                 unit = unit || 'px';
3847                 superclass.setAttribute.call(this, 'left', val[0], unit);
3848                 superclass.setAttribute.call(this, 'top', val[1], unit);
3849             } else {
3850                 superclass.setAttribute.call(this, attr, val, unit);
3851             }
3852         };
3853
3854         proto.getAttribute = function(attr) {
3855             if (this.patterns.points.test(attr)) {
3856                 var val = [
3857                         superclass.getAttribute.call(this, 'left'),
3858                         superclass.getAttribute.call(this, 'top')
3859                         ];
3860             } else {
3861                 val = superclass.getAttribute.call(this, attr);
3862             }
3863
3864             return val;
3865         };
3866
3867         proto.doMethod = function(attr, start, end) {
3868             var val = null;
3869
3870             if (this.patterns.points.test(attr)) {
3871                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3872                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3873             } else {
3874                 val = superclass.doMethod.call(this, attr, start, end);
3875             }
3876             return val;
3877         };
3878
3879         proto.setRuntimeAttribute = function(attr) {
3880             if (this.patterns.points.test(attr)) {
3881                 var el = this.getEl();
3882                 var attributes = this.attributes;
3883                 var start;
3884                 var control = attributes['points']['control'] || [];
3885                 var end;
3886                 var i, len;
3887
3888                 if (control.length > 0 && !(control[0] instanceof Array)) {
3889                     control = [control];
3890                 } else {
3891                     var tmp = [];
3892                     for (i = 0,len = control.length; i < len; ++i) {
3893                         tmp[i] = control[i];
3894                     }
3895                     control = tmp;
3896                 }
3897
3898                 Roo.fly(el).position();
3899
3900                 if (isset(attributes['points']['from'])) {
3901                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3902                 }
3903                 else {
3904                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3905                 }
3906
3907                 start = this.getAttribute('points');
3908
3909
3910                 if (isset(attributes['points']['to'])) {
3911                     end = translateValues.call(this, attributes['points']['to'], start);
3912
3913                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3914                     for (i = 0,len = control.length; i < len; ++i) {
3915                         control[i] = translateValues.call(this, control[i], start);
3916                     }
3917
3918
3919                 } else if (isset(attributes['points']['by'])) {
3920                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3921
3922                     for (i = 0,len = control.length; i < len; ++i) {
3923                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3924                     }
3925                 }
3926
3927                 this.runtimeAttributes[attr] = [start];
3928
3929                 if (control.length > 0) {
3930                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3931                 }
3932
3933                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3934             }
3935             else {
3936                 superclass.setRuntimeAttribute.call(this, attr);
3937             }
3938         };
3939
3940         var translateValues = function(val, start) {
3941             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3942             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3943
3944             return val;
3945         };
3946
3947         var isset = function(prop) {
3948             return (typeof prop !== 'undefined');
3949         };
3950     })();
3951 /*
3952  * Portions of this file are based on pieces of Yahoo User Interface Library
3953  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3954  * YUI licensed under the BSD License:
3955  * http://developer.yahoo.net/yui/license.txt
3956  * <script type="text/javascript">
3957  *
3958  */
3959     (function() {
3960         Roo.lib.Scroll = function(el, attributes, duration, method) {
3961             if (el) {
3962                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3963             }
3964         };
3965
3966         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3967
3968
3969         var Y = Roo.lib;
3970         var superclass = Y.Scroll.superclass;
3971         var proto = Y.Scroll.prototype;
3972
3973         proto.toString = function() {
3974             var el = this.getEl();
3975             var id = el.id || el.tagName;
3976             return ("Scroll " + id);
3977         };
3978
3979         proto.doMethod = function(attr, start, end) {
3980             var val = null;
3981
3982             if (attr == 'scroll') {
3983                 val = [
3984                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3985                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3986                         ];
3987
3988             } else {
3989                 val = superclass.doMethod.call(this, attr, start, end);
3990             }
3991             return val;
3992         };
3993
3994         proto.getAttribute = function(attr) {
3995             var val = null;
3996             var el = this.getEl();
3997
3998             if (attr == 'scroll') {
3999                 val = [ el.scrollLeft, el.scrollTop ];
4000             } else {
4001                 val = superclass.getAttribute.call(this, attr);
4002             }
4003
4004             return val;
4005         };
4006
4007         proto.setAttribute = function(attr, val, unit) {
4008             var el = this.getEl();
4009
4010             if (attr == 'scroll') {
4011                 el.scrollLeft = val[0];
4012                 el.scrollTop = val[1];
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017     })();
4018 /*
4019  * Based on:
4020  * Ext JS Library 1.1.1
4021  * Copyright(c) 2006-2007, Ext JS, LLC.
4022  *
4023  * Originally Released Under LGPL - original licence link has changed is not relivant.
4024  *
4025  * Fork - LGPL
4026  * <script type="text/javascript">
4027  */
4028
4029
4030 // nasty IE9 hack - what a pile of crap that is..
4031
4032  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4033     Range.prototype.createContextualFragment = function (html) {
4034         var doc = window.document;
4035         var container = doc.createElement("div");
4036         container.innerHTML = html;
4037         var frag = doc.createDocumentFragment(), n;
4038         while ((n = container.firstChild)) {
4039             frag.appendChild(n);
4040         }
4041         return frag;
4042     };
4043 }
4044
4045 /**
4046  * @class Roo.DomHelper
4047  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4048  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4049  * @singleton
4050  */
4051 Roo.DomHelper = function(){
4052     var tempTableEl = null;
4053     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4054     var tableRe = /^table|tbody|tr|td$/i;
4055     var xmlns = {};
4056     // build as innerHTML where available
4057     /** @ignore */
4058     var createHtml = function(o){
4059         if(typeof o == 'string'){
4060             return o;
4061         }
4062         var b = "";
4063         if(!o.tag){
4064             o.tag = "div";
4065         }
4066         b += "<" + o.tag;
4067         for(var attr in o){
4068             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4069             if(attr == "style"){
4070                 var s = o["style"];
4071                 if(typeof s == "function"){
4072                     s = s.call();
4073                 }
4074                 if(typeof s == "string"){
4075                     b += ' style="' + s + '"';
4076                 }else if(typeof s == "object"){
4077                     b += ' style="';
4078                     for(var key in s){
4079                         if(typeof s[key] != "function"){
4080                             b += key + ":" + s[key] + ";";
4081                         }
4082                     }
4083                     b += '"';
4084                 }
4085             }else{
4086                 if(attr == "cls"){
4087                     b += ' class="' + o["cls"] + '"';
4088                 }else if(attr == "htmlFor"){
4089                     b += ' for="' + o["htmlFor"] + '"';
4090                 }else{
4091                     b += " " + attr + '="' + o[attr] + '"';
4092                 }
4093             }
4094         }
4095         if(emptyTags.test(o.tag)){
4096             b += "/>";
4097         }else{
4098             b += ">";
4099             var cn = o.children || o.cn;
4100             if(cn){
4101                 //http://bugs.kde.org/show_bug.cgi?id=71506
4102                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4103                     for(var i = 0, len = cn.length; i < len; i++) {
4104                         b += createHtml(cn[i], b);
4105                     }
4106                 }else{
4107                     b += createHtml(cn, b);
4108                 }
4109             }
4110             if(o.html){
4111                 b += o.html;
4112             }
4113             b += "</" + o.tag + ">";
4114         }
4115         return b;
4116     };
4117
4118     // build as dom
4119     /** @ignore */
4120     var createDom = function(o, parentNode){
4121          
4122         // defininition craeted..
4123         var ns = false;
4124         if (o.ns && o.ns != 'html') {
4125                
4126             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4127                 xmlns[o.ns] = o.xmlns;
4128                 ns = o.xmlns;
4129             }
4130             if (typeof(xmlns[o.ns]) == 'undefined') {
4131                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4132             }
4133             ns = xmlns[o.ns];
4134         }
4135         
4136         
4137         if (typeof(o) == 'string') {
4138             return parentNode.appendChild(document.createTextNode(o));
4139         }
4140         o.tag = o.tag || div;
4141         if (o.ns && Roo.isIE) {
4142             ns = false;
4143             o.tag = o.ns + ':' + o.tag;
4144             
4145         }
4146         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4147         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4148         for(var attr in o){
4149             
4150             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4151                     attr == "style" || typeof o[attr] == "function") continue;
4152                     
4153             if(attr=="cls" && Roo.isIE){
4154                 el.className = o["cls"];
4155             }else{
4156                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4157                 else el[attr] = o[attr];
4158             }
4159         }
4160         Roo.DomHelper.applyStyles(el, o.style);
4161         var cn = o.children || o.cn;
4162         if(cn){
4163             //http://bugs.kde.org/show_bug.cgi?id=71506
4164              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4165                 for(var i = 0, len = cn.length; i < len; i++) {
4166                     createDom(cn[i], el);
4167                 }
4168             }else{
4169                 createDom(cn, el);
4170             }
4171         }
4172         if(o.html){
4173             el.innerHTML = o.html;
4174         }
4175         if(parentNode){
4176            parentNode.appendChild(el);
4177         }
4178         return el;
4179     };
4180
4181     var ieTable = function(depth, s, h, e){
4182         tempTableEl.innerHTML = [s, h, e].join('');
4183         var i = -1, el = tempTableEl;
4184         while(++i < depth){
4185             el = el.firstChild;
4186         }
4187         return el;
4188     };
4189
4190     // kill repeat to save bytes
4191     var ts = '<table>',
4192         te = '</table>',
4193         tbs = ts+'<tbody>',
4194         tbe = '</tbody>'+te,
4195         trs = tbs + '<tr>',
4196         tre = '</tr>'+tbe;
4197
4198     /**
4199      * @ignore
4200      * Nasty code for IE's broken table implementation
4201      */
4202     var insertIntoTable = function(tag, where, el, html){
4203         if(!tempTableEl){
4204             tempTableEl = document.createElement('div');
4205         }
4206         var node;
4207         var before = null;
4208         if(tag == 'td'){
4209             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4210                 return;
4211             }
4212             if(where == 'beforebegin'){
4213                 before = el;
4214                 el = el.parentNode;
4215             } else{
4216                 before = el.nextSibling;
4217                 el = el.parentNode;
4218             }
4219             node = ieTable(4, trs, html, tre);
4220         }
4221         else if(tag == 'tr'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(3, tbs, html, tbe);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(3, tbs, html, tbe);
4230             } else{ // INTO a TR
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(4, trs, html, tre);
4235             }
4236         } else if(tag == 'tbody'){
4237             if(where == 'beforebegin'){
4238                 before = el;
4239                 el = el.parentNode;
4240                 node = ieTable(2, ts, html, te);
4241             } else if(where == 'afterend'){
4242                 before = el.nextSibling;
4243                 el = el.parentNode;
4244                 node = ieTable(2, ts, html, te);
4245             } else{
4246                 if(where == 'afterbegin'){
4247                     before = el.firstChild;
4248                 }
4249                 node = ieTable(3, tbs, html, tbe);
4250             }
4251         } else{ // TABLE
4252             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4253                 return;
4254             }
4255             if(where == 'afterbegin'){
4256                 before = el.firstChild;
4257             }
4258             node = ieTable(2, ts, html, te);
4259         }
4260         el.insertBefore(node, before);
4261         return node;
4262     };
4263
4264     return {
4265     /** True to force the use of DOM instead of html fragments @type Boolean */
4266     useDom : false,
4267
4268     /**
4269      * Returns the markup for the passed Element(s) config
4270      * @param {Object} o The Dom object spec (and children)
4271      * @return {String}
4272      */
4273     markup : function(o){
4274         return createHtml(o);
4275     },
4276
4277     /**
4278      * Applies a style specification to an element
4279      * @param {String/HTMLElement} el The element to apply styles to
4280      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4281      * a function which returns such a specification.
4282      */
4283     applyStyles : function(el, styles){
4284         if(styles){
4285            el = Roo.fly(el);
4286            if(typeof styles == "string"){
4287                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4288                var matches;
4289                while ((matches = re.exec(styles)) != null){
4290                    el.setStyle(matches[1], matches[2]);
4291                }
4292            }else if (typeof styles == "object"){
4293                for (var style in styles){
4294                   el.setStyle(style, styles[style]);
4295                }
4296            }else if (typeof styles == "function"){
4297                 Roo.DomHelper.applyStyles(el, styles.call());
4298            }
4299         }
4300     },
4301
4302     /**
4303      * Inserts an HTML fragment into the Dom
4304      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4305      * @param {HTMLElement} el The context element
4306      * @param {String} html The HTML fragmenet
4307      * @return {HTMLElement} The new node
4308      */
4309     insertHtml : function(where, el, html){
4310         where = where.toLowerCase();
4311         if(el.insertAdjacentHTML){
4312             if(tableRe.test(el.tagName)){
4313                 var rs;
4314                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4315                     return rs;
4316                 }
4317             }
4318             switch(where){
4319                 case "beforebegin":
4320                     el.insertAdjacentHTML('BeforeBegin', html);
4321                     return el.previousSibling;
4322                 case "afterbegin":
4323                     el.insertAdjacentHTML('AfterBegin', html);
4324                     return el.firstChild;
4325                 case "beforeend":
4326                     el.insertAdjacentHTML('BeforeEnd', html);
4327                     return el.lastChild;
4328                 case "afterend":
4329                     el.insertAdjacentHTML('AfterEnd', html);
4330                     return el.nextSibling;
4331             }
4332             throw 'Illegal insertion point -> "' + where + '"';
4333         }
4334         var range = el.ownerDocument.createRange();
4335         var frag;
4336         switch(where){
4337              case "beforebegin":
4338                 range.setStartBefore(el);
4339                 frag = range.createContextualFragment(html);
4340                 el.parentNode.insertBefore(frag, el);
4341                 return el.previousSibling;
4342              case "afterbegin":
4343                 if(el.firstChild){
4344                     range.setStartBefore(el.firstChild);
4345                     frag = range.createContextualFragment(html);
4346                     el.insertBefore(frag, el.firstChild);
4347                     return el.firstChild;
4348                 }else{
4349                     el.innerHTML = html;
4350                     return el.firstChild;
4351                 }
4352             case "beforeend":
4353                 if(el.lastChild){
4354                     range.setStartAfter(el.lastChild);
4355                     frag = range.createContextualFragment(html);
4356                     el.appendChild(frag);
4357                     return el.lastChild;
4358                 }else{
4359                     el.innerHTML = html;
4360                     return el.lastChild;
4361                 }
4362             case "afterend":
4363                 range.setStartAfter(el);
4364                 frag = range.createContextualFragment(html);
4365                 el.parentNode.insertBefore(frag, el.nextSibling);
4366                 return el.nextSibling;
4367             }
4368             throw 'Illegal insertion point -> "' + where + '"';
4369     },
4370
4371     /**
4372      * Creates new Dom element(s) and inserts them before el
4373      * @param {String/HTMLElement/Element} el The context element
4374      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4375      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4376      * @return {HTMLElement/Roo.Element} The new node
4377      */
4378     insertBefore : function(el, o, returnElement){
4379         return this.doInsert(el, o, returnElement, "beforeBegin");
4380     },
4381
4382     /**
4383      * Creates new Dom element(s) and inserts them after el
4384      * @param {String/HTMLElement/Element} el The context element
4385      * @param {Object} o The Dom object spec (and children)
4386      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4387      * @return {HTMLElement/Roo.Element} The new node
4388      */
4389     insertAfter : function(el, o, returnElement){
4390         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4391     },
4392
4393     /**
4394      * Creates new Dom element(s) and inserts them as the first child of el
4395      * @param {String/HTMLElement/Element} el The context element
4396      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4397      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4398      * @return {HTMLElement/Roo.Element} The new node
4399      */
4400     insertFirst : function(el, o, returnElement){
4401         return this.doInsert(el, o, returnElement, "afterBegin");
4402     },
4403
4404     // private
4405     doInsert : function(el, o, returnElement, pos, sibling){
4406         el = Roo.getDom(el);
4407         var newNode;
4408         if(this.useDom || o.ns){
4409             newNode = createDom(o, null);
4410             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4411         }else{
4412             var html = createHtml(o);
4413             newNode = this.insertHtml(pos, el, html);
4414         }
4415         return returnElement ? Roo.get(newNode, true) : newNode;
4416     },
4417
4418     /**
4419      * Creates new Dom element(s) and appends them to el
4420      * @param {String/HTMLElement/Element} el The context element
4421      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4422      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4423      * @return {HTMLElement/Roo.Element} The new node
4424      */
4425     append : function(el, o, returnElement){
4426         el = Roo.getDom(el);
4427         var newNode;
4428         if(this.useDom || o.ns){
4429             newNode = createDom(o, null);
4430             el.appendChild(newNode);
4431         }else{
4432             var html = createHtml(o);
4433             newNode = this.insertHtml("beforeEnd", el, html);
4434         }
4435         return returnElement ? Roo.get(newNode, true) : newNode;
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and overwrites the contents of el with them
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     overwrite : function(el, o, returnElement){
4446         el = Roo.getDom(el);
4447         if (o.ns) {
4448           
4449             while (el.childNodes.length) {
4450                 el.removeChild(el.firstChild);
4451             }
4452             createDom(o, el);
4453         } else {
4454             el.innerHTML = createHtml(o);   
4455         }
4456         
4457         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4458     },
4459
4460     /**
4461      * Creates a new Roo.DomHelper.Template from the Dom object spec
4462      * @param {Object} o The Dom object spec (and children)
4463      * @return {Roo.DomHelper.Template} The new template
4464      */
4465     createTemplate : function(o){
4466         var html = createHtml(o);
4467         return new Roo.Template(html);
4468     }
4469     };
4470 }();
4471 /*
4472  * Based on:
4473  * Ext JS Library 1.1.1
4474  * Copyright(c) 2006-2007, Ext JS, LLC.
4475  *
4476  * Originally Released Under LGPL - original licence link has changed is not relivant.
4477  *
4478  * Fork - LGPL
4479  * <script type="text/javascript">
4480  */
4481  
4482 /**
4483 * @class Roo.Template
4484 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4485 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4486 * Usage:
4487 <pre><code>
4488 var t = new Roo.Template({
4489     html :  '&lt;div name="{id}"&gt;' + 
4490         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4491         '&lt;/div&gt;',
4492     myformat: function (value, allValues) {
4493         return 'XX' + value;
4494     }
4495 });
4496 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4497 </code></pre>
4498 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4499 * @constructor
4500 * @param {Object} cfg - Configuration object.
4501 */
4502 Roo.Template = function(cfg){
4503     // BC!
4504     if(cfg instanceof Array){
4505         cfg = cfg.join("");
4506     }else if(arguments.length > 1){
4507         cfg = Array.prototype.join.call(arguments, "");
4508     }
4509     
4510     
4511     if (typeof(cfg) == 'object') {
4512         Roo.apply(this,cfg)
4513     } else {
4514         // bc
4515         this.html = cfg;
4516     }
4517     
4518     
4519 };
4520 Roo.Template.prototype = {
4521     
4522     /**
4523      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4524      */
4525     html : '',
4526     /**
4527      * Returns an HTML fragment of this template with the specified values applied.
4528      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4529      * @return {String} The HTML fragment
4530      */
4531     applyTemplate : function(values){
4532         try {
4533             
4534             if(this.compiled){
4535                 return this.compiled(values);
4536             }
4537             var useF = this.disableFormats !== true;
4538             var fm = Roo.util.Format, tpl = this;
4539             var fn = function(m, name, format, args){
4540                 if(format && useF){
4541                     if(format.substr(0, 5) == "this."){
4542                         return tpl.call(format.substr(5), values[name], values);
4543                     }else{
4544                         if(args){
4545                             // quoted values are required for strings in compiled templates, 
4546                             // but for non compiled we need to strip them
4547                             // quoted reversed for jsmin
4548                             var re = /^\s*['"](.*)["']\s*$/;
4549                             args = args.split(',');
4550                             for(var i = 0, len = args.length; i < len; i++){
4551                                 args[i] = args[i].replace(re, "$1");
4552                             }
4553                             args = [values[name]].concat(args);
4554                         }else{
4555                             args = [values[name]];
4556                         }
4557                         return fm[format].apply(fm, args);
4558                     }
4559                 }else{
4560                     return values[name] !== undefined ? values[name] : "";
4561                 }
4562             };
4563             return this.html.replace(this.re, fn);
4564         } catch (e) {
4565             Roo.log(e);
4566             throw e;
4567         }
4568          
4569     },
4570     
4571     /**
4572      * Sets the HTML used as the template and optionally compiles it.
4573      * @param {String} html
4574      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4575      * @return {Roo.Template} this
4576      */
4577     set : function(html, compile){
4578         this.html = html;
4579         this.compiled = null;
4580         if(compile){
4581             this.compile();
4582         }
4583         return this;
4584     },
4585     
4586     /**
4587      * True to disable format functions (defaults to false)
4588      * @type Boolean
4589      */
4590     disableFormats : false,
4591     
4592     /**
4593     * The regular expression used to match template variables 
4594     * @type RegExp
4595     * @property 
4596     */
4597     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4598     
4599     /**
4600      * Compiles the template into an internal function, eliminating the RegEx overhead.
4601      * @return {Roo.Template} this
4602      */
4603     compile : function(){
4604         var fm = Roo.util.Format;
4605         var useF = this.disableFormats !== true;
4606         var sep = Roo.isGecko ? "+" : ",";
4607         var fn = function(m, name, format, args){
4608             if(format && useF){
4609                 args = args ? ',' + args : "";
4610                 if(format.substr(0, 5) != "this."){
4611                     format = "fm." + format + '(';
4612                 }else{
4613                     format = 'this.call("'+ format.substr(5) + '", ';
4614                     args = ", values";
4615                 }
4616             }else{
4617                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4618             }
4619             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4620         };
4621         var body;
4622         // branched to use + in gecko and [].join() in others
4623         if(Roo.isGecko){
4624             body = "this.compiled = function(values){ return '" +
4625                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4626                     "';};";
4627         }else{
4628             body = ["this.compiled = function(values){ return ['"];
4629             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4630             body.push("'].join('');};");
4631             body = body.join('');
4632         }
4633         /**
4634          * eval:var:values
4635          * eval:var:fm
4636          */
4637         eval(body);
4638         return this;
4639     },
4640     
4641     // private function used to call members
4642     call : function(fnName, value, allValues){
4643         return this[fnName](value, allValues);
4644     },
4645     
4646     /**
4647      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4648      * @param {String/HTMLElement/Roo.Element} el The context element
4649      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4650      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4651      * @return {HTMLElement/Roo.Element} The new node or Element
4652      */
4653     insertFirst: function(el, values, returnElement){
4654         return this.doInsert('afterBegin', el, values, returnElement);
4655     },
4656
4657     /**
4658      * Applies the supplied values to the template and inserts the new node(s) before el.
4659      * @param {String/HTMLElement/Roo.Element} el The context element
4660      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4661      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4662      * @return {HTMLElement/Roo.Element} The new node or Element
4663      */
4664     insertBefore: function(el, values, returnElement){
4665         return this.doInsert('beforeBegin', el, values, returnElement);
4666     },
4667
4668     /**
4669      * Applies the supplied values to the template and inserts the new node(s) after el.
4670      * @param {String/HTMLElement/Roo.Element} el The context element
4671      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4672      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4673      * @return {HTMLElement/Roo.Element} The new node or Element
4674      */
4675     insertAfter : function(el, values, returnElement){
4676         return this.doInsert('afterEnd', el, values, returnElement);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and appends the new node(s) to el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     append : function(el, values, returnElement){
4687         return this.doInsert('beforeEnd', el, values, returnElement);
4688     },
4689
4690     doInsert : function(where, el, values, returnEl){
4691         el = Roo.getDom(el);
4692         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4693         return returnEl ? Roo.get(newNode, true) : newNode;
4694     },
4695
4696     /**
4697      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4698      * @param {String/HTMLElement/Roo.Element} el The context element
4699      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4700      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4701      * @return {HTMLElement/Roo.Element} The new node or Element
4702      */
4703     overwrite : function(el, values, returnElement){
4704         el = Roo.getDom(el);
4705         el.innerHTML = this.applyTemplate(values);
4706         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4707     }
4708 };
4709 /**
4710  * Alias for {@link #applyTemplate}
4711  * @method
4712  */
4713 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4714
4715 // backwards compat
4716 Roo.DomHelper.Template = Roo.Template;
4717
4718 /**
4719  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4720  * @param {String/HTMLElement} el A DOM element or its id
4721  * @returns {Roo.Template} The created template
4722  * @static
4723  */
4724 Roo.Template.from = function(el){
4725     el = Roo.getDom(el);
4726     return new Roo.Template(el.value || el.innerHTML);
4727 };/*
4728  * Based on:
4729  * Ext JS Library 1.1.1
4730  * Copyright(c) 2006-2007, Ext JS, LLC.
4731  *
4732  * Originally Released Under LGPL - original licence link has changed is not relivant.
4733  *
4734  * Fork - LGPL
4735  * <script type="text/javascript">
4736  */
4737  
4738
4739 /*
4740  * This is code is also distributed under MIT license for use
4741  * with jQuery and prototype JavaScript libraries.
4742  */
4743 /**
4744  * @class Roo.DomQuery
4745 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4746 <p>
4747 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4748
4749 <p>
4750 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4751 </p>
4752 <h4>Element Selectors:</h4>
4753 <ul class="list">
4754     <li> <b>*</b> any element</li>
4755     <li> <b>E</b> an element with the tag E</li>
4756     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4757     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4758     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4759     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4760 </ul>
4761 <h4>Attribute Selectors:</h4>
4762 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4763 <ul class="list">
4764     <li> <b>E[foo]</b> has an attribute "foo"</li>
4765     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4766     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4767     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4768     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4769     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4770     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4771 </ul>
4772 <h4>Pseudo Classes:</h4>
4773 <ul class="list">
4774     <li> <b>E:first-child</b> E is the first child of its parent</li>
4775     <li> <b>E:last-child</b> E is the last child of its parent</li>
4776     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4777     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4778     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4779     <li> <b>E:only-child</b> E is the only child of its parent</li>
4780     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4781     <li> <b>E:first</b> the first E in the resultset</li>
4782     <li> <b>E:last</b> the last E in the resultset</li>
4783     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4784     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4785     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4786     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4787     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4788     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4789     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4790     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4791     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4792 </ul>
4793 <h4>CSS Value Selectors:</h4>
4794 <ul class="list">
4795     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4796     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4797     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4798     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4799     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4800     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4801 </ul>
4802  * @singleton
4803  */
4804 Roo.DomQuery = function(){
4805     var cache = {}, simpleCache = {}, valueCache = {};
4806     var nonSpace = /\S/;
4807     var trimRe = /^\s+|\s+$/g;
4808     var tplRe = /\{(\d+)\}/g;
4809     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4810     var tagTokenRe = /^(#)?([\w-\*]+)/;
4811     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4812
4813     function child(p, index){
4814         var i = 0;
4815         var n = p.firstChild;
4816         while(n){
4817             if(n.nodeType == 1){
4818                if(++i == index){
4819                    return n;
4820                }
4821             }
4822             n = n.nextSibling;
4823         }
4824         return null;
4825     };
4826
4827     function next(n){
4828         while((n = n.nextSibling) && n.nodeType != 1);
4829         return n;
4830     };
4831
4832     function prev(n){
4833         while((n = n.previousSibling) && n.nodeType != 1);
4834         return n;
4835     };
4836
4837     function children(d){
4838         var n = d.firstChild, ni = -1;
4839             while(n){
4840                 var nx = n.nextSibling;
4841                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4842                     d.removeChild(n);
4843                 }else{
4844                     n.nodeIndex = ++ni;
4845                 }
4846                 n = nx;
4847             }
4848             return this;
4849         };
4850
4851     function byClassName(c, a, v){
4852         if(!v){
4853             return c;
4854         }
4855         var r = [], ri = -1, cn;
4856         for(var i = 0, ci; ci = c[i]; i++){
4857             if((' '+ci.className+' ').indexOf(v) != -1){
4858                 r[++ri] = ci;
4859             }
4860         }
4861         return r;
4862     };
4863
4864     function attrValue(n, attr){
4865         if(!n.tagName && typeof n.length != "undefined"){
4866             n = n[0];
4867         }
4868         if(!n){
4869             return null;
4870         }
4871         if(attr == "for"){
4872             return n.htmlFor;
4873         }
4874         if(attr == "class" || attr == "className"){
4875             return n.className;
4876         }
4877         return n.getAttribute(attr) || n[attr];
4878
4879     };
4880
4881     function getNodes(ns, mode, tagName){
4882         var result = [], ri = -1, cs;
4883         if(!ns){
4884             return result;
4885         }
4886         tagName = tagName || "*";
4887         if(typeof ns.getElementsByTagName != "undefined"){
4888             ns = [ns];
4889         }
4890         if(!mode){
4891             for(var i = 0, ni; ni = ns[i]; i++){
4892                 cs = ni.getElementsByTagName(tagName);
4893                 for(var j = 0, ci; ci = cs[j]; j++){
4894                     result[++ri] = ci;
4895                 }
4896             }
4897         }else if(mode == "/" || mode == ">"){
4898             var utag = tagName.toUpperCase();
4899             for(var i = 0, ni, cn; ni = ns[i]; i++){
4900                 cn = ni.children || ni.childNodes;
4901                 for(var j = 0, cj; cj = cn[j]; j++){
4902                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4903                         result[++ri] = cj;
4904                     }
4905                 }
4906             }
4907         }else if(mode == "+"){
4908             var utag = tagName.toUpperCase();
4909             for(var i = 0, n; n = ns[i]; i++){
4910                 while((n = n.nextSibling) && n.nodeType != 1);
4911                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4912                     result[++ri] = n;
4913                 }
4914             }
4915         }else if(mode == "~"){
4916             for(var i = 0, n; n = ns[i]; i++){
4917                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4918                 if(n){
4919                     result[++ri] = n;
4920                 }
4921             }
4922         }
4923         return result;
4924     };
4925
4926     function concat(a, b){
4927         if(b.slice){
4928             return a.concat(b);
4929         }
4930         for(var i = 0, l = b.length; i < l; i++){
4931             a[a.length] = b[i];
4932         }
4933         return a;
4934     }
4935
4936     function byTag(cs, tagName){
4937         if(cs.tagName || cs == document){
4938             cs = [cs];
4939         }
4940         if(!tagName){
4941             return cs;
4942         }
4943         var r = [], ri = -1;
4944         tagName = tagName.toLowerCase();
4945         for(var i = 0, ci; ci = cs[i]; i++){
4946             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4947                 r[++ri] = ci;
4948             }
4949         }
4950         return r;
4951     };
4952
4953     function byId(cs, attr, id){
4954         if(cs.tagName || cs == document){
4955             cs = [cs];
4956         }
4957         if(!id){
4958             return cs;
4959         }
4960         var r = [], ri = -1;
4961         for(var i = 0,ci; ci = cs[i]; i++){
4962             if(ci && ci.id == id){
4963                 r[++ri] = ci;
4964                 return r;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function byAttribute(cs, attr, value, op, custom){
4971         var r = [], ri = -1, st = custom=="{";
4972         var f = Roo.DomQuery.operators[op];
4973         for(var i = 0, ci; ci = cs[i]; i++){
4974             var a;
4975             if(st){
4976                 a = Roo.DomQuery.getStyle(ci, attr);
4977             }
4978             else if(attr == "class" || attr == "className"){
4979                 a = ci.className;
4980             }else if(attr == "for"){
4981                 a = ci.htmlFor;
4982             }else if(attr == "href"){
4983                 a = ci.getAttribute("href", 2);
4984             }else{
4985                 a = ci.getAttribute(attr);
4986             }
4987             if((f && f(a, value)) || (!f && a)){
4988                 r[++ri] = ci;
4989             }
4990         }
4991         return r;
4992     };
4993
4994     function byPseudo(cs, name, value){
4995         return Roo.DomQuery.pseudos[name](cs, value);
4996     };
4997
4998     // This is for IE MSXML which does not support expandos.
4999     // IE runs the same speed using setAttribute, however FF slows way down
5000     // and Safari completely fails so they need to continue to use expandos.
5001     var isIE = window.ActiveXObject ? true : false;
5002
5003     // this eval is stop the compressor from
5004     // renaming the variable to something shorter
5005     
5006     /** eval:var:batch */
5007     var batch = 30803; 
5008
5009     var key = 30803;
5010
5011     function nodupIEXml(cs){
5012         var d = ++key;
5013         cs[0].setAttribute("_nodup", d);
5014         var r = [cs[0]];
5015         for(var i = 1, len = cs.length; i < len; i++){
5016             var c = cs[i];
5017             if(!c.getAttribute("_nodup") != d){
5018                 c.setAttribute("_nodup", d);
5019                 r[r.length] = c;
5020             }
5021         }
5022         for(var i = 0, len = cs.length; i < len; i++){
5023             cs[i].removeAttribute("_nodup");
5024         }
5025         return r;
5026     }
5027
5028     function nodup(cs){
5029         if(!cs){
5030             return [];
5031         }
5032         var len = cs.length, c, i, r = cs, cj, ri = -1;
5033         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5034             return cs;
5035         }
5036         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5037             return nodupIEXml(cs);
5038         }
5039         var d = ++key;
5040         cs[0]._nodup = d;
5041         for(i = 1; c = cs[i]; i++){
5042             if(c._nodup != d){
5043                 c._nodup = d;
5044             }else{
5045                 r = [];
5046                 for(var j = 0; j < i; j++){
5047                     r[++ri] = cs[j];
5048                 }
5049                 for(j = i+1; cj = cs[j]; j++){
5050                     if(cj._nodup != d){
5051                         cj._nodup = d;
5052                         r[++ri] = cj;
5053                     }
5054                 }
5055                 return r;
5056             }
5057         }
5058         return r;
5059     }
5060
5061     function quickDiffIEXml(c1, c2){
5062         var d = ++key;
5063         for(var i = 0, len = c1.length; i < len; i++){
5064             c1[i].setAttribute("_qdiff", d);
5065         }
5066         var r = [];
5067         for(var i = 0, len = c2.length; i < len; i++){
5068             if(c2[i].getAttribute("_qdiff") != d){
5069                 r[r.length] = c2[i];
5070             }
5071         }
5072         for(var i = 0, len = c1.length; i < len; i++){
5073            c1[i].removeAttribute("_qdiff");
5074         }
5075         return r;
5076     }
5077
5078     function quickDiff(c1, c2){
5079         var len1 = c1.length;
5080         if(!len1){
5081             return c2;
5082         }
5083         if(isIE && c1[0].selectSingleNode){
5084             return quickDiffIEXml(c1, c2);
5085         }
5086         var d = ++key;
5087         for(var i = 0; i < len1; i++){
5088             c1[i]._qdiff = d;
5089         }
5090         var r = [];
5091         for(var i = 0, len = c2.length; i < len; i++){
5092             if(c2[i]._qdiff != d){
5093                 r[r.length] = c2[i];
5094             }
5095         }
5096         return r;
5097     }
5098
5099     function quickId(ns, mode, root, id){
5100         if(ns == root){
5101            var d = root.ownerDocument || root;
5102            return d.getElementById(id);
5103         }
5104         ns = getNodes(ns, mode, "*");
5105         return byId(ns, null, id);
5106     }
5107
5108     return {
5109         getStyle : function(el, name){
5110             return Roo.fly(el).getStyle(name);
5111         },
5112         /**
5113          * Compiles a selector/xpath query into a reusable function. The returned function
5114          * takes one parameter "root" (optional), which is the context node from where the query should start.
5115          * @param {String} selector The selector/xpath query
5116          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5117          * @return {Function}
5118          */
5119         compile : function(path, type){
5120             type = type || "select";
5121             
5122             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5123             var q = path, mode, lq;
5124             var tk = Roo.DomQuery.matchers;
5125             var tklen = tk.length;
5126             var mm;
5127
5128             // accept leading mode switch
5129             var lmode = q.match(modeRe);
5130             if(lmode && lmode[1]){
5131                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5132                 q = q.replace(lmode[1], "");
5133             }
5134             // strip leading slashes
5135             while(path.substr(0, 1)=="/"){
5136                 path = path.substr(1);
5137             }
5138
5139             while(q && lq != q){
5140                 lq = q;
5141                 var tm = q.match(tagTokenRe);
5142                 if(type == "select"){
5143                     if(tm){
5144                         if(tm[1] == "#"){
5145                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5146                         }else{
5147                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5148                         }
5149                         q = q.replace(tm[0], "");
5150                     }else if(q.substr(0, 1) != '@'){
5151                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5152                     }
5153                 }else{
5154                     if(tm){
5155                         if(tm[1] == "#"){
5156                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5157                         }else{
5158                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5159                         }
5160                         q = q.replace(tm[0], "");
5161                     }
5162                 }
5163                 while(!(mm = q.match(modeRe))){
5164                     var matched = false;
5165                     for(var j = 0; j < tklen; j++){
5166                         var t = tk[j];
5167                         var m = q.match(t.re);
5168                         if(m){
5169                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5170                                                     return m[i];
5171                                                 });
5172                             q = q.replace(m[0], "");
5173                             matched = true;
5174                             break;
5175                         }
5176                     }
5177                     // prevent infinite loop on bad selector
5178                     if(!matched){
5179                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5180                     }
5181                 }
5182                 if(mm[1]){
5183                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5184                     q = q.replace(mm[1], "");
5185                 }
5186             }
5187             fn[fn.length] = "return nodup(n);\n}";
5188             
5189              /** 
5190               * list of variables that need from compression as they are used by eval.
5191              *  eval:var:batch 
5192              *  eval:var:nodup
5193              *  eval:var:byTag
5194              *  eval:var:ById
5195              *  eval:var:getNodes
5196              *  eval:var:quickId
5197              *  eval:var:mode
5198              *  eval:var:root
5199              *  eval:var:n
5200              *  eval:var:byClassName
5201              *  eval:var:byPseudo
5202              *  eval:var:byAttribute
5203              *  eval:var:attrValue
5204              * 
5205              **/ 
5206             eval(fn.join(""));
5207             return f;
5208         },
5209
5210         /**
5211          * Selects a group of elements.
5212          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5213          * @param {Node} root (optional) The start of the query (defaults to document).
5214          * @return {Array}
5215          */
5216         select : function(path, root, type){
5217             if(!root || root == document){
5218                 root = document;
5219             }
5220             if(typeof root == "string"){
5221                 root = document.getElementById(root);
5222             }
5223             var paths = path.split(",");
5224             var results = [];
5225             for(var i = 0, len = paths.length; i < len; i++){
5226                 var p = paths[i].replace(trimRe, "");
5227                 if(!cache[p]){
5228                     cache[p] = Roo.DomQuery.compile(p);
5229                     if(!cache[p]){
5230                         throw p + " is not a valid selector";
5231                     }
5232                 }
5233                 var result = cache[p](root);
5234                 if(result && result != document){
5235                     results = results.concat(result);
5236                 }
5237             }
5238             if(paths.length > 1){
5239                 return nodup(results);
5240             }
5241             return results;
5242         },
5243
5244         /**
5245          * Selects a single element.
5246          * @param {String} selector The selector/xpath query
5247          * @param {Node} root (optional) The start of the query (defaults to document).
5248          * @return {Element}
5249          */
5250         selectNode : function(path, root){
5251             return Roo.DomQuery.select(path, root)[0];
5252         },
5253
5254         /**
5255          * Selects the value of a node, optionally replacing null with the defaultValue.
5256          * @param {String} selector The selector/xpath query
5257          * @param {Node} root (optional) The start of the query (defaults to document).
5258          * @param {String} defaultValue
5259          */
5260         selectValue : function(path, root, defaultValue){
5261             path = path.replace(trimRe, "");
5262             if(!valueCache[path]){
5263                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5264             }
5265             var n = valueCache[path](root);
5266             n = n[0] ? n[0] : n;
5267             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5268             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5269         },
5270
5271         /**
5272          * Selects the value of a node, parsing integers and floats.
5273          * @param {String} selector The selector/xpath query
5274          * @param {Node} root (optional) The start of the query (defaults to document).
5275          * @param {Number} defaultValue
5276          * @return {Number}
5277          */
5278         selectNumber : function(path, root, defaultValue){
5279             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5280             return parseFloat(v);
5281         },
5282
5283         /**
5284          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5285          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5286          * @param {String} selector The simple selector to test
5287          * @return {Boolean}
5288          */
5289         is : function(el, ss){
5290             if(typeof el == "string"){
5291                 el = document.getElementById(el);
5292             }
5293             var isArray = (el instanceof Array);
5294             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5295             return isArray ? (result.length == el.length) : (result.length > 0);
5296         },
5297
5298         /**
5299          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5300          * @param {Array} el An array of elements to filter
5301          * @param {String} selector The simple selector to test
5302          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5303          * the selector instead of the ones that match
5304          * @return {Array}
5305          */
5306         filter : function(els, ss, nonMatches){
5307             ss = ss.replace(trimRe, "");
5308             if(!simpleCache[ss]){
5309                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5310             }
5311             var result = simpleCache[ss](els);
5312             return nonMatches ? quickDiff(result, els) : result;
5313         },
5314
5315         /**
5316          * Collection of matching regular expressions and code snippets.
5317          */
5318         matchers : [{
5319                 re: /^\.([\w-]+)/,
5320                 select: 'n = byClassName(n, null, " {1} ");'
5321             }, {
5322                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5323                 select: 'n = byPseudo(n, "{1}", "{2}");'
5324             },{
5325                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5326                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5327             }, {
5328                 re: /^#([\w-]+)/,
5329                 select: 'n = byId(n, null, "{1}");'
5330             },{
5331                 re: /^@([\w-]+)/,
5332                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5333             }
5334         ],
5335
5336         /**
5337          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5338          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5339          */
5340         operators : {
5341             "=" : function(a, v){
5342                 return a == v;
5343             },
5344             "!=" : function(a, v){
5345                 return a != v;
5346             },
5347             "^=" : function(a, v){
5348                 return a && a.substr(0, v.length) == v;
5349             },
5350             "$=" : function(a, v){
5351                 return a && a.substr(a.length-v.length) == v;
5352             },
5353             "*=" : function(a, v){
5354                 return a && a.indexOf(v) !== -1;
5355             },
5356             "%=" : function(a, v){
5357                 return (a % v) == 0;
5358             },
5359             "|=" : function(a, v){
5360                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5361             },
5362             "~=" : function(a, v){
5363                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5364             }
5365         },
5366
5367         /**
5368          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5369          * and the argument (if any) supplied in the selector.
5370          */
5371         pseudos : {
5372             "first-child" : function(c){
5373                 var r = [], ri = -1, n;
5374                 for(var i = 0, ci; ci = n = c[i]; i++){
5375                     while((n = n.previousSibling) && n.nodeType != 1);
5376                     if(!n){
5377                         r[++ri] = ci;
5378                     }
5379                 }
5380                 return r;
5381             },
5382
5383             "last-child" : function(c){
5384                 var r = [], ri = -1, n;
5385                 for(var i = 0, ci; ci = n = c[i]; i++){
5386                     while((n = n.nextSibling) && n.nodeType != 1);
5387                     if(!n){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "nth-child" : function(c, a) {
5395                 var r = [], ri = -1;
5396                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5397                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5398                 for(var i = 0, n; n = c[i]; i++){
5399                     var pn = n.parentNode;
5400                     if (batch != pn._batch) {
5401                         var j = 0;
5402                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5403                             if(cn.nodeType == 1){
5404                                cn.nodeIndex = ++j;
5405                             }
5406                         }
5407                         pn._batch = batch;
5408                     }
5409                     if (f == 1) {
5410                         if (l == 0 || n.nodeIndex == l){
5411                             r[++ri] = n;
5412                         }
5413                     } else if ((n.nodeIndex + l) % f == 0){
5414                         r[++ri] = n;
5415                     }
5416                 }
5417
5418                 return r;
5419             },
5420
5421             "only-child" : function(c){
5422                 var r = [], ri = -1;;
5423                 for(var i = 0, ci; ci = c[i]; i++){
5424                     if(!prev(ci) && !next(ci)){
5425                         r[++ri] = ci;
5426                     }
5427                 }
5428                 return r;
5429             },
5430
5431             "empty" : function(c){
5432                 var r = [], ri = -1;
5433                 for(var i = 0, ci; ci = c[i]; i++){
5434                     var cns = ci.childNodes, j = 0, cn, empty = true;
5435                     while(cn = cns[j]){
5436                         ++j;
5437                         if(cn.nodeType == 1 || cn.nodeType == 3){
5438                             empty = false;
5439                             break;
5440                         }
5441                     }
5442                     if(empty){
5443                         r[++ri] = ci;
5444                     }
5445                 }
5446                 return r;
5447             },
5448
5449             "contains" : function(c, v){
5450                 var r = [], ri = -1;
5451                 for(var i = 0, ci; ci = c[i]; i++){
5452                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5453                         r[++ri] = ci;
5454                     }
5455                 }
5456                 return r;
5457             },
5458
5459             "nodeValue" : function(c, v){
5460                 var r = [], ri = -1;
5461                 for(var i = 0, ci; ci = c[i]; i++){
5462                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             },
5468
5469             "checked" : function(c){
5470                 var r = [], ri = -1;
5471                 for(var i = 0, ci; ci = c[i]; i++){
5472                     if(ci.checked == true){
5473                         r[++ri] = ci;
5474                     }
5475                 }
5476                 return r;
5477             },
5478
5479             "not" : function(c, ss){
5480                 return Roo.DomQuery.filter(c, ss, true);
5481             },
5482
5483             "odd" : function(c){
5484                 return this["nth-child"](c, "odd");
5485             },
5486
5487             "even" : function(c){
5488                 return this["nth-child"](c, "even");
5489             },
5490
5491             "nth" : function(c, a){
5492                 return c[a-1] || [];
5493             },
5494
5495             "first" : function(c){
5496                 return c[0] || [];
5497             },
5498
5499             "last" : function(c){
5500                 return c[c.length-1] || [];
5501             },
5502
5503             "has" : function(c, ss){
5504                 var s = Roo.DomQuery.select;
5505                 var r = [], ri = -1;
5506                 for(var i = 0, ci; ci = c[i]; i++){
5507                     if(s(ss, ci).length > 0){
5508                         r[++ri] = ci;
5509                     }
5510                 }
5511                 return r;
5512             },
5513
5514             "next" : function(c, ss){
5515                 var is = Roo.DomQuery.is;
5516                 var r = [], ri = -1;
5517                 for(var i = 0, ci; ci = c[i]; i++){
5518                     var n = next(ci);
5519                     if(n && is(n, ss)){
5520                         r[++ri] = ci;
5521                     }
5522                 }
5523                 return r;
5524             },
5525
5526             "prev" : function(c, ss){
5527                 var is = Roo.DomQuery.is;
5528                 var r = [], ri = -1;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     var n = prev(ci);
5531                     if(n && is(n, ss)){
5532                         r[++ri] = ci;
5533                     }
5534                 }
5535                 return r;
5536             }
5537         }
5538     };
5539 }();
5540
5541 /**
5542  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5543  * @param {String} path The selector/xpath query
5544  * @param {Node} root (optional) The start of the query (defaults to document).
5545  * @return {Array}
5546  * @member Roo
5547  * @method query
5548  */
5549 Roo.query = Roo.DomQuery.select;
5550 /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560
5561 /**
5562  * @class Roo.util.Observable
5563  * Base class that provides a common interface for publishing events. Subclasses are expected to
5564  * to have a property "events" with all the events defined.<br>
5565  * For example:
5566  * <pre><code>
5567  Employee = function(name){
5568     this.name = name;
5569     this.addEvents({
5570         "fired" : true,
5571         "quit" : true
5572     });
5573  }
5574  Roo.extend(Employee, Roo.util.Observable);
5575 </code></pre>
5576  * @param {Object} config properties to use (incuding events / listeners)
5577  */
5578
5579 Roo.util.Observable = function(cfg){
5580     
5581     cfg = cfg|| {};
5582     this.addEvents(cfg.events || {});
5583     if (cfg.events) {
5584         delete cfg.events; // make sure
5585     }
5586      
5587     Roo.apply(this, cfg);
5588     
5589     if(this.listeners){
5590         this.on(this.listeners);
5591         delete this.listeners;
5592     }
5593 };
5594 Roo.util.Observable.prototype = {
5595     /** 
5596  * @cfg {Object} listeners  list of events and functions to call for this object, 
5597  * For example :
5598  * <pre><code>
5599     listeners :  { 
5600        'click' : function(e) {
5601            ..... 
5602         } ,
5603         .... 
5604     } 
5605   </code></pre>
5606  */
5607     
5608     
5609     /**
5610      * Fires the specified event with the passed parameters (minus the event name).
5611      * @param {String} eventName
5612      * @param {Object...} args Variable number of parameters are passed to handlers
5613      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5614      */
5615     fireEvent : function(){
5616         var ce = this.events[arguments[0].toLowerCase()];
5617         if(typeof ce == "object"){
5618             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5619         }else{
5620             return true;
5621         }
5622     },
5623
5624     // private
5625     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5626
5627     /**
5628      * Appends an event handler to this component
5629      * @param {String}   eventName The type of event to listen for
5630      * @param {Function} handler The method the event invokes
5631      * @param {Object}   scope (optional) The scope in which to execute the handler
5632      * function. The handler function's "this" context.
5633      * @param {Object}   options (optional) An object containing handler configuration
5634      * properties. This may contain any of the following properties:<ul>
5635      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5636      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5637      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5638      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5639      * by the specified number of milliseconds. If the event fires again within that time, the original
5640      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5641      * </ul><br>
5642      * <p>
5643      * <b>Combining Options</b><br>
5644      * Using the options argument, it is possible to combine different types of listeners:<br>
5645      * <br>
5646      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5647                 <pre><code>
5648                 el.on('click', this.onClick, this, {
5649                         single: true,
5650                 delay: 100,
5651                 forumId: 4
5652                 });
5653                 </code></pre>
5654      * <p>
5655      * <b>Attaching multiple handlers in 1 call</b><br>
5656      * The method also allows for a single argument to be passed which is a config object containing properties
5657      * which specify multiple handlers.
5658      * <pre><code>
5659                 el.on({
5660                         'click': {
5661                         fn: this.onClick,
5662                         scope: this,
5663                         delay: 100
5664                 }, 
5665                 'mouseover': {
5666                         fn: this.onMouseOver,
5667                         scope: this
5668                 },
5669                 'mouseout': {
5670                         fn: this.onMouseOut,
5671                         scope: this
5672                 }
5673                 });
5674                 </code></pre>
5675      * <p>
5676      * Or a shorthand syntax which passes the same scope object to all handlers:
5677         <pre><code>
5678                 el.on({
5679                         'click': this.onClick,
5680                 'mouseover': this.onMouseOver,
5681                 'mouseout': this.onMouseOut,
5682                 scope: this
5683                 });
5684                 </code></pre>
5685      */
5686     addListener : function(eventName, fn, scope, o){
5687         if(typeof eventName == "object"){
5688             o = eventName;
5689             for(var e in o){
5690                 if(this.filterOptRe.test(e)){
5691                     continue;
5692                 }
5693                 if(typeof o[e] == "function"){
5694                     // shared options
5695                     this.addListener(e, o[e], o.scope,  o);
5696                 }else{
5697                     // individual options
5698                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5699                 }
5700             }
5701             return;
5702         }
5703         o = (!o || typeof o == "boolean") ? {} : o;
5704         eventName = eventName.toLowerCase();
5705         var ce = this.events[eventName] || true;
5706         if(typeof ce == "boolean"){
5707             ce = new Roo.util.Event(this, eventName);
5708             this.events[eventName] = ce;
5709         }
5710         ce.addListener(fn, scope, o);
5711     },
5712
5713     /**
5714      * Removes a listener
5715      * @param {String}   eventName     The type of event to listen for
5716      * @param {Function} handler        The handler to remove
5717      * @param {Object}   scope  (optional) The scope (this object) for the handler
5718      */
5719     removeListener : function(eventName, fn, scope){
5720         var ce = this.events[eventName.toLowerCase()];
5721         if(typeof ce == "object"){
5722             ce.removeListener(fn, scope);
5723         }
5724     },
5725
5726     /**
5727      * Removes all listeners for this object
5728      */
5729     purgeListeners : function(){
5730         for(var evt in this.events){
5731             if(typeof this.events[evt] == "object"){
5732                  this.events[evt].clearListeners();
5733             }
5734         }
5735     },
5736
5737     relayEvents : function(o, events){
5738         var createHandler = function(ename){
5739             return function(){
5740                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5741             };
5742         };
5743         for(var i = 0, len = events.length; i < len; i++){
5744             var ename = events[i];
5745             if(!this.events[ename]){ this.events[ename] = true; };
5746             o.on(ename, createHandler(ename), this);
5747         }
5748     },
5749
5750     /**
5751      * Used to define events on this Observable
5752      * @param {Object} object The object with the events defined
5753      */
5754     addEvents : function(o){
5755         if(!this.events){
5756             this.events = {};
5757         }
5758         Roo.applyIf(this.events, o);
5759     },
5760
5761     /**
5762      * Checks to see if this object has any listeners for a specified event
5763      * @param {String} eventName The name of the event to check for
5764      * @return {Boolean} True if the event is being listened for, else false
5765      */
5766     hasListener : function(eventName){
5767         var e = this.events[eventName];
5768         return typeof e == "object" && e.listeners.length > 0;
5769     }
5770 };
5771 /**
5772  * Appends an event handler to this element (shorthand for addListener)
5773  * @param {String}   eventName     The type of event to listen for
5774  * @param {Function} handler        The method the event invokes
5775  * @param {Object}   scope (optional) The scope in which to execute the handler
5776  * function. The handler function's "this" context.
5777  * @param {Object}   options  (optional)
5778  * @method
5779  */
5780 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5781 /**
5782  * Removes a listener (shorthand for removeListener)
5783  * @param {String}   eventName     The type of event to listen for
5784  * @param {Function} handler        The handler to remove
5785  * @param {Object}   scope  (optional) The scope (this object) for the handler
5786  * @method
5787  */
5788 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5789
5790 /**
5791  * Starts capture on the specified Observable. All events will be passed
5792  * to the supplied function with the event name + standard signature of the event
5793  * <b>before</b> the event is fired. If the supplied function returns false,
5794  * the event will not fire.
5795  * @param {Observable} o The Observable to capture
5796  * @param {Function} fn The function to call
5797  * @param {Object} scope (optional) The scope (this object) for the fn
5798  * @static
5799  */
5800 Roo.util.Observable.capture = function(o, fn, scope){
5801     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5802 };
5803
5804 /**
5805  * Removes <b>all</b> added captures from the Observable.
5806  * @param {Observable} o The Observable to release
5807  * @static
5808  */
5809 Roo.util.Observable.releaseCapture = function(o){
5810     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5811 };
5812
5813 (function(){
5814
5815     var createBuffered = function(h, o, scope){
5816         var task = new Roo.util.DelayedTask();
5817         return function(){
5818             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5819         };
5820     };
5821
5822     var createSingle = function(h, e, fn, scope){
5823         return function(){
5824             e.removeListener(fn, scope);
5825             return h.apply(scope, arguments);
5826         };
5827     };
5828
5829     var createDelayed = function(h, o, scope){
5830         return function(){
5831             var args = Array.prototype.slice.call(arguments, 0);
5832             setTimeout(function(){
5833                 h.apply(scope, args);
5834             }, o.delay || 10);
5835         };
5836     };
5837
5838     Roo.util.Event = function(obj, name){
5839         this.name = name;
5840         this.obj = obj;
5841         this.listeners = [];
5842     };
5843
5844     Roo.util.Event.prototype = {
5845         addListener : function(fn, scope, options){
5846             var o = options || {};
5847             scope = scope || this.obj;
5848             if(!this.isListening(fn, scope)){
5849                 var l = {fn: fn, scope: scope, options: o};
5850                 var h = fn;
5851                 if(o.delay){
5852                     h = createDelayed(h, o, scope);
5853                 }
5854                 if(o.single){
5855                     h = createSingle(h, this, fn, scope);
5856                 }
5857                 if(o.buffer){
5858                     h = createBuffered(h, o, scope);
5859                 }
5860                 l.fireFn = h;
5861                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5862                     this.listeners.push(l);
5863                 }else{
5864                     this.listeners = this.listeners.slice(0);
5865                     this.listeners.push(l);
5866                 }
5867             }
5868         },
5869
5870         findListener : function(fn, scope){
5871             scope = scope || this.obj;
5872             var ls = this.listeners;
5873             for(var i = 0, len = ls.length; i < len; i++){
5874                 var l = ls[i];
5875                 if(l.fn == fn && l.scope == scope){
5876                     return i;
5877                 }
5878             }
5879             return -1;
5880         },
5881
5882         isListening : function(fn, scope){
5883             return this.findListener(fn, scope) != -1;
5884         },
5885
5886         removeListener : function(fn, scope){
5887             var index;
5888             if((index = this.findListener(fn, scope)) != -1){
5889                 if(!this.firing){
5890                     this.listeners.splice(index, 1);
5891                 }else{
5892                     this.listeners = this.listeners.slice(0);
5893                     this.listeners.splice(index, 1);
5894                 }
5895                 return true;
5896             }
5897             return false;
5898         },
5899
5900         clearListeners : function(){
5901             this.listeners = [];
5902         },
5903
5904         fire : function(){
5905             var ls = this.listeners, scope, len = ls.length;
5906             if(len > 0){
5907                 this.firing = true;
5908                 var args = Array.prototype.slice.call(arguments, 0);
5909                 for(var i = 0; i < len; i++){
5910                     var l = ls[i];
5911                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5912                         this.firing = false;
5913                         return false;
5914                     }
5915                 }
5916                 this.firing = false;
5917             }
5918             return true;
5919         }
5920     };
5921 })();/*
5922  * Based on:
5923  * Ext JS Library 1.1.1
5924  * Copyright(c) 2006-2007, Ext JS, LLC.
5925  *
5926  * Originally Released Under LGPL - original licence link has changed is not relivant.
5927  *
5928  * Fork - LGPL
5929  * <script type="text/javascript">
5930  */
5931
5932 /**
5933  * @class Roo.EventManager
5934  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5935  * several useful events directly.
5936  * See {@link Roo.EventObject} for more details on normalized event objects.
5937  * @singleton
5938  */
5939 Roo.EventManager = function(){
5940     var docReadyEvent, docReadyProcId, docReadyState = false;
5941     var resizeEvent, resizeTask, textEvent, textSize;
5942     var E = Roo.lib.Event;
5943     var D = Roo.lib.Dom;
5944
5945
5946     var fireDocReady = function(){
5947         if(!docReadyState){
5948             docReadyState = true;
5949             Roo.isReady = true;
5950             if(docReadyProcId){
5951                 clearInterval(docReadyProcId);
5952             }
5953             if(Roo.isGecko || Roo.isOpera) {
5954                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5955             }
5956             if(Roo.isIE){
5957                 var defer = document.getElementById("ie-deferred-loader");
5958                 if(defer){
5959                     defer.onreadystatechange = null;
5960                     defer.parentNode.removeChild(defer);
5961                 }
5962             }
5963             if(docReadyEvent){
5964                 docReadyEvent.fire();
5965                 docReadyEvent.clearListeners();
5966             }
5967         }
5968     };
5969     
5970     var initDocReady = function(){
5971         docReadyEvent = new Roo.util.Event();
5972         if(Roo.isGecko || Roo.isOpera) {
5973             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5974         }else if(Roo.isIE){
5975             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5976             var defer = document.getElementById("ie-deferred-loader");
5977             defer.onreadystatechange = function(){
5978                 if(this.readyState == "complete"){
5979                     fireDocReady();
5980                 }
5981             };
5982         }else if(Roo.isSafari){ 
5983             docReadyProcId = setInterval(function(){
5984                 var rs = document.readyState;
5985                 if(rs == "complete") {
5986                     fireDocReady();     
5987                  }
5988             }, 10);
5989         }
5990         // no matter what, make sure it fires on load
5991         E.on(window, "load", fireDocReady);
5992     };
5993
5994     var createBuffered = function(h, o){
5995         var task = new Roo.util.DelayedTask(h);
5996         return function(e){
5997             // create new event object impl so new events don't wipe out properties
5998             e = new Roo.EventObjectImpl(e);
5999             task.delay(o.buffer, h, null, [e]);
6000         };
6001     };
6002
6003     var createSingle = function(h, el, ename, fn){
6004         return function(e){
6005             Roo.EventManager.removeListener(el, ename, fn);
6006             h(e);
6007         };
6008     };
6009
6010     var createDelayed = function(h, o){
6011         return function(e){
6012             // create new event object impl so new events don't wipe out properties
6013             e = new Roo.EventObjectImpl(e);
6014             setTimeout(function(){
6015                 h(e);
6016             }, o.delay || 10);
6017         };
6018     };
6019
6020     var listen = function(element, ename, opt, fn, scope){
6021         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6022         fn = fn || o.fn; scope = scope || o.scope;
6023         var el = Roo.getDom(element);
6024         if(!el){
6025             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6026         }
6027         var h = function(e){
6028             e = Roo.EventObject.setEvent(e);
6029             var t;
6030             if(o.delegate){
6031                 t = e.getTarget(o.delegate, el);
6032                 if(!t){
6033                     return;
6034                 }
6035             }else{
6036                 t = e.target;
6037             }
6038             if(o.stopEvent === true){
6039                 e.stopEvent();
6040             }
6041             if(o.preventDefault === true){
6042                e.preventDefault();
6043             }
6044             if(o.stopPropagation === true){
6045                 e.stopPropagation();
6046             }
6047
6048             if(o.normalized === false){
6049                 e = e.browserEvent;
6050             }
6051
6052             fn.call(scope || el, e, t, o);
6053         };
6054         if(o.delay){
6055             h = createDelayed(h, o);
6056         }
6057         if(o.single){
6058             h = createSingle(h, el, ename, fn);
6059         }
6060         if(o.buffer){
6061             h = createBuffered(h, o);
6062         }
6063         fn._handlers = fn._handlers || [];
6064         fn._handlers.push([Roo.id(el), ename, h]);
6065
6066         E.on(el, ename, h);
6067         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6068             el.addEventListener("DOMMouseScroll", h, false);
6069             E.on(window, 'unload', function(){
6070                 el.removeEventListener("DOMMouseScroll", h, false);
6071             });
6072         }
6073         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6074             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6075         }
6076         return h;
6077     };
6078
6079     var stopListening = function(el, ename, fn){
6080         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6081         if(hds){
6082             for(var i = 0, len = hds.length; i < len; i++){
6083                 var h = hds[i];
6084                 if(h[0] == id && h[1] == ename){
6085                     hd = h[2];
6086                     hds.splice(i, 1);
6087                     break;
6088                 }
6089             }
6090         }
6091         E.un(el, ename, hd);
6092         el = Roo.getDom(el);
6093         if(ename == "mousewheel" && el.addEventListener){
6094             el.removeEventListener("DOMMouseScroll", hd, false);
6095         }
6096         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6097             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6098         }
6099     };
6100
6101     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6102     
6103     var pub = {
6104         
6105         
6106         /** 
6107          * Fix for doc tools
6108          * @scope Roo.EventManager
6109          */
6110         
6111         
6112         /** 
6113          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6114          * object with a Roo.EventObject
6115          * @param {Function} fn        The method the event invokes
6116          * @param {Object}   scope    An object that becomes the scope of the handler
6117          * @param {boolean}  override If true, the obj passed in becomes
6118          *                             the execution scope of the listener
6119          * @return {Function} The wrapped function
6120          * @deprecated
6121          */
6122         wrap : function(fn, scope, override){
6123             return function(e){
6124                 Roo.EventObject.setEvent(e);
6125                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6126             };
6127         },
6128         
6129         /**
6130      * Appends an event handler to an element (shorthand for addListener)
6131      * @param {String/HTMLElement}   element        The html element or id to assign the
6132      * @param {String}   eventName The type of event to listen for
6133      * @param {Function} handler The method the event invokes
6134      * @param {Object}   scope (optional) The scope in which to execute the handler
6135      * function. The handler function's "this" context.
6136      * @param {Object}   options (optional) An object containing handler configuration
6137      * properties. This may contain any of the following properties:<ul>
6138      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6139      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6140      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6141      * <li>preventDefault {Boolean} True to prevent the default action</li>
6142      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6143      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6144      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6145      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6146      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6147      * by the specified number of milliseconds. If the event fires again within that time, the original
6148      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6149      * </ul><br>
6150      * <p>
6151      * <b>Combining Options</b><br>
6152      * Using the options argument, it is possible to combine different types of listeners:<br>
6153      * <br>
6154      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6155      * Code:<pre><code>
6156 el.on('click', this.onClick, this, {
6157     single: true,
6158     delay: 100,
6159     stopEvent : true,
6160     forumId: 4
6161 });</code></pre>
6162      * <p>
6163      * <b>Attaching multiple handlers in 1 call</b><br>
6164       * The method also allows for a single argument to be passed which is a config object containing properties
6165      * which specify multiple handlers.
6166      * <p>
6167      * Code:<pre><code>
6168 el.on({
6169     'click' : {
6170         fn: this.onClick
6171         scope: this,
6172         delay: 100
6173     },
6174     'mouseover' : {
6175         fn: this.onMouseOver
6176         scope: this
6177     },
6178     'mouseout' : {
6179         fn: this.onMouseOut
6180         scope: this
6181     }
6182 });</code></pre>
6183      * <p>
6184      * Or a shorthand syntax:<br>
6185      * Code:<pre><code>
6186 el.on({
6187     'click' : this.onClick,
6188     'mouseover' : this.onMouseOver,
6189     'mouseout' : this.onMouseOut
6190     scope: this
6191 });</code></pre>
6192      */
6193         addListener : function(element, eventName, fn, scope, options){
6194             if(typeof eventName == "object"){
6195                 var o = eventName;
6196                 for(var e in o){
6197                     if(propRe.test(e)){
6198                         continue;
6199                     }
6200                     if(typeof o[e] == "function"){
6201                         // shared options
6202                         listen(element, e, o, o[e], o.scope);
6203                     }else{
6204                         // individual options
6205                         listen(element, e, o[e]);
6206                     }
6207                 }
6208                 return;
6209             }
6210             return listen(element, eventName, options, fn, scope);
6211         },
6212         
6213         /**
6214          * Removes an event handler
6215          *
6216          * @param {String/HTMLElement}   element        The id or html element to remove the 
6217          *                             event from
6218          * @param {String}   eventName     The type of event
6219          * @param {Function} fn
6220          * @return {Boolean} True if a listener was actually removed
6221          */
6222         removeListener : function(element, eventName, fn){
6223             return stopListening(element, eventName, fn);
6224         },
6225         
6226         /**
6227          * Fires when the document is ready (before onload and before images are loaded). Can be 
6228          * accessed shorthanded Roo.onReady().
6229          * @param {Function} fn        The method the event invokes
6230          * @param {Object}   scope    An  object that becomes the scope of the handler
6231          * @param {boolean}  options
6232          */
6233         onDocumentReady : function(fn, scope, options){
6234             if(docReadyState){ // if it already fired
6235                 docReadyEvent.addListener(fn, scope, options);
6236                 docReadyEvent.fire();
6237                 docReadyEvent.clearListeners();
6238                 return;
6239             }
6240             if(!docReadyEvent){
6241                 initDocReady();
6242             }
6243             docReadyEvent.addListener(fn, scope, options);
6244         },
6245         
6246         /**
6247          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6248          * @param {Function} fn        The method the event invokes
6249          * @param {Object}   scope    An object that becomes the scope of the handler
6250          * @param {boolean}  options
6251          */
6252         onWindowResize : function(fn, scope, options){
6253             if(!resizeEvent){
6254                 resizeEvent = new Roo.util.Event();
6255                 resizeTask = new Roo.util.DelayedTask(function(){
6256                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6257                 });
6258                 E.on(window, "resize", function(){
6259                     if(Roo.isIE){
6260                         resizeTask.delay(50);
6261                     }else{
6262                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6263                     }
6264                 });
6265             }
6266             resizeEvent.addListener(fn, scope, options);
6267         },
6268
6269         /**
6270          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6271          * @param {Function} fn        The method the event invokes
6272          * @param {Object}   scope    An object that becomes the scope of the handler
6273          * @param {boolean}  options
6274          */
6275         onTextResize : function(fn, scope, options){
6276             if(!textEvent){
6277                 textEvent = new Roo.util.Event();
6278                 var textEl = new Roo.Element(document.createElement('div'));
6279                 textEl.dom.className = 'x-text-resize';
6280                 textEl.dom.innerHTML = 'X';
6281                 textEl.appendTo(document.body);
6282                 textSize = textEl.dom.offsetHeight;
6283                 setInterval(function(){
6284                     if(textEl.dom.offsetHeight != textSize){
6285                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6286                     }
6287                 }, this.textResizeInterval);
6288             }
6289             textEvent.addListener(fn, scope, options);
6290         },
6291
6292         /**
6293          * Removes the passed window resize listener.
6294          * @param {Function} fn        The method the event invokes
6295          * @param {Object}   scope    The scope of handler
6296          */
6297         removeResizeListener : function(fn, scope){
6298             if(resizeEvent){
6299                 resizeEvent.removeListener(fn, scope);
6300             }
6301         },
6302
6303         // private
6304         fireResize : function(){
6305             if(resizeEvent){
6306                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6307             }   
6308         },
6309         /**
6310          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6311          */
6312         ieDeferSrc : false,
6313         /**
6314          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6315          */
6316         textResizeInterval : 50
6317     };
6318     
6319     /**
6320      * Fix for doc tools
6321      * @scopeAlias pub=Roo.EventManager
6322      */
6323     
6324      /**
6325      * Appends an event handler to an element (shorthand for addListener)
6326      * @param {String/HTMLElement}   element        The html element or id to assign the
6327      * @param {String}   eventName The type of event to listen for
6328      * @param {Function} handler The method the event invokes
6329      * @param {Object}   scope (optional) The scope in which to execute the handler
6330      * function. The handler function's "this" context.
6331      * @param {Object}   options (optional) An object containing handler configuration
6332      * properties. This may contain any of the following properties:<ul>
6333      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6334      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6335      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6336      * <li>preventDefault {Boolean} True to prevent the default action</li>
6337      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6338      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6339      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6340      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6341      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6342      * by the specified number of milliseconds. If the event fires again within that time, the original
6343      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6344      * </ul><br>
6345      * <p>
6346      * <b>Combining Options</b><br>
6347      * Using the options argument, it is possible to combine different types of listeners:<br>
6348      * <br>
6349      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6350      * Code:<pre><code>
6351 el.on('click', this.onClick, this, {
6352     single: true,
6353     delay: 100,
6354     stopEvent : true,
6355     forumId: 4
6356 });</code></pre>
6357      * <p>
6358      * <b>Attaching multiple handlers in 1 call</b><br>
6359       * The method also allows for a single argument to be passed which is a config object containing properties
6360      * which specify multiple handlers.
6361      * <p>
6362      * Code:<pre><code>
6363 el.on({
6364     'click' : {
6365         fn: this.onClick
6366         scope: this,
6367         delay: 100
6368     },
6369     'mouseover' : {
6370         fn: this.onMouseOver
6371         scope: this
6372     },
6373     'mouseout' : {
6374         fn: this.onMouseOut
6375         scope: this
6376     }
6377 });</code></pre>
6378      * <p>
6379      * Or a shorthand syntax:<br>
6380      * Code:<pre><code>
6381 el.on({
6382     'click' : this.onClick,
6383     'mouseover' : this.onMouseOver,
6384     'mouseout' : this.onMouseOut
6385     scope: this
6386 });</code></pre>
6387      */
6388     pub.on = pub.addListener;
6389     pub.un = pub.removeListener;
6390
6391     pub.stoppedMouseDownEvent = new Roo.util.Event();
6392     return pub;
6393 }();
6394 /**
6395   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6396   * @param {Function} fn        The method the event invokes
6397   * @param {Object}   scope    An  object that becomes the scope of the handler
6398   * @param {boolean}  override If true, the obj passed in becomes
6399   *                             the execution scope of the listener
6400   * @member Roo
6401   * @method onReady
6402  */
6403 Roo.onReady = Roo.EventManager.onDocumentReady;
6404
6405 Roo.onReady(function(){
6406     var bd = Roo.get(document.body);
6407     if(!bd){ return; }
6408
6409     var cls = [
6410             Roo.isIE ? "roo-ie"
6411             : Roo.isGecko ? "roo-gecko"
6412             : Roo.isOpera ? "roo-opera"
6413             : Roo.isSafari ? "roo-safari" : ""];
6414
6415     if(Roo.isMac){
6416         cls.push("roo-mac");
6417     }
6418     if(Roo.isLinux){
6419         cls.push("roo-linux");
6420     }
6421     if(Roo.isBorderBox){
6422         cls.push('roo-border-box');
6423     }
6424     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6425         var p = bd.dom.parentNode;
6426         if(p){
6427             p.className += ' roo-strict';
6428         }
6429     }
6430     bd.addClass(cls.join(' '));
6431 });
6432
6433 /**
6434  * @class Roo.EventObject
6435  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6436  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6437  * Example:
6438  * <pre><code>
6439  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6440     e.preventDefault();
6441     var target = e.getTarget();
6442     ...
6443  }
6444  var myDiv = Roo.get("myDiv");
6445  myDiv.on("click", handleClick);
6446  //or
6447  Roo.EventManager.on("myDiv", 'click', handleClick);
6448  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6449  </code></pre>
6450  * @singleton
6451  */
6452 Roo.EventObject = function(){
6453     
6454     var E = Roo.lib.Event;
6455     
6456     // safari keypress events for special keys return bad keycodes
6457     var safariKeys = {
6458         63234 : 37, // left
6459         63235 : 39, // right
6460         63232 : 38, // up
6461         63233 : 40, // down
6462         63276 : 33, // page up
6463         63277 : 34, // page down
6464         63272 : 46, // delete
6465         63273 : 36, // home
6466         63275 : 35  // end
6467     };
6468
6469     // normalize button clicks
6470     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6471                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6472
6473     Roo.EventObjectImpl = function(e){
6474         if(e){
6475             this.setEvent(e.browserEvent || e);
6476         }
6477     };
6478     Roo.EventObjectImpl.prototype = {
6479         /**
6480          * Used to fix doc tools.
6481          * @scope Roo.EventObject.prototype
6482          */
6483             
6484
6485         
6486         
6487         /** The normal browser event */
6488         browserEvent : null,
6489         /** The button pressed in a mouse event */
6490         button : -1,
6491         /** True if the shift key was down during the event */
6492         shiftKey : false,
6493         /** True if the control key was down during the event */
6494         ctrlKey : false,
6495         /** True if the alt key was down during the event */
6496         altKey : false,
6497
6498         /** Key constant 
6499         * @type Number */
6500         BACKSPACE : 8,
6501         /** Key constant 
6502         * @type Number */
6503         TAB : 9,
6504         /** Key constant 
6505         * @type Number */
6506         RETURN : 13,
6507         /** Key constant 
6508         * @type Number */
6509         ENTER : 13,
6510         /** Key constant 
6511         * @type Number */
6512         SHIFT : 16,
6513         /** Key constant 
6514         * @type Number */
6515         CONTROL : 17,
6516         /** Key constant 
6517         * @type Number */
6518         ESC : 27,
6519         /** Key constant 
6520         * @type Number */
6521         SPACE : 32,
6522         /** Key constant 
6523         * @type Number */
6524         PAGEUP : 33,
6525         /** Key constant 
6526         * @type Number */
6527         PAGEDOWN : 34,
6528         /** Key constant 
6529         * @type Number */
6530         END : 35,
6531         /** Key constant 
6532         * @type Number */
6533         HOME : 36,
6534         /** Key constant 
6535         * @type Number */
6536         LEFT : 37,
6537         /** Key constant 
6538         * @type Number */
6539         UP : 38,
6540         /** Key constant 
6541         * @type Number */
6542         RIGHT : 39,
6543         /** Key constant 
6544         * @type Number */
6545         DOWN : 40,
6546         /** Key constant 
6547         * @type Number */
6548         DELETE : 46,
6549         /** Key constant 
6550         * @type Number */
6551         F5 : 116,
6552
6553            /** @private */
6554         setEvent : function(e){
6555             if(e == this || (e && e.browserEvent)){ // already wrapped
6556                 return e;
6557             }
6558             this.browserEvent = e;
6559             if(e){
6560                 // normalize buttons
6561                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6562                 if(e.type == 'click' && this.button == -1){
6563                     this.button = 0;
6564                 }
6565                 this.type = e.type;
6566                 this.shiftKey = e.shiftKey;
6567                 // mac metaKey behaves like ctrlKey
6568                 this.ctrlKey = e.ctrlKey || e.metaKey;
6569                 this.altKey = e.altKey;
6570                 // in getKey these will be normalized for the mac
6571                 this.keyCode = e.keyCode;
6572                 // keyup warnings on firefox.
6573                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6574                 // cache the target for the delayed and or buffered events
6575                 this.target = E.getTarget(e);
6576                 // same for XY
6577                 this.xy = E.getXY(e);
6578             }else{
6579                 this.button = -1;
6580                 this.shiftKey = false;
6581                 this.ctrlKey = false;
6582                 this.altKey = false;
6583                 this.keyCode = 0;
6584                 this.charCode =0;
6585                 this.target = null;
6586                 this.xy = [0, 0];
6587             }
6588             return this;
6589         },
6590
6591         /**
6592          * Stop the event (preventDefault and stopPropagation)
6593          */
6594         stopEvent : function(){
6595             if(this.browserEvent){
6596                 if(this.browserEvent.type == 'mousedown'){
6597                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6598                 }
6599                 E.stopEvent(this.browserEvent);
6600             }
6601         },
6602
6603         /**
6604          * Prevents the browsers default handling of the event.
6605          */
6606         preventDefault : function(){
6607             if(this.browserEvent){
6608                 E.preventDefault(this.browserEvent);
6609             }
6610         },
6611
6612         /** @private */
6613         isNavKeyPress : function(){
6614             var k = this.keyCode;
6615             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6616             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6617         },
6618
6619         isSpecialKey : function(){
6620             var k = this.keyCode;
6621             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6622             (k == 16) || (k == 17) ||
6623             (k >= 18 && k <= 20) ||
6624             (k >= 33 && k <= 35) ||
6625             (k >= 36 && k <= 39) ||
6626             (k >= 44 && k <= 45);
6627         },
6628         /**
6629          * Cancels bubbling of the event.
6630          */
6631         stopPropagation : function(){
6632             if(this.browserEvent){
6633                 if(this.type == 'mousedown'){
6634                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6635                 }
6636                 E.stopPropagation(this.browserEvent);
6637             }
6638         },
6639
6640         /**
6641          * Gets the key code for the event.
6642          * @return {Number}
6643          */
6644         getCharCode : function(){
6645             return this.charCode || this.keyCode;
6646         },
6647
6648         /**
6649          * Returns a normalized keyCode for the event.
6650          * @return {Number} The key code
6651          */
6652         getKey : function(){
6653             var k = this.keyCode || this.charCode;
6654             return Roo.isSafari ? (safariKeys[k] || k) : k;
6655         },
6656
6657         /**
6658          * Gets the x coordinate of the event.
6659          * @return {Number}
6660          */
6661         getPageX : function(){
6662             return this.xy[0];
6663         },
6664
6665         /**
6666          * Gets the y coordinate of the event.
6667          * @return {Number}
6668          */
6669         getPageY : function(){
6670             return this.xy[1];
6671         },
6672
6673         /**
6674          * Gets the time of the event.
6675          * @return {Number}
6676          */
6677         getTime : function(){
6678             if(this.browserEvent){
6679                 return E.getTime(this.browserEvent);
6680             }
6681             return null;
6682         },
6683
6684         /**
6685          * Gets the page coordinates of the event.
6686          * @return {Array} The xy values like [x, y]
6687          */
6688         getXY : function(){
6689             return this.xy;
6690         },
6691
6692         /**
6693          * Gets the target for the event.
6694          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6695          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6696                 search as a number or element (defaults to 10 || document.body)
6697          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6698          * @return {HTMLelement}
6699          */
6700         getTarget : function(selector, maxDepth, returnEl){
6701             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6702         },
6703         /**
6704          * Gets the related target.
6705          * @return {HTMLElement}
6706          */
6707         getRelatedTarget : function(){
6708             if(this.browserEvent){
6709                 return E.getRelatedTarget(this.browserEvent);
6710             }
6711             return null;
6712         },
6713
6714         /**
6715          * Normalizes mouse wheel delta across browsers
6716          * @return {Number} The delta
6717          */
6718         getWheelDelta : function(){
6719             var e = this.browserEvent;
6720             var delta = 0;
6721             if(e.wheelDelta){ /* IE/Opera. */
6722                 delta = e.wheelDelta/120;
6723             }else if(e.detail){ /* Mozilla case. */
6724                 delta = -e.detail/3;
6725             }
6726             return delta;
6727         },
6728
6729         /**
6730          * Returns true if the control, meta, shift or alt key was pressed during this event.
6731          * @return {Boolean}
6732          */
6733         hasModifier : function(){
6734             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6735         },
6736
6737         /**
6738          * Returns true if the target of this event equals el or is a child of el
6739          * @param {String/HTMLElement/Element} el
6740          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6741          * @return {Boolean}
6742          */
6743         within : function(el, related){
6744             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6745             return t && Roo.fly(el).contains(t);
6746         },
6747
6748         getPoint : function(){
6749             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6750         }
6751     };
6752
6753     return new Roo.EventObjectImpl();
6754 }();
6755             
6756     /*
6757  * Based on:
6758  * Ext JS Library 1.1.1
6759  * Copyright(c) 2006-2007, Ext JS, LLC.
6760  *
6761  * Originally Released Under LGPL - original licence link has changed is not relivant.
6762  *
6763  * Fork - LGPL
6764  * <script type="text/javascript">
6765  */
6766
6767  
6768 // was in Composite Element!??!?!
6769  
6770 (function(){
6771     var D = Roo.lib.Dom;
6772     var E = Roo.lib.Event;
6773     var A = Roo.lib.Anim;
6774
6775     // local style camelizing for speed
6776     var propCache = {};
6777     var camelRe = /(-[a-z])/gi;
6778     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6779     var view = document.defaultView;
6780
6781 /**
6782  * @class Roo.Element
6783  * Represents an Element in the DOM.<br><br>
6784  * Usage:<br>
6785 <pre><code>
6786 var el = Roo.get("my-div");
6787
6788 // or with getEl
6789 var el = getEl("my-div");
6790
6791 // or with a DOM element
6792 var el = Roo.get(myDivElement);
6793 </code></pre>
6794  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6795  * each call instead of constructing a new one.<br><br>
6796  * <b>Animations</b><br />
6797  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6798  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6799 <pre>
6800 Option    Default   Description
6801 --------- --------  ---------------------------------------------
6802 duration  .35       The duration of the animation in seconds
6803 easing    easeOut   The YUI easing method
6804 callback  none      A function to execute when the anim completes
6805 scope     this      The scope (this) of the callback function
6806 </pre>
6807 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6808 * manipulate the animation. Here's an example:
6809 <pre><code>
6810 var el = Roo.get("my-div");
6811
6812 // no animation
6813 el.setWidth(100);
6814
6815 // default animation
6816 el.setWidth(100, true);
6817
6818 // animation with some options set
6819 el.setWidth(100, {
6820     duration: 1,
6821     callback: this.foo,
6822     scope: this
6823 });
6824
6825 // using the "anim" property to get the Anim object
6826 var opt = {
6827     duration: 1,
6828     callback: this.foo,
6829     scope: this
6830 };
6831 el.setWidth(100, opt);
6832 ...
6833 if(opt.anim.isAnimated()){
6834     opt.anim.stop();
6835 }
6836 </code></pre>
6837 * <b> Composite (Collections of) Elements</b><br />
6838  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6839  * @constructor Create a new Element directly.
6840  * @param {String/HTMLElement} element
6841  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6842  */
6843     Roo.Element = function(element, forceNew){
6844         var dom = typeof element == "string" ?
6845                 document.getElementById(element) : element;
6846         if(!dom){ // invalid id/element
6847             return null;
6848         }
6849         var id = dom.id;
6850         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6851             return Roo.Element.cache[id];
6852         }
6853
6854         /**
6855          * The DOM element
6856          * @type HTMLElement
6857          */
6858         this.dom = dom;
6859
6860         /**
6861          * The DOM element ID
6862          * @type String
6863          */
6864         this.id = id || Roo.id(dom);
6865     };
6866
6867     var El = Roo.Element;
6868
6869     El.prototype = {
6870         /**
6871          * The element's default display mode  (defaults to "")
6872          * @type String
6873          */
6874         originalDisplay : "",
6875
6876         visibilityMode : 1,
6877         /**
6878          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6879          * @type String
6880          */
6881         defaultUnit : "px",
6882         /**
6883          * Sets the element's visibility mode. When setVisible() is called it
6884          * will use this to determine whether to set the visibility or the display property.
6885          * @param visMode Element.VISIBILITY or Element.DISPLAY
6886          * @return {Roo.Element} this
6887          */
6888         setVisibilityMode : function(visMode){
6889             this.visibilityMode = visMode;
6890             return this;
6891         },
6892         /**
6893          * Convenience method for setVisibilityMode(Element.DISPLAY)
6894          * @param {String} display (optional) What to set display to when visible
6895          * @return {Roo.Element} this
6896          */
6897         enableDisplayMode : function(display){
6898             this.setVisibilityMode(El.DISPLAY);
6899             if(typeof display != "undefined") this.originalDisplay = display;
6900             return this;
6901         },
6902
6903         /**
6904          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6905          * @param {String} selector The simple selector to test
6906          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6907                 search as a number or element (defaults to 10 || document.body)
6908          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6909          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6910          */
6911         findParent : function(simpleSelector, maxDepth, returnEl){
6912             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6913             maxDepth = maxDepth || 50;
6914             if(typeof maxDepth != "number"){
6915                 stopEl = Roo.getDom(maxDepth);
6916                 maxDepth = 10;
6917             }
6918             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6919                 if(dq.is(p, simpleSelector)){
6920                     return returnEl ? Roo.get(p) : p;
6921                 }
6922                 depth++;
6923                 p = p.parentNode;
6924             }
6925             return null;
6926         },
6927
6928
6929         /**
6930          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6931          * @param {String} selector The simple selector to test
6932          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6933                 search as a number or element (defaults to 10 || document.body)
6934          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6935          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6936          */
6937         findParentNode : function(simpleSelector, maxDepth, returnEl){
6938             var p = Roo.fly(this.dom.parentNode, '_internal');
6939             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6940         },
6941
6942         /**
6943          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6944          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6945          * @param {String} selector The simple selector to test
6946          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6947                 search as a number or element (defaults to 10 || document.body)
6948          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6949          */
6950         up : function(simpleSelector, maxDepth){
6951             return this.findParentNode(simpleSelector, maxDepth, true);
6952         },
6953
6954
6955
6956         /**
6957          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6958          * @param {String} selector The simple selector to test
6959          * @return {Boolean} True if this element matches the selector, else false
6960          */
6961         is : function(simpleSelector){
6962             return Roo.DomQuery.is(this.dom, simpleSelector);
6963         },
6964
6965         /**
6966          * Perform animation on this element.
6967          * @param {Object} args The YUI animation control args
6968          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6969          * @param {Function} onComplete (optional) Function to call when animation completes
6970          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6971          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6972          * @return {Roo.Element} this
6973          */
6974         animate : function(args, duration, onComplete, easing, animType){
6975             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6976             return this;
6977         },
6978
6979         /*
6980          * @private Internal animation call
6981          */
6982         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6983             animType = animType || 'run';
6984             opt = opt || {};
6985             var anim = Roo.lib.Anim[animType](
6986                 this.dom, args,
6987                 (opt.duration || defaultDur) || .35,
6988                 (opt.easing || defaultEase) || 'easeOut',
6989                 function(){
6990                     Roo.callback(cb, this);
6991                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6992                 },
6993                 this
6994             );
6995             opt.anim = anim;
6996             return anim;
6997         },
6998
6999         // private legacy anim prep
7000         preanim : function(a, i){
7001             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7002         },
7003
7004         /**
7005          * Removes worthless text nodes
7006          * @param {Boolean} forceReclean (optional) By default the element
7007          * keeps track if it has been cleaned already so
7008          * you can call this over and over. However, if you update the element and
7009          * need to force a reclean, you can pass true.
7010          */
7011         clean : function(forceReclean){
7012             if(this.isCleaned && forceReclean !== true){
7013                 return this;
7014             }
7015             var ns = /\S/;
7016             var d = this.dom, n = d.firstChild, ni = -1;
7017             while(n){
7018                 var nx = n.nextSibling;
7019                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7020                     d.removeChild(n);
7021                 }else{
7022                     n.nodeIndex = ++ni;
7023                 }
7024                 n = nx;
7025             }
7026             this.isCleaned = true;
7027             return this;
7028         },
7029
7030         // private
7031         calcOffsetsTo : function(el){
7032             el = Roo.get(el);
7033             var d = el.dom;
7034             var restorePos = false;
7035             if(el.getStyle('position') == 'static'){
7036                 el.position('relative');
7037                 restorePos = true;
7038             }
7039             var x = 0, y =0;
7040             var op = this.dom;
7041             while(op && op != d && op.tagName != 'HTML'){
7042                 x+= op.offsetLeft;
7043                 y+= op.offsetTop;
7044                 op = op.offsetParent;
7045             }
7046             if(restorePos){
7047                 el.position('static');
7048             }
7049             return [x, y];
7050         },
7051
7052         /**
7053          * Scrolls this element into view within the passed container.
7054          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7055          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7056          * @return {Roo.Element} this
7057          */
7058         scrollIntoView : function(container, hscroll){
7059             var c = Roo.getDom(container) || document.body;
7060             var el = this.dom;
7061
7062             var o = this.calcOffsetsTo(c),
7063                 l = o[0],
7064                 t = o[1],
7065                 b = t+el.offsetHeight,
7066                 r = l+el.offsetWidth;
7067
7068             var ch = c.clientHeight;
7069             var ct = parseInt(c.scrollTop, 10);
7070             var cl = parseInt(c.scrollLeft, 10);
7071             var cb = ct + ch;
7072             var cr = cl + c.clientWidth;
7073
7074             if(t < ct){
7075                 c.scrollTop = t;
7076             }else if(b > cb){
7077                 c.scrollTop = b-ch;
7078             }
7079
7080             if(hscroll !== false){
7081                 if(l < cl){
7082                     c.scrollLeft = l;
7083                 }else if(r > cr){
7084                     c.scrollLeft = r-c.clientWidth;
7085                 }
7086             }
7087             return this;
7088         },
7089
7090         // private
7091         scrollChildIntoView : function(child, hscroll){
7092             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7093         },
7094
7095         /**
7096          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7097          * the new height may not be available immediately.
7098          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7099          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7100          * @param {Function} onComplete (optional) Function to call when animation completes
7101          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7102          * @return {Roo.Element} this
7103          */
7104         autoHeight : function(animate, duration, onComplete, easing){
7105             var oldHeight = this.getHeight();
7106             this.clip();
7107             this.setHeight(1); // force clipping
7108             setTimeout(function(){
7109                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7110                 if(!animate){
7111                     this.setHeight(height);
7112                     this.unclip();
7113                     if(typeof onComplete == "function"){
7114                         onComplete();
7115                     }
7116                 }else{
7117                     this.setHeight(oldHeight); // restore original height
7118                     this.setHeight(height, animate, duration, function(){
7119                         this.unclip();
7120                         if(typeof onComplete == "function") onComplete();
7121                     }.createDelegate(this), easing);
7122                 }
7123             }.createDelegate(this), 0);
7124             return this;
7125         },
7126
7127         /**
7128          * Returns true if this element is an ancestor of the passed element
7129          * @param {HTMLElement/String} el The element to check
7130          * @return {Boolean} True if this element is an ancestor of el, else false
7131          */
7132         contains : function(el){
7133             if(!el){return false;}
7134             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7135         },
7136
7137         /**
7138          * Checks whether the element is currently visible using both visibility and display properties.
7139          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7140          * @return {Boolean} True if the element is currently visible, else false
7141          */
7142         isVisible : function(deep) {
7143             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7144             if(deep !== true || !vis){
7145                 return vis;
7146             }
7147             var p = this.dom.parentNode;
7148             while(p && p.tagName.toLowerCase() != "body"){
7149                 if(!Roo.fly(p, '_isVisible').isVisible()){
7150                     return false;
7151                 }
7152                 p = p.parentNode;
7153             }
7154             return true;
7155         },
7156
7157         /**
7158          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7159          * @param {String} selector The CSS selector
7160          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7161          * @return {CompositeElement/CompositeElementLite} The composite element
7162          */
7163         select : function(selector, unique){
7164             return El.select(selector, unique, this.dom);
7165         },
7166
7167         /**
7168          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7169          * @param {String} selector The CSS selector
7170          * @return {Array} An array of the matched nodes
7171          */
7172         query : function(selector, unique){
7173             return Roo.DomQuery.select(selector, this.dom);
7174         },
7175
7176         /**
7177          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7178          * @param {String} selector The CSS selector
7179          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7180          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7181          */
7182         child : function(selector, returnDom){
7183             var n = Roo.DomQuery.selectNode(selector, this.dom);
7184             return returnDom ? n : Roo.get(n);
7185         },
7186
7187         /**
7188          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7189          * @param {String} selector The CSS selector
7190          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7191          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7192          */
7193         down : function(selector, returnDom){
7194             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7195             return returnDom ? n : Roo.get(n);
7196         },
7197
7198         /**
7199          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7200          * @param {String} group The group the DD object is member of
7201          * @param {Object} config The DD config object
7202          * @param {Object} overrides An object containing methods to override/implement on the DD object
7203          * @return {Roo.dd.DD} The DD object
7204          */
7205         initDD : function(group, config, overrides){
7206             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7207             return Roo.apply(dd, overrides);
7208         },
7209
7210         /**
7211          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7212          * @param {String} group The group the DDProxy object is member of
7213          * @param {Object} config The DDProxy config object
7214          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7215          * @return {Roo.dd.DDProxy} The DDProxy object
7216          */
7217         initDDProxy : function(group, config, overrides){
7218             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7219             return Roo.apply(dd, overrides);
7220         },
7221
7222         /**
7223          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7224          * @param {String} group The group the DDTarget object is member of
7225          * @param {Object} config The DDTarget config object
7226          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7227          * @return {Roo.dd.DDTarget} The DDTarget object
7228          */
7229         initDDTarget : function(group, config, overrides){
7230             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7231             return Roo.apply(dd, overrides);
7232         },
7233
7234         /**
7235          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7236          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7237          * @param {Boolean} visible Whether the element is visible
7238          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7239          * @return {Roo.Element} this
7240          */
7241          setVisible : function(visible, animate){
7242             if(!animate || !A){
7243                 if(this.visibilityMode == El.DISPLAY){
7244                     this.setDisplayed(visible);
7245                 }else{
7246                     this.fixDisplay();
7247                     this.dom.style.visibility = visible ? "visible" : "hidden";
7248                 }
7249             }else{
7250                 // closure for composites
7251                 var dom = this.dom;
7252                 var visMode = this.visibilityMode;
7253                 if(visible){
7254                     this.setOpacity(.01);
7255                     this.setVisible(true);
7256                 }
7257                 this.anim({opacity: { to: (visible?1:0) }},
7258                       this.preanim(arguments, 1),
7259                       null, .35, 'easeIn', function(){
7260                          if(!visible){
7261                              if(visMode == El.DISPLAY){
7262                                  dom.style.display = "none";
7263                              }else{
7264                                  dom.style.visibility = "hidden";
7265                              }
7266                              Roo.get(dom).setOpacity(1);
7267                          }
7268                      });
7269             }
7270             return this;
7271         },
7272
7273         /**
7274          * Returns true if display is not "none"
7275          * @return {Boolean}
7276          */
7277         isDisplayed : function() {
7278             return this.getStyle("display") != "none";
7279         },
7280
7281         /**
7282          * Toggles the element's visibility or display, depending on visibility mode.
7283          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7284          * @return {Roo.Element} this
7285          */
7286         toggle : function(animate){
7287             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7288             return this;
7289         },
7290
7291         /**
7292          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7293          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7294          * @return {Roo.Element} this
7295          */
7296         setDisplayed : function(value) {
7297             if(typeof value == "boolean"){
7298                value = value ? this.originalDisplay : "none";
7299             }
7300             this.setStyle("display", value);
7301             return this;
7302         },
7303
7304         /**
7305          * Tries to focus the element. Any exceptions are caught and ignored.
7306          * @return {Roo.Element} this
7307          */
7308         focus : function() {
7309             try{
7310                 this.dom.focus();
7311             }catch(e){}
7312             return this;
7313         },
7314
7315         /**
7316          * Tries to blur the element. Any exceptions are caught and ignored.
7317          * @return {Roo.Element} this
7318          */
7319         blur : function() {
7320             try{
7321                 this.dom.blur();
7322             }catch(e){}
7323             return this;
7324         },
7325
7326         /**
7327          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7328          * @param {String/Array} className The CSS class to add, or an array of classes
7329          * @return {Roo.Element} this
7330          */
7331         addClass : function(className){
7332             if(className instanceof Array){
7333                 for(var i = 0, len = className.length; i < len; i++) {
7334                     this.addClass(className[i]);
7335                 }
7336             }else{
7337                 if(className && !this.hasClass(className)){
7338                     this.dom.className = this.dom.className + " " + className;
7339                 }
7340             }
7341             return this;
7342         },
7343
7344         /**
7345          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7346          * @param {String/Array} className The CSS class to add, or an array of classes
7347          * @return {Roo.Element} this
7348          */
7349         radioClass : function(className){
7350             var siblings = this.dom.parentNode.childNodes;
7351             for(var i = 0; i < siblings.length; i++) {
7352                 var s = siblings[i];
7353                 if(s.nodeType == 1){
7354                     Roo.get(s).removeClass(className);
7355                 }
7356             }
7357             this.addClass(className);
7358             return this;
7359         },
7360
7361         /**
7362          * Removes one or more CSS classes from the element.
7363          * @param {String/Array} className The CSS class to remove, or an array of classes
7364          * @return {Roo.Element} this
7365          */
7366         removeClass : function(className){
7367             if(!className || !this.dom.className){
7368                 return this;
7369             }
7370             if(className instanceof Array){
7371                 for(var i = 0, len = className.length; i < len; i++) {
7372                     this.removeClass(className[i]);
7373                 }
7374             }else{
7375                 if(this.hasClass(className)){
7376                     var re = this.classReCache[className];
7377                     if (!re) {
7378                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7379                        this.classReCache[className] = re;
7380                     }
7381                     this.dom.className =
7382                         this.dom.className.replace(re, " ");
7383                 }
7384             }
7385             return this;
7386         },
7387
7388         // private
7389         classReCache: {},
7390
7391         /**
7392          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7393          * @param {String} className The CSS class to toggle
7394          * @return {Roo.Element} this
7395          */
7396         toggleClass : function(className){
7397             if(this.hasClass(className)){
7398                 this.removeClass(className);
7399             }else{
7400                 this.addClass(className);
7401             }
7402             return this;
7403         },
7404
7405         /**
7406          * Checks if the specified CSS class exists on this element's DOM node.
7407          * @param {String} className The CSS class to check for
7408          * @return {Boolean} True if the class exists, else false
7409          */
7410         hasClass : function(className){
7411             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7412         },
7413
7414         /**
7415          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7416          * @param {String} oldClassName The CSS class to replace
7417          * @param {String} newClassName The replacement CSS class
7418          * @return {Roo.Element} this
7419          */
7420         replaceClass : function(oldClassName, newClassName){
7421             this.removeClass(oldClassName);
7422             this.addClass(newClassName);
7423             return this;
7424         },
7425
7426         /**
7427          * Returns an object with properties matching the styles requested.
7428          * For example, el.getStyles('color', 'font-size', 'width') might return
7429          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7430          * @param {String} style1 A style name
7431          * @param {String} style2 A style name
7432          * @param {String} etc.
7433          * @return {Object} The style object
7434          */
7435         getStyles : function(){
7436             var a = arguments, len = a.length, r = {};
7437             for(var i = 0; i < len; i++){
7438                 r[a[i]] = this.getStyle(a[i]);
7439             }
7440             return r;
7441         },
7442
7443         /**
7444          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7445          * @param {String} property The style property whose value is returned.
7446          * @return {String} The current value of the style property for this element.
7447          */
7448         getStyle : function(){
7449             return view && view.getComputedStyle ?
7450                 function(prop){
7451                     var el = this.dom, v, cs, camel;
7452                     if(prop == 'float'){
7453                         prop = "cssFloat";
7454                     }
7455                     if(el.style && (v = el.style[prop])){
7456                         return v;
7457                     }
7458                     if(cs = view.getComputedStyle(el, "")){
7459                         if(!(camel = propCache[prop])){
7460                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7461                         }
7462                         return cs[camel];
7463                     }
7464                     return null;
7465                 } :
7466                 function(prop){
7467                     var el = this.dom, v, cs, camel;
7468                     if(prop == 'opacity'){
7469                         if(typeof el.style.filter == 'string'){
7470                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7471                             if(m){
7472                                 var fv = parseFloat(m[1]);
7473                                 if(!isNaN(fv)){
7474                                     return fv ? fv / 100 : 0;
7475                                 }
7476                             }
7477                         }
7478                         return 1;
7479                     }else if(prop == 'float'){
7480                         prop = "styleFloat";
7481                     }
7482                     if(!(camel = propCache[prop])){
7483                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7484                     }
7485                     if(v = el.style[camel]){
7486                         return v;
7487                     }
7488                     if(cs = el.currentStyle){
7489                         return cs[camel];
7490                     }
7491                     return null;
7492                 };
7493         }(),
7494
7495         /**
7496          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7497          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7498          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7499          * @return {Roo.Element} this
7500          */
7501         setStyle : function(prop, value){
7502             if(typeof prop == "string"){
7503                 
7504                 if (prop == 'float') {
7505                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7506                     return this;
7507                 }
7508                 
7509                 var camel;
7510                 if(!(camel = propCache[prop])){
7511                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7512                 }
7513                 
7514                 if(camel == 'opacity') {
7515                     this.setOpacity(value);
7516                 }else{
7517                     this.dom.style[camel] = value;
7518                 }
7519             }else{
7520                 for(var style in prop){
7521                     if(typeof prop[style] != "function"){
7522                        this.setStyle(style, prop[style]);
7523                     }
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         /**
7530          * More flexible version of {@link #setStyle} for setting style properties.
7531          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7532          * a function which returns such a specification.
7533          * @return {Roo.Element} this
7534          */
7535         applyStyles : function(style){
7536             Roo.DomHelper.applyStyles(this.dom, style);
7537             return this;
7538         },
7539
7540         /**
7541           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7542           * @return {Number} The X position of the element
7543           */
7544         getX : function(){
7545             return D.getX(this.dom);
7546         },
7547
7548         /**
7549           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7550           * @return {Number} The Y position of the element
7551           */
7552         getY : function(){
7553             return D.getY(this.dom);
7554         },
7555
7556         /**
7557           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7558           * @return {Array} The XY position of the element
7559           */
7560         getXY : function(){
7561             return D.getXY(this.dom);
7562         },
7563
7564         /**
7565          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7566          * @param {Number} The X position of the element
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Roo.Element} this
7569          */
7570         setX : function(x, animate){
7571             if(!animate || !A){
7572                 D.setX(this.dom, x);
7573             }else{
7574                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7581          * @param {Number} The Y position of the element
7582          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7583          * @return {Roo.Element} this
7584          */
7585         setY : function(y, animate){
7586             if(!animate || !A){
7587                 D.setY(this.dom, y);
7588             }else{
7589                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7590             }
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7596          * @param {String} left The left CSS property value
7597          * @return {Roo.Element} this
7598          */
7599         setLeft : function(left){
7600             this.setStyle("left", this.addUnits(left));
7601             return this;
7602         },
7603
7604         /**
7605          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7606          * @param {String} top The top CSS property value
7607          * @return {Roo.Element} this
7608          */
7609         setTop : function(top){
7610             this.setStyle("top", this.addUnits(top));
7611             return this;
7612         },
7613
7614         /**
7615          * Sets the element's CSS right style.
7616          * @param {String} right The right CSS property value
7617          * @return {Roo.Element} this
7618          */
7619         setRight : function(right){
7620             this.setStyle("right", this.addUnits(right));
7621             return this;
7622         },
7623
7624         /**
7625          * Sets the element's CSS bottom style.
7626          * @param {String} bottom The bottom CSS property value
7627          * @return {Roo.Element} this
7628          */
7629         setBottom : function(bottom){
7630             this.setStyle("bottom", this.addUnits(bottom));
7631             return this;
7632         },
7633
7634         /**
7635          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7636          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7637          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7638          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7639          * @return {Roo.Element} this
7640          */
7641         setXY : function(pos, animate){
7642             if(!animate || !A){
7643                 D.setXY(this.dom, pos);
7644             }else{
7645                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7646             }
7647             return this;
7648         },
7649
7650         /**
7651          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7652          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7653          * @param {Number} x X value for new position (coordinates are page-based)
7654          * @param {Number} y Y value for new position (coordinates are page-based)
7655          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7656          * @return {Roo.Element} this
7657          */
7658         setLocation : function(x, y, animate){
7659             this.setXY([x, y], this.preanim(arguments, 2));
7660             return this;
7661         },
7662
7663         /**
7664          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7665          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7666          * @param {Number} x X value for new position (coordinates are page-based)
7667          * @param {Number} y Y value for new position (coordinates are page-based)
7668          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7669          * @return {Roo.Element} this
7670          */
7671         moveTo : function(x, y, animate){
7672             this.setXY([x, y], this.preanim(arguments, 2));
7673             return this;
7674         },
7675
7676         /**
7677          * Returns the region of the given element.
7678          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7679          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7680          */
7681         getRegion : function(){
7682             return D.getRegion(this.dom);
7683         },
7684
7685         /**
7686          * Returns the offset height of the element
7687          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7688          * @return {Number} The element's height
7689          */
7690         getHeight : function(contentHeight){
7691             var h = this.dom.offsetHeight || 0;
7692             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7693         },
7694
7695         /**
7696          * Returns the offset width of the element
7697          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7698          * @return {Number} The element's width
7699          */
7700         getWidth : function(contentWidth){
7701             var w = this.dom.offsetWidth || 0;
7702             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7703         },
7704
7705         /**
7706          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7707          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7708          * if a height has not been set using CSS.
7709          * @return {Number}
7710          */
7711         getComputedHeight : function(){
7712             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7713             if(!h){
7714                 h = parseInt(this.getStyle('height'), 10) || 0;
7715                 if(!this.isBorderBox()){
7716                     h += this.getFrameWidth('tb');
7717                 }
7718             }
7719             return h;
7720         },
7721
7722         /**
7723          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7724          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7725          * if a width has not been set using CSS.
7726          * @return {Number}
7727          */
7728         getComputedWidth : function(){
7729             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7730             if(!w){
7731                 w = parseInt(this.getStyle('width'), 10) || 0;
7732                 if(!this.isBorderBox()){
7733                     w += this.getFrameWidth('lr');
7734                 }
7735             }
7736             return w;
7737         },
7738
7739         /**
7740          * Returns the size of the element.
7741          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7742          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7743          */
7744         getSize : function(contentSize){
7745             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7746         },
7747
7748         /**
7749          * Returns the width and height of the viewport.
7750          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7751          */
7752         getViewSize : function(){
7753             var d = this.dom, doc = document, aw = 0, ah = 0;
7754             if(d == doc || d == doc.body){
7755                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7756             }else{
7757                 return {
7758                     width : d.clientWidth,
7759                     height: d.clientHeight
7760                 };
7761             }
7762         },
7763
7764         /**
7765          * Returns the value of the "value" attribute
7766          * @param {Boolean} asNumber true to parse the value as a number
7767          * @return {String/Number}
7768          */
7769         getValue : function(asNumber){
7770             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7771         },
7772
7773         // private
7774         adjustWidth : function(width){
7775             if(typeof width == "number"){
7776                 if(this.autoBoxAdjust && !this.isBorderBox()){
7777                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7778                 }
7779                 if(width < 0){
7780                     width = 0;
7781                 }
7782             }
7783             return width;
7784         },
7785
7786         // private
7787         adjustHeight : function(height){
7788             if(typeof height == "number"){
7789                if(this.autoBoxAdjust && !this.isBorderBox()){
7790                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7791                }
7792                if(height < 0){
7793                    height = 0;
7794                }
7795             }
7796             return height;
7797         },
7798
7799         /**
7800          * Set the width of the element
7801          * @param {Number} width The new width
7802          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7803          * @return {Roo.Element} this
7804          */
7805         setWidth : function(width, animate){
7806             width = this.adjustWidth(width);
7807             if(!animate || !A){
7808                 this.dom.style.width = this.addUnits(width);
7809             }else{
7810                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7811             }
7812             return this;
7813         },
7814
7815         /**
7816          * Set the height of the element
7817          * @param {Number} height The new height
7818          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7819          * @return {Roo.Element} this
7820          */
7821          setHeight : function(height, animate){
7822             height = this.adjustHeight(height);
7823             if(!animate || !A){
7824                 this.dom.style.height = this.addUnits(height);
7825             }else{
7826                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7827             }
7828             return this;
7829         },
7830
7831         /**
7832          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7833          * @param {Number} width The new width
7834          * @param {Number} height The new height
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838          setSize : function(width, height, animate){
7839             if(typeof width == "object"){ // in case of object from getSize()
7840                 height = width.height; width = width.width;
7841             }
7842             width = this.adjustWidth(width); height = this.adjustHeight(height);
7843             if(!animate || !A){
7844                 this.dom.style.width = this.addUnits(width);
7845                 this.dom.style.height = this.addUnits(height);
7846             }else{
7847                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7848             }
7849             return this;
7850         },
7851
7852         /**
7853          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7854          * @param {Number} x X value for new position (coordinates are page-based)
7855          * @param {Number} y Y value for new position (coordinates are page-based)
7856          * @param {Number} width The new width
7857          * @param {Number} height The new height
7858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setBounds : function(x, y, width, height, animate){
7862             if(!animate || !A){
7863                 this.setSize(width, height);
7864                 this.setLocation(x, y);
7865             }else{
7866                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7867                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7868                               this.preanim(arguments, 4), 'motion');
7869             }
7870             return this;
7871         },
7872
7873         /**
7874          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7875          * @param {Roo.lib.Region} region The region to fill
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setRegion : function(region, animate){
7880             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7881             return this;
7882         },
7883
7884         /**
7885          * Appends an event handler
7886          *
7887          * @param {String}   eventName     The type of event to append
7888          * @param {Function} fn        The method the event invokes
7889          * @param {Object} scope       (optional) The scope (this object) of the fn
7890          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7891          */
7892         addListener : function(eventName, fn, scope, options){
7893             if (this.dom) {
7894                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7895             }
7896         },
7897
7898         /**
7899          * Removes an event handler from this element
7900          * @param {String} eventName the type of event to remove
7901          * @param {Function} fn the method the event invokes
7902          * @return {Roo.Element} this
7903          */
7904         removeListener : function(eventName, fn){
7905             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7906             return this;
7907         },
7908
7909         /**
7910          * Removes all previous added listeners from this element
7911          * @return {Roo.Element} this
7912          */
7913         removeAllListeners : function(){
7914             E.purgeElement(this.dom);
7915             return this;
7916         },
7917
7918         relayEvent : function(eventName, observable){
7919             this.on(eventName, function(e){
7920                 observable.fireEvent(eventName, e);
7921             });
7922         },
7923
7924         /**
7925          * Set the opacity of the element
7926          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7927          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7928          * @return {Roo.Element} this
7929          */
7930          setOpacity : function(opacity, animate){
7931             if(!animate || !A){
7932                 var s = this.dom.style;
7933                 if(Roo.isIE){
7934                     s.zoom = 1;
7935                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7936                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7937                 }else{
7938                     s.opacity = opacity;
7939                 }
7940             }else{
7941                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7942             }
7943             return this;
7944         },
7945
7946         /**
7947          * Gets the left X coordinate
7948          * @param {Boolean} local True to get the local css position instead of page coordinate
7949          * @return {Number}
7950          */
7951         getLeft : function(local){
7952             if(!local){
7953                 return this.getX();
7954             }else{
7955                 return parseInt(this.getStyle("left"), 10) || 0;
7956             }
7957         },
7958
7959         /**
7960          * Gets the right X coordinate of the element (element X position + element width)
7961          * @param {Boolean} local True to get the local css position instead of page coordinate
7962          * @return {Number}
7963          */
7964         getRight : function(local){
7965             if(!local){
7966                 return this.getX() + this.getWidth();
7967             }else{
7968                 return (this.getLeft(true) + this.getWidth()) || 0;
7969             }
7970         },
7971
7972         /**
7973          * Gets the top Y coordinate
7974          * @param {Boolean} local True to get the local css position instead of page coordinate
7975          * @return {Number}
7976          */
7977         getTop : function(local) {
7978             if(!local){
7979                 return this.getY();
7980             }else{
7981                 return parseInt(this.getStyle("top"), 10) || 0;
7982             }
7983         },
7984
7985         /**
7986          * Gets the bottom Y coordinate of the element (element Y position + element height)
7987          * @param {Boolean} local True to get the local css position instead of page coordinate
7988          * @return {Number}
7989          */
7990         getBottom : function(local){
7991             if(!local){
7992                 return this.getY() + this.getHeight();
7993             }else{
7994                 return (this.getTop(true) + this.getHeight()) || 0;
7995             }
7996         },
7997
7998         /**
7999         * Initializes positioning on this element. If a desired position is not passed, it will make the
8000         * the element positioned relative IF it is not already positioned.
8001         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8002         * @param {Number} zIndex (optional) The zIndex to apply
8003         * @param {Number} x (optional) Set the page X position
8004         * @param {Number} y (optional) Set the page Y position
8005         */
8006         position : function(pos, zIndex, x, y){
8007             if(!pos){
8008                if(this.getStyle('position') == 'static'){
8009                    this.setStyle('position', 'relative');
8010                }
8011             }else{
8012                 this.setStyle("position", pos);
8013             }
8014             if(zIndex){
8015                 this.setStyle("z-index", zIndex);
8016             }
8017             if(x !== undefined && y !== undefined){
8018                 this.setXY([x, y]);
8019             }else if(x !== undefined){
8020                 this.setX(x);
8021             }else if(y !== undefined){
8022                 this.setY(y);
8023             }
8024         },
8025
8026         /**
8027         * Clear positioning back to the default when the document was loaded
8028         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8029         * @return {Roo.Element} this
8030          */
8031         clearPositioning : function(value){
8032             value = value ||'';
8033             this.setStyle({
8034                 "left": value,
8035                 "right": value,
8036                 "top": value,
8037                 "bottom": value,
8038                 "z-index": "",
8039                 "position" : "static"
8040             });
8041             return this;
8042         },
8043
8044         /**
8045         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8046         * snapshot before performing an update and then restoring the element.
8047         * @return {Object}
8048         */
8049         getPositioning : function(){
8050             var l = this.getStyle("left");
8051             var t = this.getStyle("top");
8052             return {
8053                 "position" : this.getStyle("position"),
8054                 "left" : l,
8055                 "right" : l ? "" : this.getStyle("right"),
8056                 "top" : t,
8057                 "bottom" : t ? "" : this.getStyle("bottom"),
8058                 "z-index" : this.getStyle("z-index")
8059             };
8060         },
8061
8062         /**
8063          * Gets the width of the border(s) for the specified side(s)
8064          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8065          * passing lr would get the border (l)eft width + the border (r)ight width.
8066          * @return {Number} The width of the sides passed added together
8067          */
8068         getBorderWidth : function(side){
8069             return this.addStyles(side, El.borders);
8070         },
8071
8072         /**
8073          * Gets the width of the padding(s) for the specified side(s)
8074          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8075          * passing lr would get the padding (l)eft + the padding (r)ight.
8076          * @return {Number} The padding of the sides passed added together
8077          */
8078         getPadding : function(side){
8079             return this.addStyles(side, El.paddings);
8080         },
8081
8082         /**
8083         * Set positioning with an object returned by getPositioning().
8084         * @param {Object} posCfg
8085         * @return {Roo.Element} this
8086          */
8087         setPositioning : function(pc){
8088             this.applyStyles(pc);
8089             if(pc.right == "auto"){
8090                 this.dom.style.right = "";
8091             }
8092             if(pc.bottom == "auto"){
8093                 this.dom.style.bottom = "";
8094             }
8095             return this;
8096         },
8097
8098         // private
8099         fixDisplay : function(){
8100             if(this.getStyle("display") == "none"){
8101                 this.setStyle("visibility", "hidden");
8102                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8103                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8104                     this.setStyle("display", "block");
8105                 }
8106             }
8107         },
8108
8109         /**
8110          * Quick set left and top adding default units
8111          * @param {String} left The left CSS property value
8112          * @param {String} top The top CSS property value
8113          * @return {Roo.Element} this
8114          */
8115          setLeftTop : function(left, top){
8116             this.dom.style.left = this.addUnits(left);
8117             this.dom.style.top = this.addUnits(top);
8118             return this;
8119         },
8120
8121         /**
8122          * Move this element relative to its current position.
8123          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8124          * @param {Number} distance How far to move the element in pixels
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          move : function(direction, distance, animate){
8129             var xy = this.getXY();
8130             direction = direction.toLowerCase();
8131             switch(direction){
8132                 case "l":
8133                 case "left":
8134                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8135                     break;
8136                case "r":
8137                case "right":
8138                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8139                     break;
8140                case "t":
8141                case "top":
8142                case "up":
8143                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8144                     break;
8145                case "b":
8146                case "bottom":
8147                case "down":
8148                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8149                     break;
8150             }
8151             return this;
8152         },
8153
8154         /**
8155          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8156          * @return {Roo.Element} this
8157          */
8158         clip : function(){
8159             if(!this.isClipped){
8160                this.isClipped = true;
8161                this.originalClip = {
8162                    "o": this.getStyle("overflow"),
8163                    "x": this.getStyle("overflow-x"),
8164                    "y": this.getStyle("overflow-y")
8165                };
8166                this.setStyle("overflow", "hidden");
8167                this.setStyle("overflow-x", "hidden");
8168                this.setStyle("overflow-y", "hidden");
8169             }
8170             return this;
8171         },
8172
8173         /**
8174          *  Return clipping (overflow) to original clipping before clip() was called
8175          * @return {Roo.Element} this
8176          */
8177         unclip : function(){
8178             if(this.isClipped){
8179                 this.isClipped = false;
8180                 var o = this.originalClip;
8181                 if(o.o){this.setStyle("overflow", o.o);}
8182                 if(o.x){this.setStyle("overflow-x", o.x);}
8183                 if(o.y){this.setStyle("overflow-y", o.y);}
8184             }
8185             return this;
8186         },
8187
8188
8189         /**
8190          * Gets the x,y coordinates specified by the anchor position on the element.
8191          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8192          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8193          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8194          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8195          * @return {Array} [x, y] An array containing the element's x and y coordinates
8196          */
8197         getAnchorXY : function(anchor, local, s){
8198             //Passing a different size is useful for pre-calculating anchors,
8199             //especially for anchored animations that change the el size.
8200
8201             var w, h, vp = false;
8202             if(!s){
8203                 var d = this.dom;
8204                 if(d == document.body || d == document){
8205                     vp = true;
8206                     w = D.getViewWidth(); h = D.getViewHeight();
8207                 }else{
8208                     w = this.getWidth(); h = this.getHeight();
8209                 }
8210             }else{
8211                 w = s.width;  h = s.height;
8212             }
8213             var x = 0, y = 0, r = Math.round;
8214             switch((anchor || "tl").toLowerCase()){
8215                 case "c":
8216                     x = r(w*.5);
8217                     y = r(h*.5);
8218                 break;
8219                 case "t":
8220                     x = r(w*.5);
8221                     y = 0;
8222                 break;
8223                 case "l":
8224                     x = 0;
8225                     y = r(h*.5);
8226                 break;
8227                 case "r":
8228                     x = w;
8229                     y = r(h*.5);
8230                 break;
8231                 case "b":
8232                     x = r(w*.5);
8233                     y = h;
8234                 break;
8235                 case "tl":
8236                     x = 0;
8237                     y = 0;
8238                 break;
8239                 case "bl":
8240                     x = 0;
8241                     y = h;
8242                 break;
8243                 case "br":
8244                     x = w;
8245                     y = h;
8246                 break;
8247                 case "tr":
8248                     x = w;
8249                     y = 0;
8250                 break;
8251             }
8252             if(local === true){
8253                 return [x, y];
8254             }
8255             if(vp){
8256                 var sc = this.getScroll();
8257                 return [x + sc.left, y + sc.top];
8258             }
8259             //Add the element's offset xy
8260             var o = this.getXY();
8261             return [x+o[0], y+o[1]];
8262         },
8263
8264         /**
8265          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8266          * supported position values.
8267          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8268          * @param {String} position The position to align to.
8269          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8270          * @return {Array} [x, y]
8271          */
8272         getAlignToXY : function(el, p, o){
8273             el = Roo.get(el);
8274             var d = this.dom;
8275             if(!el.dom){
8276                 throw "Element.alignTo with an element that doesn't exist";
8277             }
8278             var c = false; //constrain to viewport
8279             var p1 = "", p2 = "";
8280             o = o || [0,0];
8281
8282             if(!p){
8283                 p = "tl-bl";
8284             }else if(p == "?"){
8285                 p = "tl-bl?";
8286             }else if(p.indexOf("-") == -1){
8287                 p = "tl-" + p;
8288             }
8289             p = p.toLowerCase();
8290             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8291             if(!m){
8292                throw "Element.alignTo with an invalid alignment " + p;
8293             }
8294             p1 = m[1]; p2 = m[2]; c = !!m[3];
8295
8296             //Subtract the aligned el's internal xy from the target's offset xy
8297             //plus custom offset to get the aligned el's new offset xy
8298             var a1 = this.getAnchorXY(p1, true);
8299             var a2 = el.getAnchorXY(p2, false);
8300             var x = a2[0] - a1[0] + o[0];
8301             var y = a2[1] - a1[1] + o[1];
8302             if(c){
8303                 //constrain the aligned el to viewport if necessary
8304                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8305                 // 5px of margin for ie
8306                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8307
8308                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8309                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8310                 //otherwise swap the aligned el to the opposite border of the target.
8311                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8312                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8313                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8314                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8315
8316                var doc = document;
8317                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8318                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8319
8320                if((x+w) > dw + scrollX){
8321                     x = swapX ? r.left-w : dw+scrollX-w;
8322                 }
8323                if(x < scrollX){
8324                    x = swapX ? r.right : scrollX;
8325                }
8326                if((y+h) > dh + scrollY){
8327                     y = swapY ? r.top-h : dh+scrollY-h;
8328                 }
8329                if (y < scrollY){
8330                    y = swapY ? r.bottom : scrollY;
8331                }
8332             }
8333             return [x,y];
8334         },
8335
8336         // private
8337         getConstrainToXY : function(){
8338             var os = {top:0, left:0, bottom:0, right: 0};
8339
8340             return function(el, local, offsets, proposedXY){
8341                 el = Roo.get(el);
8342                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8343
8344                 var vw, vh, vx = 0, vy = 0;
8345                 if(el.dom == document.body || el.dom == document){
8346                     vw = Roo.lib.Dom.getViewWidth();
8347                     vh = Roo.lib.Dom.getViewHeight();
8348                 }else{
8349                     vw = el.dom.clientWidth;
8350                     vh = el.dom.clientHeight;
8351                     if(!local){
8352                         var vxy = el.getXY();
8353                         vx = vxy[0];
8354                         vy = vxy[1];
8355                     }
8356                 }
8357
8358                 var s = el.getScroll();
8359
8360                 vx += offsets.left + s.left;
8361                 vy += offsets.top + s.top;
8362
8363                 vw -= offsets.right;
8364                 vh -= offsets.bottom;
8365
8366                 var vr = vx+vw;
8367                 var vb = vy+vh;
8368
8369                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8370                 var x = xy[0], y = xy[1];
8371                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8372
8373                 // only move it if it needs it
8374                 var moved = false;
8375
8376                 // first validate right/bottom
8377                 if((x + w) > vr){
8378                     x = vr - w;
8379                     moved = true;
8380                 }
8381                 if((y + h) > vb){
8382                     y = vb - h;
8383                     moved = true;
8384                 }
8385                 // then make sure top/left isn't negative
8386                 if(x < vx){
8387                     x = vx;
8388                     moved = true;
8389                 }
8390                 if(y < vy){
8391                     y = vy;
8392                     moved = true;
8393                 }
8394                 return moved ? [x, y] : false;
8395             };
8396         }(),
8397
8398         // private
8399         adjustForConstraints : function(xy, parent, offsets){
8400             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8401         },
8402
8403         /**
8404          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8405          * document it aligns it to the viewport.
8406          * The position parameter is optional, and can be specified in any one of the following formats:
8407          * <ul>
8408          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8409          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8410          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8411          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8412          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8413          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8414          * </ul>
8415          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8416          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8417          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8418          * that specified in order to enforce the viewport constraints.
8419          * Following are all of the supported anchor positions:
8420     <pre>
8421     Value  Description
8422     -----  -----------------------------
8423     tl     The top left corner (default)
8424     t      The center of the top edge
8425     tr     The top right corner
8426     l      The center of the left edge
8427     c      In the center of the element
8428     r      The center of the right edge
8429     bl     The bottom left corner
8430     b      The center of the bottom edge
8431     br     The bottom right corner
8432     </pre>
8433     Example Usage:
8434     <pre><code>
8435     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8436     el.alignTo("other-el");
8437
8438     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8439     el.alignTo("other-el", "tr?");
8440
8441     // align the bottom right corner of el with the center left edge of other-el
8442     el.alignTo("other-el", "br-l?");
8443
8444     // align the center of el with the bottom left corner of other-el and
8445     // adjust the x position by -6 pixels (and the y position by 0)
8446     el.alignTo("other-el", "c-bl", [-6, 0]);
8447     </code></pre>
8448          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8449          * @param {String} position The position to align to.
8450          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8451          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8452          * @return {Roo.Element} this
8453          */
8454         alignTo : function(element, position, offsets, animate){
8455             var xy = this.getAlignToXY(element, position, offsets);
8456             this.setXY(xy, this.preanim(arguments, 3));
8457             return this;
8458         },
8459
8460         /**
8461          * Anchors an element to another element and realigns it when the window is resized.
8462          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8463          * @param {String} position The position to align to.
8464          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8465          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8466          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8467          * is a number, it is used as the buffer delay (defaults to 50ms).
8468          * @param {Function} callback The function to call after the animation finishes
8469          * @return {Roo.Element} this
8470          */
8471         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8472             var action = function(){
8473                 this.alignTo(el, alignment, offsets, animate);
8474                 Roo.callback(callback, this);
8475             };
8476             Roo.EventManager.onWindowResize(action, this);
8477             var tm = typeof monitorScroll;
8478             if(tm != 'undefined'){
8479                 Roo.EventManager.on(window, 'scroll', action, this,
8480                     {buffer: tm == 'number' ? monitorScroll : 50});
8481             }
8482             action.call(this); // align immediately
8483             return this;
8484         },
8485         /**
8486          * Clears any opacity settings from this element. Required in some cases for IE.
8487          * @return {Roo.Element} this
8488          */
8489         clearOpacity : function(){
8490             if (window.ActiveXObject) {
8491                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8492                     this.dom.style.filter = "";
8493                 }
8494             } else {
8495                 this.dom.style.opacity = "";
8496                 this.dom.style["-moz-opacity"] = "";
8497                 this.dom.style["-khtml-opacity"] = "";
8498             }
8499             return this;
8500         },
8501
8502         /**
8503          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8504          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8505          * @return {Roo.Element} this
8506          */
8507         hide : function(animate){
8508             this.setVisible(false, this.preanim(arguments, 0));
8509             return this;
8510         },
8511
8512         /**
8513         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8514         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8515          * @return {Roo.Element} this
8516          */
8517         show : function(animate){
8518             this.setVisible(true, this.preanim(arguments, 0));
8519             return this;
8520         },
8521
8522         /**
8523          * @private Test if size has a unit, otherwise appends the default
8524          */
8525         addUnits : function(size){
8526             return Roo.Element.addUnits(size, this.defaultUnit);
8527         },
8528
8529         /**
8530          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8531          * @return {Roo.Element} this
8532          */
8533         beginMeasure : function(){
8534             var el = this.dom;
8535             if(el.offsetWidth || el.offsetHeight){
8536                 return this; // offsets work already
8537             }
8538             var changed = [];
8539             var p = this.dom, b = document.body; // start with this element
8540             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8541                 var pe = Roo.get(p);
8542                 if(pe.getStyle('display') == 'none'){
8543                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8544                     p.style.visibility = "hidden";
8545                     p.style.display = "block";
8546                 }
8547                 p = p.parentNode;
8548             }
8549             this._measureChanged = changed;
8550             return this;
8551
8552         },
8553
8554         /**
8555          * Restores displays to before beginMeasure was called
8556          * @return {Roo.Element} this
8557          */
8558         endMeasure : function(){
8559             var changed = this._measureChanged;
8560             if(changed){
8561                 for(var i = 0, len = changed.length; i < len; i++) {
8562                     var r = changed[i];
8563                     r.el.style.visibility = r.visibility;
8564                     r.el.style.display = "none";
8565                 }
8566                 this._measureChanged = null;
8567             }
8568             return this;
8569         },
8570
8571         /**
8572         * Update the innerHTML of this element, optionally searching for and processing scripts
8573         * @param {String} html The new HTML
8574         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8575         * @param {Function} callback For async script loading you can be noticed when the update completes
8576         * @return {Roo.Element} this
8577          */
8578         update : function(html, loadScripts, callback){
8579             if(typeof html == "undefined"){
8580                 html = "";
8581             }
8582             if(loadScripts !== true){
8583                 this.dom.innerHTML = html;
8584                 if(typeof callback == "function"){
8585                     callback();
8586                 }
8587                 return this;
8588             }
8589             var id = Roo.id();
8590             var dom = this.dom;
8591
8592             html += '<span id="' + id + '"></span>';
8593
8594             E.onAvailable(id, function(){
8595                 var hd = document.getElementsByTagName("head")[0];
8596                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8597                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8598                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8599
8600                 var match;
8601                 while(match = re.exec(html)){
8602                     var attrs = match[1];
8603                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8604                     if(srcMatch && srcMatch[2]){
8605                        var s = document.createElement("script");
8606                        s.src = srcMatch[2];
8607                        var typeMatch = attrs.match(typeRe);
8608                        if(typeMatch && typeMatch[2]){
8609                            s.type = typeMatch[2];
8610                        }
8611                        hd.appendChild(s);
8612                     }else if(match[2] && match[2].length > 0){
8613                         if(window.execScript) {
8614                            window.execScript(match[2]);
8615                         } else {
8616                             /**
8617                              * eval:var:id
8618                              * eval:var:dom
8619                              * eval:var:html
8620                              * 
8621                              */
8622                            window.eval(match[2]);
8623                         }
8624                     }
8625                 }
8626                 var el = document.getElementById(id);
8627                 if(el){el.parentNode.removeChild(el);}
8628                 if(typeof callback == "function"){
8629                     callback();
8630                 }
8631             });
8632             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8633             return this;
8634         },
8635
8636         /**
8637          * Direct access to the UpdateManager update() method (takes the same parameters).
8638          * @param {String/Function} url The url for this request or a function to call to get the url
8639          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8640          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8641          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8642          * @return {Roo.Element} this
8643          */
8644         load : function(){
8645             var um = this.getUpdateManager();
8646             um.update.apply(um, arguments);
8647             return this;
8648         },
8649
8650         /**
8651         * Gets this element's UpdateManager
8652         * @return {Roo.UpdateManager} The UpdateManager
8653         */
8654         getUpdateManager : function(){
8655             if(!this.updateManager){
8656                 this.updateManager = new Roo.UpdateManager(this);
8657             }
8658             return this.updateManager;
8659         },
8660
8661         /**
8662          * Disables text selection for this element (normalized across browsers)
8663          * @return {Roo.Element} this
8664          */
8665         unselectable : function(){
8666             this.dom.unselectable = "on";
8667             this.swallowEvent("selectstart", true);
8668             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8669             this.addClass("x-unselectable");
8670             return this;
8671         },
8672
8673         /**
8674         * Calculates the x, y to center this element on the screen
8675         * @return {Array} The x, y values [x, y]
8676         */
8677         getCenterXY : function(){
8678             return this.getAlignToXY(document, 'c-c');
8679         },
8680
8681         /**
8682         * Centers the Element in either the viewport, or another Element.
8683         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8684         */
8685         center : function(centerIn){
8686             this.alignTo(centerIn || document, 'c-c');
8687             return this;
8688         },
8689
8690         /**
8691          * Tests various css rules/browsers to determine if this element uses a border box
8692          * @return {Boolean}
8693          */
8694         isBorderBox : function(){
8695             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8696         },
8697
8698         /**
8699          * Return a box {x, y, width, height} that can be used to set another elements
8700          * size/location to match this element.
8701          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8702          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8703          * @return {Object} box An object in the format {x, y, width, height}
8704          */
8705         getBox : function(contentBox, local){
8706             var xy;
8707             if(!local){
8708                 xy = this.getXY();
8709             }else{
8710                 var left = parseInt(this.getStyle("left"), 10) || 0;
8711                 var top = parseInt(this.getStyle("top"), 10) || 0;
8712                 xy = [left, top];
8713             }
8714             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8715             if(!contentBox){
8716                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8717             }else{
8718                 var l = this.getBorderWidth("l")+this.getPadding("l");
8719                 var r = this.getBorderWidth("r")+this.getPadding("r");
8720                 var t = this.getBorderWidth("t")+this.getPadding("t");
8721                 var b = this.getBorderWidth("b")+this.getPadding("b");
8722                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8723             }
8724             bx.right = bx.x + bx.width;
8725             bx.bottom = bx.y + bx.height;
8726             return bx;
8727         },
8728
8729         /**
8730          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8731          for more information about the sides.
8732          * @param {String} sides
8733          * @return {Number}
8734          */
8735         getFrameWidth : function(sides, onlyContentBox){
8736             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8737         },
8738
8739         /**
8740          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8741          * @param {Object} box The box to fill {x, y, width, height}
8742          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8743          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8744          * @return {Roo.Element} this
8745          */
8746         setBox : function(box, adjust, animate){
8747             var w = box.width, h = box.height;
8748             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8749                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8750                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8751             }
8752             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8753             return this;
8754         },
8755
8756         /**
8757          * Forces the browser to repaint this element
8758          * @return {Roo.Element} this
8759          */
8760          repaint : function(){
8761             var dom = this.dom;
8762             this.addClass("x-repaint");
8763             setTimeout(function(){
8764                 Roo.get(dom).removeClass("x-repaint");
8765             }, 1);
8766             return this;
8767         },
8768
8769         /**
8770          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8771          * then it returns the calculated width of the sides (see getPadding)
8772          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8773          * @return {Object/Number}
8774          */
8775         getMargins : function(side){
8776             if(!side){
8777                 return {
8778                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8779                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8780                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8781                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8782                 };
8783             }else{
8784                 return this.addStyles(side, El.margins);
8785              }
8786         },
8787
8788         // private
8789         addStyles : function(sides, styles){
8790             var val = 0, v, w;
8791             for(var i = 0, len = sides.length; i < len; i++){
8792                 v = this.getStyle(styles[sides.charAt(i)]);
8793                 if(v){
8794                      w = parseInt(v, 10);
8795                      if(w){ val += w; }
8796                 }
8797             }
8798             return val;
8799         },
8800
8801         /**
8802          * Creates a proxy element of this element
8803          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8804          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8805          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8806          * @return {Roo.Element} The new proxy element
8807          */
8808         createProxy : function(config, renderTo, matchBox){
8809             if(renderTo){
8810                 renderTo = Roo.getDom(renderTo);
8811             }else{
8812                 renderTo = document.body;
8813             }
8814             config = typeof config == "object" ?
8815                 config : {tag : "div", cls: config};
8816             var proxy = Roo.DomHelper.append(renderTo, config, true);
8817             if(matchBox){
8818                proxy.setBox(this.getBox());
8819             }
8820             return proxy;
8821         },
8822
8823         /**
8824          * Puts a mask over this element to disable user interaction. Requires core.css.
8825          * This method can only be applied to elements which accept child nodes.
8826          * @param {String} msg (optional) A message to display in the mask
8827          * @param {String} msgCls (optional) A css class to apply to the msg element
8828          * @return {Element} The mask  element
8829          */
8830         mask : function(msg, msgCls){
8831             if(this.getStyle("position") == "static"){
8832                 this.setStyle("position", "relative");
8833             }
8834             if(!this._mask){
8835                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8836             }
8837             this.addClass("x-masked");
8838             this._mask.setDisplayed(true);
8839             if(typeof msg == 'string'){
8840                 if(!this._maskMsg){
8841                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8842                 }
8843                 var mm = this._maskMsg;
8844                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8845                 mm.dom.firstChild.innerHTML = msg;
8846                 mm.setDisplayed(true);
8847                 mm.center(this);
8848             }
8849             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8850                 this._mask.setHeight(this.getHeight());
8851             }
8852             return this._mask;
8853         },
8854
8855         /**
8856          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8857          * it is cached for reuse.
8858          */
8859         unmask : function(removeEl){
8860             if(this._mask){
8861                 if(removeEl === true){
8862                     this._mask.remove();
8863                     delete this._mask;
8864                     if(this._maskMsg){
8865                         this._maskMsg.remove();
8866                         delete this._maskMsg;
8867                     }
8868                 }else{
8869                     this._mask.setDisplayed(false);
8870                     if(this._maskMsg){
8871                         this._maskMsg.setDisplayed(false);
8872                     }
8873                 }
8874             }
8875             this.removeClass("x-masked");
8876         },
8877
8878         /**
8879          * Returns true if this element is masked
8880          * @return {Boolean}
8881          */
8882         isMasked : function(){
8883             return this._mask && this._mask.isVisible();
8884         },
8885
8886         /**
8887          * Creates an iframe shim for this element to keep selects and other windowed objects from
8888          * showing through.
8889          * @return {Roo.Element} The new shim element
8890          */
8891         createShim : function(){
8892             var el = document.createElement('iframe');
8893             el.frameBorder = 'no';
8894             el.className = 'roo-shim';
8895             if(Roo.isIE && Roo.isSecure){
8896                 el.src = Roo.SSL_SECURE_URL;
8897             }
8898             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8899             shim.autoBoxAdjust = false;
8900             return shim;
8901         },
8902
8903         /**
8904          * Removes this element from the DOM and deletes it from the cache
8905          */
8906         remove : function(){
8907             if(this.dom.parentNode){
8908                 this.dom.parentNode.removeChild(this.dom);
8909             }
8910             delete El.cache[this.dom.id];
8911         },
8912
8913         /**
8914          * Sets up event handlers to add and remove a css class when the mouse is over this element
8915          * @param {String} className
8916          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8917          * mouseout events for children elements
8918          * @return {Roo.Element} this
8919          */
8920         addClassOnOver : function(className, preventFlicker){
8921             this.on("mouseover", function(){
8922                 Roo.fly(this, '_internal').addClass(className);
8923             }, this.dom);
8924             var removeFn = function(e){
8925                 if(preventFlicker !== true || !e.within(this, true)){
8926                     Roo.fly(this, '_internal').removeClass(className);
8927                 }
8928             };
8929             this.on("mouseout", removeFn, this.dom);
8930             return this;
8931         },
8932
8933         /**
8934          * Sets up event handlers to add and remove a css class when this element has the focus
8935          * @param {String} className
8936          * @return {Roo.Element} this
8937          */
8938         addClassOnFocus : function(className){
8939             this.on("focus", function(){
8940                 Roo.fly(this, '_internal').addClass(className);
8941             }, this.dom);
8942             this.on("blur", function(){
8943                 Roo.fly(this, '_internal').removeClass(className);
8944             }, this.dom);
8945             return this;
8946         },
8947         /**
8948          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8949          * @param {String} className
8950          * @return {Roo.Element} this
8951          */
8952         addClassOnClick : function(className){
8953             var dom = this.dom;
8954             this.on("mousedown", function(){
8955                 Roo.fly(dom, '_internal').addClass(className);
8956                 var d = Roo.get(document);
8957                 var fn = function(){
8958                     Roo.fly(dom, '_internal').removeClass(className);
8959                     d.removeListener("mouseup", fn);
8960                 };
8961                 d.on("mouseup", fn);
8962             });
8963             return this;
8964         },
8965
8966         /**
8967          * Stops the specified event from bubbling and optionally prevents the default action
8968          * @param {String} eventName
8969          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8970          * @return {Roo.Element} this
8971          */
8972         swallowEvent : function(eventName, preventDefault){
8973             var fn = function(e){
8974                 e.stopPropagation();
8975                 if(preventDefault){
8976                     e.preventDefault();
8977                 }
8978             };
8979             if(eventName instanceof Array){
8980                 for(var i = 0, len = eventName.length; i < len; i++){
8981                      this.on(eventName[i], fn);
8982                 }
8983                 return this;
8984             }
8985             this.on(eventName, fn);
8986             return this;
8987         },
8988
8989         /**
8990          * @private
8991          */
8992       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8993
8994         /**
8995          * Sizes this element to its parent element's dimensions performing
8996          * neccessary box adjustments.
8997          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8998          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8999          * @return {Roo.Element} this
9000          */
9001         fitToParent : function(monitorResize, targetParent) {
9002           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9003           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9004           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9005             return;
9006           }
9007           var p = Roo.get(targetParent || this.dom.parentNode);
9008           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9009           if (monitorResize === true) {
9010             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9011             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9012           }
9013           return this;
9014         },
9015
9016         /**
9017          * Gets the next sibling, skipping text nodes
9018          * @return {HTMLElement} The next sibling or null
9019          */
9020         getNextSibling : function(){
9021             var n = this.dom.nextSibling;
9022             while(n && n.nodeType != 1){
9023                 n = n.nextSibling;
9024             }
9025             return n;
9026         },
9027
9028         /**
9029          * Gets the previous sibling, skipping text nodes
9030          * @return {HTMLElement} The previous sibling or null
9031          */
9032         getPrevSibling : function(){
9033             var n = this.dom.previousSibling;
9034             while(n && n.nodeType != 1){
9035                 n = n.previousSibling;
9036             }
9037             return n;
9038         },
9039
9040
9041         /**
9042          * Appends the passed element(s) to this element
9043          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9044          * @return {Roo.Element} this
9045          */
9046         appendChild: function(el){
9047             el = Roo.get(el);
9048             el.appendTo(this);
9049             return this;
9050         },
9051
9052         /**
9053          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9054          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9055          * automatically generated with the specified attributes.
9056          * @param {HTMLElement} insertBefore (optional) a child element of this element
9057          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9058          * @return {Roo.Element} The new child element
9059          */
9060         createChild: function(config, insertBefore, returnDom){
9061             config = config || {tag:'div'};
9062             if(insertBefore){
9063                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9064             }
9065             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9066         },
9067
9068         /**
9069          * Appends this element to the passed element
9070          * @param {String/HTMLElement/Element} el The new parent element
9071          * @return {Roo.Element} this
9072          */
9073         appendTo: function(el){
9074             el = Roo.getDom(el);
9075             el.appendChild(this.dom);
9076             return this;
9077         },
9078
9079         /**
9080          * Inserts this element before the passed element in the DOM
9081          * @param {String/HTMLElement/Element} el The element to insert before
9082          * @return {Roo.Element} this
9083          */
9084         insertBefore: function(el){
9085             el = Roo.getDom(el);
9086             el.parentNode.insertBefore(this.dom, el);
9087             return this;
9088         },
9089
9090         /**
9091          * Inserts this element after the passed element in the DOM
9092          * @param {String/HTMLElement/Element} el The element to insert after
9093          * @return {Roo.Element} this
9094          */
9095         insertAfter: function(el){
9096             el = Roo.getDom(el);
9097             el.parentNode.insertBefore(this.dom, el.nextSibling);
9098             return this;
9099         },
9100
9101         /**
9102          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9103          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9104          * @return {Roo.Element} The new child
9105          */
9106         insertFirst: function(el, returnDom){
9107             el = el || {};
9108             if(typeof el == 'object' && !el.nodeType){ // dh config
9109                 return this.createChild(el, this.dom.firstChild, returnDom);
9110             }else{
9111                 el = Roo.getDom(el);
9112                 this.dom.insertBefore(el, this.dom.firstChild);
9113                 return !returnDom ? Roo.get(el) : el;
9114             }
9115         },
9116
9117         /**
9118          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9119          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9120          * @param {String} where (optional) 'before' or 'after' defaults to before
9121          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9122          * @return {Roo.Element} the inserted Element
9123          */
9124         insertSibling: function(el, where, returnDom){
9125             where = where ? where.toLowerCase() : 'before';
9126             el = el || {};
9127             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9128
9129             if(typeof el == 'object' && !el.nodeType){ // dh config
9130                 if(where == 'after' && !this.dom.nextSibling){
9131                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9132                 }else{
9133                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9134                 }
9135
9136             }else{
9137                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9138                             where == 'before' ? this.dom : this.dom.nextSibling);
9139                 if(!returnDom){
9140                     rt = Roo.get(rt);
9141                 }
9142             }
9143             return rt;
9144         },
9145
9146         /**
9147          * Creates and wraps this element with another element
9148          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9149          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9150          * @return {HTMLElement/Element} The newly created wrapper element
9151          */
9152         wrap: function(config, returnDom){
9153             if(!config){
9154                 config = {tag: "div"};
9155             }
9156             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9157             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9158             return newEl;
9159         },
9160
9161         /**
9162          * Replaces the passed element with this element
9163          * @param {String/HTMLElement/Element} el The element to replace
9164          * @return {Roo.Element} this
9165          */
9166         replace: function(el){
9167             el = Roo.get(el);
9168             this.insertBefore(el);
9169             el.remove();
9170             return this;
9171         },
9172
9173         /**
9174          * Inserts an html fragment into this element
9175          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9176          * @param {String} html The HTML fragment
9177          * @param {Boolean} returnEl True to return an Roo.Element
9178          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9179          */
9180         insertHtml : function(where, html, returnEl){
9181             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9182             return returnEl ? Roo.get(el) : el;
9183         },
9184
9185         /**
9186          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9187          * @param {Object} o The object with the attributes
9188          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9189          * @return {Roo.Element} this
9190          */
9191         set : function(o, useSet){
9192             var el = this.dom;
9193             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9194             for(var attr in o){
9195                 if(attr == "style" || typeof o[attr] == "function") continue;
9196                 if(attr=="cls"){
9197                     el.className = o["cls"];
9198                 }else{
9199                     if(useSet) el.setAttribute(attr, o[attr]);
9200                     else el[attr] = o[attr];
9201                 }
9202             }
9203             if(o.style){
9204                 Roo.DomHelper.applyStyles(el, o.style);
9205             }
9206             return this;
9207         },
9208
9209         /**
9210          * Convenience method for constructing a KeyMap
9211          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9212          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9213          * @param {Function} fn The function to call
9214          * @param {Object} scope (optional) The scope of the function
9215          * @return {Roo.KeyMap} The KeyMap created
9216          */
9217         addKeyListener : function(key, fn, scope){
9218             var config;
9219             if(typeof key != "object" || key instanceof Array){
9220                 config = {
9221                     key: key,
9222                     fn: fn,
9223                     scope: scope
9224                 };
9225             }else{
9226                 config = {
9227                     key : key.key,
9228                     shift : key.shift,
9229                     ctrl : key.ctrl,
9230                     alt : key.alt,
9231                     fn: fn,
9232                     scope: scope
9233                 };
9234             }
9235             return new Roo.KeyMap(this, config);
9236         },
9237
9238         /**
9239          * Creates a KeyMap for this element
9240          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9241          * @return {Roo.KeyMap} The KeyMap created
9242          */
9243         addKeyMap : function(config){
9244             return new Roo.KeyMap(this, config);
9245         },
9246
9247         /**
9248          * Returns true if this element is scrollable.
9249          * @return {Boolean}
9250          */
9251          isScrollable : function(){
9252             var dom = this.dom;
9253             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9254         },
9255
9256         /**
9257          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9258          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9259          * @param {Number} value The new scroll value
9260          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9261          * @return {Element} this
9262          */
9263
9264         scrollTo : function(side, value, animate){
9265             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9266             if(!animate || !A){
9267                 this.dom[prop] = value;
9268             }else{
9269                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9270                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9271             }
9272             return this;
9273         },
9274
9275         /**
9276          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9277          * within this element's scrollable range.
9278          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9279          * @param {Number} distance How far to scroll the element in pixels
9280          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9281          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9282          * was scrolled as far as it could go.
9283          */
9284          scroll : function(direction, distance, animate){
9285              if(!this.isScrollable()){
9286                  return;
9287              }
9288              var el = this.dom;
9289              var l = el.scrollLeft, t = el.scrollTop;
9290              var w = el.scrollWidth, h = el.scrollHeight;
9291              var cw = el.clientWidth, ch = el.clientHeight;
9292              direction = direction.toLowerCase();
9293              var scrolled = false;
9294              var a = this.preanim(arguments, 2);
9295              switch(direction){
9296                  case "l":
9297                  case "left":
9298                      if(w - l > cw){
9299                          var v = Math.min(l + distance, w-cw);
9300                          this.scrollTo("left", v, a);
9301                          scrolled = true;
9302                      }
9303                      break;
9304                 case "r":
9305                 case "right":
9306                      if(l > 0){
9307                          var v = Math.max(l - distance, 0);
9308                          this.scrollTo("left", v, a);
9309                          scrolled = true;
9310                      }
9311                      break;
9312                 case "t":
9313                 case "top":
9314                 case "up":
9315                      if(t > 0){
9316                          var v = Math.max(t - distance, 0);
9317                          this.scrollTo("top", v, a);
9318                          scrolled = true;
9319                      }
9320                      break;
9321                 case "b":
9322                 case "bottom":
9323                 case "down":
9324                      if(h - t > ch){
9325                          var v = Math.min(t + distance, h-ch);
9326                          this.scrollTo("top", v, a);
9327                          scrolled = true;
9328                      }
9329                      break;
9330              }
9331              return scrolled;
9332         },
9333
9334         /**
9335          * Translates the passed page coordinates into left/top css values for this element
9336          * @param {Number/Array} x The page x or an array containing [x, y]
9337          * @param {Number} y The page y
9338          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9339          */
9340         translatePoints : function(x, y){
9341             if(typeof x == 'object' || x instanceof Array){
9342                 y = x[1]; x = x[0];
9343             }
9344             var p = this.getStyle('position');
9345             var o = this.getXY();
9346
9347             var l = parseInt(this.getStyle('left'), 10);
9348             var t = parseInt(this.getStyle('top'), 10);
9349
9350             if(isNaN(l)){
9351                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9352             }
9353             if(isNaN(t)){
9354                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9355             }
9356
9357             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9358         },
9359
9360         /**
9361          * Returns the current scroll position of the element.
9362          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9363          */
9364         getScroll : function(){
9365             var d = this.dom, doc = document;
9366             if(d == doc || d == doc.body){
9367                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9368                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9369                 return {left: l, top: t};
9370             }else{
9371                 return {left: d.scrollLeft, top: d.scrollTop};
9372             }
9373         },
9374
9375         /**
9376          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9377          * are convert to standard 6 digit hex color.
9378          * @param {String} attr The css attribute
9379          * @param {String} defaultValue The default value to use when a valid color isn't found
9380          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9381          * YUI color anims.
9382          */
9383         getColor : function(attr, defaultValue, prefix){
9384             var v = this.getStyle(attr);
9385             if(!v || v == "transparent" || v == "inherit") {
9386                 return defaultValue;
9387             }
9388             var color = typeof prefix == "undefined" ? "#" : prefix;
9389             if(v.substr(0, 4) == "rgb("){
9390                 var rvs = v.slice(4, v.length -1).split(",");
9391                 for(var i = 0; i < 3; i++){
9392                     var h = parseInt(rvs[i]).toString(16);
9393                     if(h < 16){
9394                         h = "0" + h;
9395                     }
9396                     color += h;
9397                 }
9398             } else {
9399                 if(v.substr(0, 1) == "#"){
9400                     if(v.length == 4) {
9401                         for(var i = 1; i < 4; i++){
9402                             var c = v.charAt(i);
9403                             color +=  c + c;
9404                         }
9405                     }else if(v.length == 7){
9406                         color += v.substr(1);
9407                     }
9408                 }
9409             }
9410             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9411         },
9412
9413         /**
9414          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9415          * gradient background, rounded corners and a 4-way shadow.
9416          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9417          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9418          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9419          * @return {Roo.Element} this
9420          */
9421         boxWrap : function(cls){
9422             cls = cls || 'x-box';
9423             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9424             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9425             return el;
9426         },
9427
9428         /**
9429          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9430          * @param {String} namespace The namespace in which to look for the attribute
9431          * @param {String} name The attribute name
9432          * @return {String} The attribute value
9433          */
9434         getAttributeNS : Roo.isIE ? function(ns, name){
9435             var d = this.dom;
9436             var type = typeof d[ns+":"+name];
9437             if(type != 'undefined' && type != 'unknown'){
9438                 return d[ns+":"+name];
9439             }
9440             return d[name];
9441         } : function(ns, name){
9442             var d = this.dom;
9443             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9444         }
9445     };
9446
9447     var ep = El.prototype;
9448
9449     /**
9450      * Appends an event handler (Shorthand for addListener)
9451      * @param {String}   eventName     The type of event to append
9452      * @param {Function} fn        The method the event invokes
9453      * @param {Object} scope       (optional) The scope (this object) of the fn
9454      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9455      * @method
9456      */
9457     ep.on = ep.addListener;
9458         // backwards compat
9459     ep.mon = ep.addListener;
9460
9461     /**
9462      * Removes an event handler from this element (shorthand for removeListener)
9463      * @param {String} eventName the type of event to remove
9464      * @param {Function} fn the method the event invokes
9465      * @return {Roo.Element} this
9466      * @method
9467      */
9468     ep.un = ep.removeListener;
9469
9470     /**
9471      * true to automatically adjust width and height settings for box-model issues (default to true)
9472      */
9473     ep.autoBoxAdjust = true;
9474
9475     // private
9476     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9477
9478     // private
9479     El.addUnits = function(v, defaultUnit){
9480         if(v === "" || v == "auto"){
9481             return v;
9482         }
9483         if(v === undefined){
9484             return '';
9485         }
9486         if(typeof v == "number" || !El.unitPattern.test(v)){
9487             return v + (defaultUnit || 'px');
9488         }
9489         return v;
9490     };
9491
9492     // special markup used throughout Roo when box wrapping elements
9493     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9494     /**
9495      * Visibility mode constant - Use visibility to hide element
9496      * @static
9497      * @type Number
9498      */
9499     El.VISIBILITY = 1;
9500     /**
9501      * Visibility mode constant - Use display to hide element
9502      * @static
9503      * @type Number
9504      */
9505     El.DISPLAY = 2;
9506
9507     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9508     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9509     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9510
9511
9512
9513     /**
9514      * @private
9515      */
9516     El.cache = {};
9517
9518     var docEl;
9519
9520     /**
9521      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9522      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9523      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9524      * @return {Element} The Element object
9525      * @static
9526      */
9527     El.get = function(el){
9528         var ex, elm, id;
9529         if(!el){ return null; }
9530         if(typeof el == "string"){ // element id
9531             if(!(elm = document.getElementById(el))){
9532                 return null;
9533             }
9534             if(ex = El.cache[el]){
9535                 ex.dom = elm;
9536             }else{
9537                 ex = El.cache[el] = new El(elm);
9538             }
9539             return ex;
9540         }else if(el.tagName){ // dom element
9541             if(!(id = el.id)){
9542                 id = Roo.id(el);
9543             }
9544             if(ex = El.cache[id]){
9545                 ex.dom = el;
9546             }else{
9547                 ex = El.cache[id] = new El(el);
9548             }
9549             return ex;
9550         }else if(el instanceof El){
9551             if(el != docEl){
9552                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9553                                                               // catch case where it hasn't been appended
9554                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9555             }
9556             return el;
9557         }else if(el.isComposite){
9558             return el;
9559         }else if(el instanceof Array){
9560             return El.select(el);
9561         }else if(el == document){
9562             // create a bogus element object representing the document object
9563             if(!docEl){
9564                 var f = function(){};
9565                 f.prototype = El.prototype;
9566                 docEl = new f();
9567                 docEl.dom = document;
9568             }
9569             return docEl;
9570         }
9571         return null;
9572     };
9573
9574     // private
9575     El.uncache = function(el){
9576         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9577             if(a[i]){
9578                 delete El.cache[a[i].id || a[i]];
9579             }
9580         }
9581     };
9582
9583     // private
9584     // Garbage collection - uncache elements/purge listeners on orphaned elements
9585     // so we don't hold a reference and cause the browser to retain them
9586     El.garbageCollect = function(){
9587         if(!Roo.enableGarbageCollector){
9588             clearInterval(El.collectorThread);
9589             return;
9590         }
9591         for(var eid in El.cache){
9592             var el = El.cache[eid], d = el.dom;
9593             // -------------------------------------------------------
9594             // Determining what is garbage:
9595             // -------------------------------------------------------
9596             // !d
9597             // dom node is null, definitely garbage
9598             // -------------------------------------------------------
9599             // !d.parentNode
9600             // no parentNode == direct orphan, definitely garbage
9601             // -------------------------------------------------------
9602             // !d.offsetParent && !document.getElementById(eid)
9603             // display none elements have no offsetParent so we will
9604             // also try to look it up by it's id. However, check
9605             // offsetParent first so we don't do unneeded lookups.
9606             // This enables collection of elements that are not orphans
9607             // directly, but somewhere up the line they have an orphan
9608             // parent.
9609             // -------------------------------------------------------
9610             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9611                 delete El.cache[eid];
9612                 if(d && Roo.enableListenerCollection){
9613                     E.purgeElement(d);
9614                 }
9615             }
9616         }
9617     }
9618     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9619
9620
9621     // dom is optional
9622     El.Flyweight = function(dom){
9623         this.dom = dom;
9624     };
9625     El.Flyweight.prototype = El.prototype;
9626
9627     El._flyweights = {};
9628     /**
9629      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9630      * the dom node can be overwritten by other code.
9631      * @param {String/HTMLElement} el The dom node or id
9632      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9633      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9634      * @static
9635      * @return {Element} The shared Element object
9636      */
9637     El.fly = function(el, named){
9638         named = named || '_global';
9639         el = Roo.getDom(el);
9640         if(!el){
9641             return null;
9642         }
9643         if(!El._flyweights[named]){
9644             El._flyweights[named] = new El.Flyweight();
9645         }
9646         El._flyweights[named].dom = el;
9647         return El._flyweights[named];
9648     };
9649
9650     /**
9651      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9652      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9653      * Shorthand of {@link Roo.Element#get}
9654      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9655      * @return {Element} The Element object
9656      * @member Roo
9657      * @method get
9658      */
9659     Roo.get = El.get;
9660     /**
9661      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9662      * the dom node can be overwritten by other code.
9663      * Shorthand of {@link Roo.Element#fly}
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      * @member Roo
9670      * @method fly
9671      */
9672     Roo.fly = El.fly;
9673
9674     // speedy lookup for elements never to box adjust
9675     var noBoxAdjust = Roo.isStrict ? {
9676         select:1
9677     } : {
9678         input:1, select:1, textarea:1
9679     };
9680     if(Roo.isIE || Roo.isGecko){
9681         noBoxAdjust['button'] = 1;
9682     }
9683
9684
9685     Roo.EventManager.on(window, 'unload', function(){
9686         delete El.cache;
9687         delete El._flyweights;
9688     });
9689 })();
9690
9691
9692
9693
9694 if(Roo.DomQuery){
9695     Roo.Element.selectorFunction = Roo.DomQuery.select;
9696 }
9697
9698 Roo.Element.select = function(selector, unique, root){
9699     var els;
9700     if(typeof selector == "string"){
9701         els = Roo.Element.selectorFunction(selector, root);
9702     }else if(selector.length !== undefined){
9703         els = selector;
9704     }else{
9705         throw "Invalid selector";
9706     }
9707     if(unique === true){
9708         return new Roo.CompositeElement(els);
9709     }else{
9710         return new Roo.CompositeElementLite(els);
9711     }
9712 };
9713 /**
9714  * Selects elements based on the passed CSS selector to enable working on them as 1.
9715  * @param {String/Array} selector The CSS selector or an array of elements
9716  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9717  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9718  * @return {CompositeElementLite/CompositeElement}
9719  * @member Roo
9720  * @method select
9721  */
9722 Roo.select = Roo.Element.select;
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737 /*
9738  * Based on:
9739  * Ext JS Library 1.1.1
9740  * Copyright(c) 2006-2007, Ext JS, LLC.
9741  *
9742  * Originally Released Under LGPL - original licence link has changed is not relivant.
9743  *
9744  * Fork - LGPL
9745  * <script type="text/javascript">
9746  */
9747
9748
9749
9750 //Notifies Element that fx methods are available
9751 Roo.enableFx = true;
9752
9753 /**
9754  * @class Roo.Fx
9755  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9756  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9757  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9758  * Element effects to work.</p><br/>
9759  *
9760  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9761  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9762  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9763  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9764  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9765  * expected results and should be done with care.</p><br/>
9766  *
9767  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9768  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9769 <pre>
9770 Value  Description
9771 -----  -----------------------------
9772 tl     The top left corner
9773 t      The center of the top edge
9774 tr     The top right corner
9775 l      The center of the left edge
9776 r      The center of the right edge
9777 bl     The bottom left corner
9778 b      The center of the bottom edge
9779 br     The bottom right corner
9780 </pre>
9781  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9782  * below are common options that can be passed to any Fx method.</b>
9783  * @cfg {Function} callback A function called when the effect is finished
9784  * @cfg {Object} scope The scope of the effect function
9785  * @cfg {String} easing A valid Easing value for the effect
9786  * @cfg {String} afterCls A css class to apply after the effect
9787  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9788  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9789  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9790  * effects that end with the element being visually hidden, ignored otherwise)
9791  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9792  * a function which returns such a specification that will be applied to the Element after the effect finishes
9793  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9794  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9795  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9796  */
9797 Roo.Fx = {
9798         /**
9799          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9800          * origin for the slide effect.  This function automatically handles wrapping the element with
9801          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9802          * Usage:
9803          *<pre><code>
9804 // default: slide the element in from the top
9805 el.slideIn();
9806
9807 // custom: slide the element in from the right with a 2-second duration
9808 el.slideIn('r', { duration: 2 });
9809
9810 // common config options shown with default values
9811 el.slideIn('t', {
9812     easing: 'easeOut',
9813     duration: .5
9814 });
9815 </code></pre>
9816          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9817          * @param {Object} options (optional) Object literal with any of the Fx config options
9818          * @return {Roo.Element} The Element
9819          */
9820     slideIn : function(anchor, o){
9821         var el = this.getFxEl();
9822         o = o || {};
9823
9824         el.queueFx(o, function(){
9825
9826             anchor = anchor || "t";
9827
9828             // fix display to visibility
9829             this.fixDisplay();
9830
9831             // restore values after effect
9832             var r = this.getFxRestore();
9833             var b = this.getBox();
9834             // fixed size for slide
9835             this.setSize(b);
9836
9837             // wrap if needed
9838             var wrap = this.fxWrap(r.pos, o, "hidden");
9839
9840             var st = this.dom.style;
9841             st.visibility = "visible";
9842             st.position = "absolute";
9843
9844             // clear out temp styles after slide and unwrap
9845             var after = function(){
9846                 el.fxUnwrap(wrap, r.pos, o);
9847                 st.width = r.width;
9848                 st.height = r.height;
9849                 el.afterFx(o);
9850             };
9851             // time to calc the positions
9852             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9853
9854             switch(anchor.toLowerCase()){
9855                 case "t":
9856                     wrap.setSize(b.width, 0);
9857                     st.left = st.bottom = "0";
9858                     a = {height: bh};
9859                 break;
9860                 case "l":
9861                     wrap.setSize(0, b.height);
9862                     st.right = st.top = "0";
9863                     a = {width: bw};
9864                 break;
9865                 case "r":
9866                     wrap.setSize(0, b.height);
9867                     wrap.setX(b.right);
9868                     st.left = st.top = "0";
9869                     a = {width: bw, points: pt};
9870                 break;
9871                 case "b":
9872                     wrap.setSize(b.width, 0);
9873                     wrap.setY(b.bottom);
9874                     st.left = st.top = "0";
9875                     a = {height: bh, points: pt};
9876                 break;
9877                 case "tl":
9878                     wrap.setSize(0, 0);
9879                     st.right = st.bottom = "0";
9880                     a = {width: bw, height: bh};
9881                 break;
9882                 case "bl":
9883                     wrap.setSize(0, 0);
9884                     wrap.setY(b.y+b.height);
9885                     st.right = st.top = "0";
9886                     a = {width: bw, height: bh, points: pt};
9887                 break;
9888                 case "br":
9889                     wrap.setSize(0, 0);
9890                     wrap.setXY([b.right, b.bottom]);
9891                     st.left = st.top = "0";
9892                     a = {width: bw, height: bh, points: pt};
9893                 break;
9894                 case "tr":
9895                     wrap.setSize(0, 0);
9896                     wrap.setX(b.x+b.width);
9897                     st.left = st.bottom = "0";
9898                     a = {width: bw, height: bh, points: pt};
9899                 break;
9900             }
9901             this.dom.style.visibility = "visible";
9902             wrap.show();
9903
9904             arguments.callee.anim = wrap.fxanim(a,
9905                 o,
9906                 'motion',
9907                 .5,
9908                 'easeOut', after);
9909         });
9910         return this;
9911     },
9912     
9913         /**
9914          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9915          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9916          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9917          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9918          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9919          * Usage:
9920          *<pre><code>
9921 // default: slide the element out to the top
9922 el.slideOut();
9923
9924 // custom: slide the element out to the right with a 2-second duration
9925 el.slideOut('r', { duration: 2 });
9926
9927 // common config options shown with default values
9928 el.slideOut('t', {
9929     easing: 'easeOut',
9930     duration: .5,
9931     remove: false,
9932     useDisplay: false
9933 });
9934 </code></pre>
9935          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9936          * @param {Object} options (optional) Object literal with any of the Fx config options
9937          * @return {Roo.Element} The Element
9938          */
9939     slideOut : function(anchor, o){
9940         var el = this.getFxEl();
9941         o = o || {};
9942
9943         el.queueFx(o, function(){
9944
9945             anchor = anchor || "t";
9946
9947             // restore values after effect
9948             var r = this.getFxRestore();
9949             
9950             var b = this.getBox();
9951             // fixed size for slide
9952             this.setSize(b);
9953
9954             // wrap if needed
9955             var wrap = this.fxWrap(r.pos, o, "visible");
9956
9957             var st = this.dom.style;
9958             st.visibility = "visible";
9959             st.position = "absolute";
9960
9961             wrap.setSize(b);
9962
9963             var after = function(){
9964                 if(o.useDisplay){
9965                     el.setDisplayed(false);
9966                 }else{
9967                     el.hide();
9968                 }
9969
9970                 el.fxUnwrap(wrap, r.pos, o);
9971
9972                 st.width = r.width;
9973                 st.height = r.height;
9974
9975                 el.afterFx(o);
9976             };
9977
9978             var a, zero = {to: 0};
9979             switch(anchor.toLowerCase()){
9980                 case "t":
9981                     st.left = st.bottom = "0";
9982                     a = {height: zero};
9983                 break;
9984                 case "l":
9985                     st.right = st.top = "0";
9986                     a = {width: zero};
9987                 break;
9988                 case "r":
9989                     st.left = st.top = "0";
9990                     a = {width: zero, points: {to:[b.right, b.y]}};
9991                 break;
9992                 case "b":
9993                     st.left = st.top = "0";
9994                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9995                 break;
9996                 case "tl":
9997                     st.right = st.bottom = "0";
9998                     a = {width: zero, height: zero};
9999                 break;
10000                 case "bl":
10001                     st.right = st.top = "0";
10002                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10003                 break;
10004                 case "br":
10005                     st.left = st.top = "0";
10006                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10007                 break;
10008                 case "tr":
10009                     st.left = st.bottom = "0";
10010                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10011                 break;
10012             }
10013
10014             arguments.callee.anim = wrap.fxanim(a,
10015                 o,
10016                 'motion',
10017                 .5,
10018                 "easeOut", after);
10019         });
10020         return this;
10021     },
10022
10023         /**
10024          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10025          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10026          * The element must be removed from the DOM using the 'remove' config option if desired.
10027          * Usage:
10028          *<pre><code>
10029 // default
10030 el.puff();
10031
10032 // common config options shown with default values
10033 el.puff({
10034     easing: 'easeOut',
10035     duration: .5,
10036     remove: false,
10037     useDisplay: false
10038 });
10039 </code></pre>
10040          * @param {Object} options (optional) Object literal with any of the Fx config options
10041          * @return {Roo.Element} The Element
10042          */
10043     puff : function(o){
10044         var el = this.getFxEl();
10045         o = o || {};
10046
10047         el.queueFx(o, function(){
10048             this.clearOpacity();
10049             this.show();
10050
10051             // restore values after effect
10052             var r = this.getFxRestore();
10053             var st = this.dom.style;
10054
10055             var after = function(){
10056                 if(o.useDisplay){
10057                     el.setDisplayed(false);
10058                 }else{
10059                     el.hide();
10060                 }
10061
10062                 el.clearOpacity();
10063
10064                 el.setPositioning(r.pos);
10065                 st.width = r.width;
10066                 st.height = r.height;
10067                 st.fontSize = '';
10068                 el.afterFx(o);
10069             };
10070
10071             var width = this.getWidth();
10072             var height = this.getHeight();
10073
10074             arguments.callee.anim = this.fxanim({
10075                     width : {to: this.adjustWidth(width * 2)},
10076                     height : {to: this.adjustHeight(height * 2)},
10077                     points : {by: [-(width * .5), -(height * .5)]},
10078                     opacity : {to: 0},
10079                     fontSize: {to:200, unit: "%"}
10080                 },
10081                 o,
10082                 'motion',
10083                 .5,
10084                 "easeOut", after);
10085         });
10086         return this;
10087     },
10088
10089         /**
10090          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10091          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10092          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10093          * Usage:
10094          *<pre><code>
10095 // default
10096 el.switchOff();
10097
10098 // all config options shown with default values
10099 el.switchOff({
10100     easing: 'easeIn',
10101     duration: .3,
10102     remove: false,
10103     useDisplay: false
10104 });
10105 </code></pre>
10106          * @param {Object} options (optional) Object literal with any of the Fx config options
10107          * @return {Roo.Element} The Element
10108          */
10109     switchOff : function(o){
10110         var el = this.getFxEl();
10111         o = o || {};
10112
10113         el.queueFx(o, function(){
10114             this.clearOpacity();
10115             this.clip();
10116
10117             // restore values after effect
10118             var r = this.getFxRestore();
10119             var st = this.dom.style;
10120
10121             var after = function(){
10122                 if(o.useDisplay){
10123                     el.setDisplayed(false);
10124                 }else{
10125                     el.hide();
10126                 }
10127
10128                 el.clearOpacity();
10129                 el.setPositioning(r.pos);
10130                 st.width = r.width;
10131                 st.height = r.height;
10132
10133                 el.afterFx(o);
10134             };
10135
10136             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10137                 this.clearOpacity();
10138                 (function(){
10139                     this.fxanim({
10140                         height:{to:1},
10141                         points:{by:[0, this.getHeight() * .5]}
10142                     }, o, 'motion', 0.3, 'easeIn', after);
10143                 }).defer(100, this);
10144             });
10145         });
10146         return this;
10147     },
10148
10149     /**
10150      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10151      * changed using the "attr" config option) and then fading back to the original color. If no original
10152      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10153      * Usage:
10154 <pre><code>
10155 // default: highlight background to yellow
10156 el.highlight();
10157
10158 // custom: highlight foreground text to blue for 2 seconds
10159 el.highlight("0000ff", { attr: 'color', duration: 2 });
10160
10161 // common config options shown with default values
10162 el.highlight("ffff9c", {
10163     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10164     endColor: (current color) or "ffffff",
10165     easing: 'easeIn',
10166     duration: 1
10167 });
10168 </code></pre>
10169      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10170      * @param {Object} options (optional) Object literal with any of the Fx config options
10171      * @return {Roo.Element} The Element
10172      */ 
10173     highlight : function(color, o){
10174         var el = this.getFxEl();
10175         o = o || {};
10176
10177         el.queueFx(o, function(){
10178             color = color || "ffff9c";
10179             attr = o.attr || "backgroundColor";
10180
10181             this.clearOpacity();
10182             this.show();
10183
10184             var origColor = this.getColor(attr);
10185             var restoreColor = this.dom.style[attr];
10186             endColor = (o.endColor || origColor) || "ffffff";
10187
10188             var after = function(){
10189                 el.dom.style[attr] = restoreColor;
10190                 el.afterFx(o);
10191             };
10192
10193             var a = {};
10194             a[attr] = {from: color, to: endColor};
10195             arguments.callee.anim = this.fxanim(a,
10196                 o,
10197                 'color',
10198                 1,
10199                 'easeIn', after);
10200         });
10201         return this;
10202     },
10203
10204    /**
10205     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10206     * Usage:
10207 <pre><code>
10208 // default: a single light blue ripple
10209 el.frame();
10210
10211 // custom: 3 red ripples lasting 3 seconds total
10212 el.frame("ff0000", 3, { duration: 3 });
10213
10214 // common config options shown with default values
10215 el.frame("C3DAF9", 1, {
10216     duration: 1 //duration of entire animation (not each individual ripple)
10217     // Note: Easing is not configurable and will be ignored if included
10218 });
10219 </code></pre>
10220     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10221     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10222     * @param {Object} options (optional) Object literal with any of the Fx config options
10223     * @return {Roo.Element} The Element
10224     */
10225     frame : function(color, count, o){
10226         var el = this.getFxEl();
10227         o = o || {};
10228
10229         el.queueFx(o, function(){
10230             color = color || "#C3DAF9";
10231             if(color.length == 6){
10232                 color = "#" + color;
10233             }
10234             count = count || 1;
10235             duration = o.duration || 1;
10236             this.show();
10237
10238             var b = this.getBox();
10239             var animFn = function(){
10240                 var proxy = this.createProxy({
10241
10242                      style:{
10243                         visbility:"hidden",
10244                         position:"absolute",
10245                         "z-index":"35000", // yee haw
10246                         border:"0px solid " + color
10247                      }
10248                   });
10249                 var scale = Roo.isBorderBox ? 2 : 1;
10250                 proxy.animate({
10251                     top:{from:b.y, to:b.y - 20},
10252                     left:{from:b.x, to:b.x - 20},
10253                     borderWidth:{from:0, to:10},
10254                     opacity:{from:1, to:0},
10255                     height:{from:b.height, to:(b.height + (20*scale))},
10256                     width:{from:b.width, to:(b.width + (20*scale))}
10257                 }, duration, function(){
10258                     proxy.remove();
10259                 });
10260                 if(--count > 0){
10261                      animFn.defer((duration/2)*1000, this);
10262                 }else{
10263                     el.afterFx(o);
10264                 }
10265             };
10266             animFn.call(this);
10267         });
10268         return this;
10269     },
10270
10271    /**
10272     * Creates a pause before any subsequent queued effects begin.  If there are
10273     * no effects queued after the pause it will have no effect.
10274     * Usage:
10275 <pre><code>
10276 el.pause(1);
10277 </code></pre>
10278     * @param {Number} seconds The length of time to pause (in seconds)
10279     * @return {Roo.Element} The Element
10280     */
10281     pause : function(seconds){
10282         var el = this.getFxEl();
10283         var o = {};
10284
10285         el.queueFx(o, function(){
10286             setTimeout(function(){
10287                 el.afterFx(o);
10288             }, seconds * 1000);
10289         });
10290         return this;
10291     },
10292
10293    /**
10294     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10295     * using the "endOpacity" config option.
10296     * Usage:
10297 <pre><code>
10298 // default: fade in from opacity 0 to 100%
10299 el.fadeIn();
10300
10301 // custom: fade in from opacity 0 to 75% over 2 seconds
10302 el.fadeIn({ endOpacity: .75, duration: 2});
10303
10304 // common config options shown with default values
10305 el.fadeIn({
10306     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10307     easing: 'easeOut',
10308     duration: .5
10309 });
10310 </code></pre>
10311     * @param {Object} options (optional) Object literal with any of the Fx config options
10312     * @return {Roo.Element} The Element
10313     */
10314     fadeIn : function(o){
10315         var el = this.getFxEl();
10316         o = o || {};
10317         el.queueFx(o, function(){
10318             this.setOpacity(0);
10319             this.fixDisplay();
10320             this.dom.style.visibility = 'visible';
10321             var to = o.endOpacity || 1;
10322             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10323                 o, null, .5, "easeOut", function(){
10324                 if(to == 1){
10325                     this.clearOpacity();
10326                 }
10327                 el.afterFx(o);
10328             });
10329         });
10330         return this;
10331     },
10332
10333    /**
10334     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10335     * using the "endOpacity" config option.
10336     * Usage:
10337 <pre><code>
10338 // default: fade out from the element's current opacity to 0
10339 el.fadeOut();
10340
10341 // custom: fade out from the element's current opacity to 25% over 2 seconds
10342 el.fadeOut({ endOpacity: .25, duration: 2});
10343
10344 // common config options shown with default values
10345 el.fadeOut({
10346     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10347     easing: 'easeOut',
10348     duration: .5
10349     remove: false,
10350     useDisplay: false
10351 });
10352 </code></pre>
10353     * @param {Object} options (optional) Object literal with any of the Fx config options
10354     * @return {Roo.Element} The Element
10355     */
10356     fadeOut : function(o){
10357         var el = this.getFxEl();
10358         o = o || {};
10359         el.queueFx(o, function(){
10360             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10361                 o, null, .5, "easeOut", function(){
10362                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10363                      this.dom.style.display = "none";
10364                 }else{
10365                      this.dom.style.visibility = "hidden";
10366                 }
10367                 this.clearOpacity();
10368                 el.afterFx(o);
10369             });
10370         });
10371         return this;
10372     },
10373
10374    /**
10375     * Animates the transition of an element's dimensions from a starting height/width
10376     * to an ending height/width.
10377     * Usage:
10378 <pre><code>
10379 // change height and width to 100x100 pixels
10380 el.scale(100, 100);
10381
10382 // common config options shown with default values.  The height and width will default to
10383 // the element's existing values if passed as null.
10384 el.scale(
10385     [element's width],
10386     [element's height], {
10387     easing: 'easeOut',
10388     duration: .35
10389 });
10390 </code></pre>
10391     * @param {Number} width  The new width (pass undefined to keep the original width)
10392     * @param {Number} height  The new height (pass undefined to keep the original height)
10393     * @param {Object} options (optional) Object literal with any of the Fx config options
10394     * @return {Roo.Element} The Element
10395     */
10396     scale : function(w, h, o){
10397         this.shift(Roo.apply({}, o, {
10398             width: w,
10399             height: h
10400         }));
10401         return this;
10402     },
10403
10404    /**
10405     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10406     * Any of these properties not specified in the config object will not be changed.  This effect 
10407     * requires that at least one new dimension, position or opacity setting must be passed in on
10408     * the config object in order for the function to have any effect.
10409     * Usage:
10410 <pre><code>
10411 // slide the element horizontally to x position 200 while changing the height and opacity
10412 el.shift({ x: 200, height: 50, opacity: .8 });
10413
10414 // common config options shown with default values.
10415 el.shift({
10416     width: [element's width],
10417     height: [element's height],
10418     x: [element's x position],
10419     y: [element's y position],
10420     opacity: [element's opacity],
10421     easing: 'easeOut',
10422     duration: .35
10423 });
10424 </code></pre>
10425     * @param {Object} options  Object literal with any of the Fx config options
10426     * @return {Roo.Element} The Element
10427     */
10428     shift : function(o){
10429         var el = this.getFxEl();
10430         o = o || {};
10431         el.queueFx(o, function(){
10432             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10433             if(w !== undefined){
10434                 a.width = {to: this.adjustWidth(w)};
10435             }
10436             if(h !== undefined){
10437                 a.height = {to: this.adjustHeight(h)};
10438             }
10439             if(x !== undefined || y !== undefined){
10440                 a.points = {to: [
10441                     x !== undefined ? x : this.getX(),
10442                     y !== undefined ? y : this.getY()
10443                 ]};
10444             }
10445             if(op !== undefined){
10446                 a.opacity = {to: op};
10447             }
10448             if(o.xy !== undefined){
10449                 a.points = {to: o.xy};
10450             }
10451             arguments.callee.anim = this.fxanim(a,
10452                 o, 'motion', .35, "easeOut", function(){
10453                 el.afterFx(o);
10454             });
10455         });
10456         return this;
10457     },
10458
10459         /**
10460          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10461          * ending point of the effect.
10462          * Usage:
10463          *<pre><code>
10464 // default: slide the element downward while fading out
10465 el.ghost();
10466
10467 // custom: slide the element out to the right with a 2-second duration
10468 el.ghost('r', { duration: 2 });
10469
10470 // common config options shown with default values
10471 el.ghost('b', {
10472     easing: 'easeOut',
10473     duration: .5
10474     remove: false,
10475     useDisplay: false
10476 });
10477 </code></pre>
10478          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10479          * @param {Object} options (optional) Object literal with any of the Fx config options
10480          * @return {Roo.Element} The Element
10481          */
10482     ghost : function(anchor, o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485
10486         el.queueFx(o, function(){
10487             anchor = anchor || "b";
10488
10489             // restore values after effect
10490             var r = this.getFxRestore();
10491             var w = this.getWidth(),
10492                 h = this.getHeight();
10493
10494             var st = this.dom.style;
10495
10496             var after = function(){
10497                 if(o.useDisplay){
10498                     el.setDisplayed(false);
10499                 }else{
10500                     el.hide();
10501                 }
10502
10503                 el.clearOpacity();
10504                 el.setPositioning(r.pos);
10505                 st.width = r.width;
10506                 st.height = r.height;
10507
10508                 el.afterFx(o);
10509             };
10510
10511             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10512             switch(anchor.toLowerCase()){
10513                 case "t":
10514                     pt.by = [0, -h];
10515                 break;
10516                 case "l":
10517                     pt.by = [-w, 0];
10518                 break;
10519                 case "r":
10520                     pt.by = [w, 0];
10521                 break;
10522                 case "b":
10523                     pt.by = [0, h];
10524                 break;
10525                 case "tl":
10526                     pt.by = [-w, -h];
10527                 break;
10528                 case "bl":
10529                     pt.by = [-w, h];
10530                 break;
10531                 case "br":
10532                     pt.by = [w, h];
10533                 break;
10534                 case "tr":
10535                     pt.by = [w, -h];
10536                 break;
10537             }
10538
10539             arguments.callee.anim = this.fxanim(a,
10540                 o,
10541                 'motion',
10542                 .5,
10543                 "easeOut", after);
10544         });
10545         return this;
10546     },
10547
10548         /**
10549          * Ensures that all effects queued after syncFx is called on the element are
10550          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10551          * @return {Roo.Element} The Element
10552          */
10553     syncFx : function(){
10554         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10555             block : false,
10556             concurrent : true,
10557             stopFx : false
10558         });
10559         return this;
10560     },
10561
10562         /**
10563          * Ensures that all effects queued after sequenceFx is called on the element are
10564          * run in sequence.  This is the opposite of {@link #syncFx}.
10565          * @return {Roo.Element} The Element
10566          */
10567     sequenceFx : function(){
10568         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10569             block : false,
10570             concurrent : false,
10571             stopFx : false
10572         });
10573         return this;
10574     },
10575
10576         /* @private */
10577     nextFx : function(){
10578         var ef = this.fxQueue[0];
10579         if(ef){
10580             ef.call(this);
10581         }
10582     },
10583
10584         /**
10585          * Returns true if the element has any effects actively running or queued, else returns false.
10586          * @return {Boolean} True if element has active effects, else false
10587          */
10588     hasActiveFx : function(){
10589         return this.fxQueue && this.fxQueue[0];
10590     },
10591
10592         /**
10593          * Stops any running effects and clears the element's internal effects queue if it contains
10594          * any additional effects that haven't started yet.
10595          * @return {Roo.Element} The Element
10596          */
10597     stopFx : function(){
10598         if(this.hasActiveFx()){
10599             var cur = this.fxQueue[0];
10600             if(cur && cur.anim && cur.anim.isAnimated()){
10601                 this.fxQueue = [cur]; // clear out others
10602                 cur.anim.stop(true);
10603             }
10604         }
10605         return this;
10606     },
10607
10608         /* @private */
10609     beforeFx : function(o){
10610         if(this.hasActiveFx() && !o.concurrent){
10611            if(o.stopFx){
10612                this.stopFx();
10613                return true;
10614            }
10615            return false;
10616         }
10617         return true;
10618     },
10619
10620         /**
10621          * Returns true if the element is currently blocking so that no other effect can be queued
10622          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10623          * used to ensure that an effect initiated by a user action runs to completion prior to the
10624          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10625          * @return {Boolean} True if blocking, else false
10626          */
10627     hasFxBlock : function(){
10628         var q = this.fxQueue;
10629         return q && q[0] && q[0].block;
10630     },
10631
10632         /* @private */
10633     queueFx : function(o, fn){
10634         if(!this.fxQueue){
10635             this.fxQueue = [];
10636         }
10637         if(!this.hasFxBlock()){
10638             Roo.applyIf(o, this.fxDefaults);
10639             if(!o.concurrent){
10640                 var run = this.beforeFx(o);
10641                 fn.block = o.block;
10642                 this.fxQueue.push(fn);
10643                 if(run){
10644                     this.nextFx();
10645                 }
10646             }else{
10647                 fn.call(this);
10648             }
10649         }
10650         return this;
10651     },
10652
10653         /* @private */
10654     fxWrap : function(pos, o, vis){
10655         var wrap;
10656         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10657             var wrapXY;
10658             if(o.fixPosition){
10659                 wrapXY = this.getXY();
10660             }
10661             var div = document.createElement("div");
10662             div.style.visibility = vis;
10663             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10664             wrap.setPositioning(pos);
10665             if(wrap.getStyle("position") == "static"){
10666                 wrap.position("relative");
10667             }
10668             this.clearPositioning('auto');
10669             wrap.clip();
10670             wrap.dom.appendChild(this.dom);
10671             if(wrapXY){
10672                 wrap.setXY(wrapXY);
10673             }
10674         }
10675         return wrap;
10676     },
10677
10678         /* @private */
10679     fxUnwrap : function(wrap, pos, o){
10680         this.clearPositioning();
10681         this.setPositioning(pos);
10682         if(!o.wrap){
10683             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10684             wrap.remove();
10685         }
10686     },
10687
10688         /* @private */
10689     getFxRestore : function(){
10690         var st = this.dom.style;
10691         return {pos: this.getPositioning(), width: st.width, height : st.height};
10692     },
10693
10694         /* @private */
10695     afterFx : function(o){
10696         if(o.afterStyle){
10697             this.applyStyles(o.afterStyle);
10698         }
10699         if(o.afterCls){
10700             this.addClass(o.afterCls);
10701         }
10702         if(o.remove === true){
10703             this.remove();
10704         }
10705         Roo.callback(o.callback, o.scope, [this]);
10706         if(!o.concurrent){
10707             this.fxQueue.shift();
10708             this.nextFx();
10709         }
10710     },
10711
10712         /* @private */
10713     getFxEl : function(){ // support for composite element fx
10714         return Roo.get(this.dom);
10715     },
10716
10717         /* @private */
10718     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10719         animType = animType || 'run';
10720         opt = opt || {};
10721         var anim = Roo.lib.Anim[animType](
10722             this.dom, args,
10723             (opt.duration || defaultDur) || .35,
10724             (opt.easing || defaultEase) || 'easeOut',
10725             function(){
10726                 Roo.callback(cb, this);
10727             },
10728             this
10729         );
10730         opt.anim = anim;
10731         return anim;
10732     }
10733 };
10734
10735 // backwords compat
10736 Roo.Fx.resize = Roo.Fx.scale;
10737
10738 //When included, Roo.Fx is automatically applied to Element so that all basic
10739 //effects are available directly via the Element API
10740 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10741  * Based on:
10742  * Ext JS Library 1.1.1
10743  * Copyright(c) 2006-2007, Ext JS, LLC.
10744  *
10745  * Originally Released Under LGPL - original licence link has changed is not relivant.
10746  *
10747  * Fork - LGPL
10748  * <script type="text/javascript">
10749  */
10750
10751
10752 /**
10753  * @class Roo.CompositeElement
10754  * Standard composite class. Creates a Roo.Element for every element in the collection.
10755  * <br><br>
10756  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10757  * actions will be performed on all the elements in this collection.</b>
10758  * <br><br>
10759  * All methods return <i>this</i> and can be chained.
10760  <pre><code>
10761  var els = Roo.select("#some-el div.some-class", true);
10762  // or select directly from an existing element
10763  var el = Roo.get('some-el');
10764  el.select('div.some-class', true);
10765
10766  els.setWidth(100); // all elements become 100 width
10767  els.hide(true); // all elements fade out and hide
10768  // or
10769  els.setWidth(100).hide(true);
10770  </code></pre>
10771  */
10772 Roo.CompositeElement = function(els){
10773     this.elements = [];
10774     this.addElements(els);
10775 };
10776 Roo.CompositeElement.prototype = {
10777     isComposite: true,
10778     addElements : function(els){
10779         if(!els) return this;
10780         if(typeof els == "string"){
10781             els = Roo.Element.selectorFunction(els);
10782         }
10783         var yels = this.elements;
10784         var index = yels.length-1;
10785         for(var i = 0, len = els.length; i < len; i++) {
10786                 yels[++index] = Roo.get(els[i]);
10787         }
10788         return this;
10789     },
10790
10791     /**
10792     * Clears this composite and adds the elements returned by the passed selector.
10793     * @param {String/Array} els A string CSS selector, an array of elements or an element
10794     * @return {CompositeElement} this
10795     */
10796     fill : function(els){
10797         this.elements = [];
10798         this.add(els);
10799         return this;
10800     },
10801
10802     /**
10803     * Filters this composite to only elements that match the passed selector.
10804     * @param {String} selector A string CSS selector
10805     * @return {CompositeElement} this
10806     */
10807     filter : function(selector){
10808         var els = [];
10809         this.each(function(el){
10810             if(el.is(selector)){
10811                 els[els.length] = el.dom;
10812             }
10813         });
10814         this.fill(els);
10815         return this;
10816     },
10817
10818     invoke : function(fn, args){
10819         var els = this.elements;
10820         for(var i = 0, len = els.length; i < len; i++) {
10821                 Roo.Element.prototype[fn].apply(els[i], args);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Adds elements to this composite.
10827     * @param {String/Array} els A string CSS selector, an array of elements or an element
10828     * @return {CompositeElement} this
10829     */
10830     add : function(els){
10831         if(typeof els == "string"){
10832             this.addElements(Roo.Element.selectorFunction(els));
10833         }else if(els.length !== undefined){
10834             this.addElements(els);
10835         }else{
10836             this.addElements([els]);
10837         }
10838         return this;
10839     },
10840     /**
10841     * Calls the passed function passing (el, this, index) for each element in this composite.
10842     * @param {Function} fn The function to call
10843     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10844     * @return {CompositeElement} this
10845     */
10846     each : function(fn, scope){
10847         var els = this.elements;
10848         for(var i = 0, len = els.length; i < len; i++){
10849             if(fn.call(scope || els[i], els[i], this, i) === false) {
10850                 break;
10851             }
10852         }
10853         return this;
10854     },
10855
10856     /**
10857      * Returns the Element object at the specified index
10858      * @param {Number} index
10859      * @return {Roo.Element}
10860      */
10861     item : function(index){
10862         return this.elements[index] || null;
10863     },
10864
10865     /**
10866      * Returns the first Element
10867      * @return {Roo.Element}
10868      */
10869     first : function(){
10870         return this.item(0);
10871     },
10872
10873     /**
10874      * Returns the last Element
10875      * @return {Roo.Element}
10876      */
10877     last : function(){
10878         return this.item(this.elements.length-1);
10879     },
10880
10881     /**
10882      * Returns the number of elements in this composite
10883      * @return Number
10884      */
10885     getCount : function(){
10886         return this.elements.length;
10887     },
10888
10889     /**
10890      * Returns true if this composite contains the passed element
10891      * @return Boolean
10892      */
10893     contains : function(el){
10894         return this.indexOf(el) !== -1;
10895     },
10896
10897     /**
10898      * Returns true if this composite contains the passed element
10899      * @return Boolean
10900      */
10901     indexOf : function(el){
10902         return this.elements.indexOf(Roo.get(el));
10903     },
10904
10905
10906     /**
10907     * Removes the specified element(s).
10908     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10909     * or an array of any of those.
10910     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10911     * @return {CompositeElement} this
10912     */
10913     removeElement : function(el, removeDom){
10914         if(el instanceof Array){
10915             for(var i = 0, len = el.length; i < len; i++){
10916                 this.removeElement(el[i]);
10917             }
10918             return this;
10919         }
10920         var index = typeof el == 'number' ? el : this.indexOf(el);
10921         if(index !== -1){
10922             if(removeDom){
10923                 var d = this.elements[index];
10924                 if(d.dom){
10925                     d.remove();
10926                 }else{
10927                     d.parentNode.removeChild(d);
10928                 }
10929             }
10930             this.elements.splice(index, 1);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Replaces the specified element with the passed element.
10937     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10938     * to replace.
10939     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10940     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10941     * @return {CompositeElement} this
10942     */
10943     replaceElement : function(el, replacement, domReplace){
10944         var index = typeof el == 'number' ? el : this.indexOf(el);
10945         if(index !== -1){
10946             if(domReplace){
10947                 this.elements[index].replaceWith(replacement);
10948             }else{
10949                 this.elements.splice(index, 1, Roo.get(replacement))
10950             }
10951         }
10952         return this;
10953     },
10954
10955     /**
10956      * Removes all elements.
10957      */
10958     clear : function(){
10959         this.elements = [];
10960     }
10961 };
10962 (function(){
10963     Roo.CompositeElement.createCall = function(proto, fnName){
10964         if(!proto[fnName]){
10965             proto[fnName] = function(){
10966                 return this.invoke(fnName, arguments);
10967             };
10968         }
10969     };
10970     for(var fnName in Roo.Element.prototype){
10971         if(typeof Roo.Element.prototype[fnName] == "function"){
10972             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10973         }
10974     };
10975 })();
10976 /*
10977  * Based on:
10978  * Ext JS Library 1.1.1
10979  * Copyright(c) 2006-2007, Ext JS, LLC.
10980  *
10981  * Originally Released Under LGPL - original licence link has changed is not relivant.
10982  *
10983  * Fork - LGPL
10984  * <script type="text/javascript">
10985  */
10986
10987 /**
10988  * @class Roo.CompositeElementLite
10989  * @extends Roo.CompositeElement
10990  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10991  <pre><code>
10992  var els = Roo.select("#some-el div.some-class");
10993  // or select directly from an existing element
10994  var el = Roo.get('some-el');
10995  el.select('div.some-class');
10996
10997  els.setWidth(100); // all elements become 100 width
10998  els.hide(true); // all elements fade out and hide
10999  // or
11000  els.setWidth(100).hide(true);
11001  </code></pre><br><br>
11002  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11003  * actions will be performed on all the elements in this collection.</b>
11004  */
11005 Roo.CompositeElementLite = function(els){
11006     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11007     this.el = new Roo.Element.Flyweight();
11008 };
11009 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11010     addElements : function(els){
11011         if(els){
11012             if(els instanceof Array){
11013                 this.elements = this.elements.concat(els);
11014             }else{
11015                 var yels = this.elements;
11016                 var index = yels.length-1;
11017                 for(var i = 0, len = els.length; i < len; i++) {
11018                     yels[++index] = els[i];
11019                 }
11020             }
11021         }
11022         return this;
11023     },
11024     invoke : function(fn, args){
11025         var els = this.elements;
11026         var el = this.el;
11027         for(var i = 0, len = els.length; i < len; i++) {
11028             el.dom = els[i];
11029                 Roo.Element.prototype[fn].apply(el, args);
11030         }
11031         return this;
11032     },
11033     /**
11034      * Returns a flyweight Element of the dom element object at the specified index
11035      * @param {Number} index
11036      * @return {Roo.Element}
11037      */
11038     item : function(index){
11039         if(!this.elements[index]){
11040             return null;
11041         }
11042         this.el.dom = this.elements[index];
11043         return this.el;
11044     },
11045
11046     // fixes scope with flyweight
11047     addListener : function(eventName, handler, scope, opt){
11048         var els = this.elements;
11049         for(var i = 0, len = els.length; i < len; i++) {
11050             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11051         }
11052         return this;
11053     },
11054
11055     /**
11056     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11057     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11058     * a reference to the dom node, use el.dom.</b>
11059     * @param {Function} fn The function to call
11060     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11061     * @return {CompositeElement} this
11062     */
11063     each : function(fn, scope){
11064         var els = this.elements;
11065         var el = this.el;
11066         for(var i = 0, len = els.length; i < len; i++){
11067             el.dom = els[i];
11068                 if(fn.call(scope || el, el, this, i) === false){
11069                 break;
11070             }
11071         }
11072         return this;
11073     },
11074
11075     indexOf : function(el){
11076         return this.elements.indexOf(Roo.getDom(el));
11077     },
11078
11079     replaceElement : function(el, replacement, domReplace){
11080         var index = typeof el == 'number' ? el : this.indexOf(el);
11081         if(index !== -1){
11082             replacement = Roo.getDom(replacement);
11083             if(domReplace){
11084                 var d = this.elements[index];
11085                 d.parentNode.insertBefore(replacement, d);
11086                 d.parentNode.removeChild(d);
11087             }
11088             this.elements.splice(index, 1, replacement);
11089         }
11090         return this;
11091     }
11092 });
11093 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11094
11095 /*
11096  * Based on:
11097  * Ext JS Library 1.1.1
11098  * Copyright(c) 2006-2007, Ext JS, LLC.
11099  *
11100  * Originally Released Under LGPL - original licence link has changed is not relivant.
11101  *
11102  * Fork - LGPL
11103  * <script type="text/javascript">
11104  */
11105
11106  
11107
11108 /**
11109  * @class Roo.data.Connection
11110  * @extends Roo.util.Observable
11111  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11112  * either to a configured URL, or to a URL specified at request time.<br><br>
11113  * <p>
11114  * Requests made by this class are asynchronous, and will return immediately. No data from
11115  * the server will be available to the statement immediately following the {@link #request} call.
11116  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11117  * <p>
11118  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11119  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11120  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11121  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11122  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11123  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11124  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11125  * standard DOM methods.
11126  * @constructor
11127  * @param {Object} config a configuration object.
11128  */
11129 Roo.data.Connection = function(config){
11130     Roo.apply(this, config);
11131     this.addEvents({
11132         /**
11133          * @event beforerequest
11134          * Fires before a network request is made to retrieve a data object.
11135          * @param {Connection} conn This Connection object.
11136          * @param {Object} options The options config object passed to the {@link #request} method.
11137          */
11138         "beforerequest" : true,
11139         /**
11140          * @event requestcomplete
11141          * Fires if the request was successfully completed.
11142          * @param {Connection} conn This Connection object.
11143          * @param {Object} response The XHR object containing the response data.
11144          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11145          * @param {Object} options The options config object passed to the {@link #request} method.
11146          */
11147         "requestcomplete" : true,
11148         /**
11149          * @event requestexception
11150          * Fires if an error HTTP status was returned from the server.
11151          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11152          * @param {Connection} conn This Connection object.
11153          * @param {Object} response The XHR object containing the response data.
11154          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11155          * @param {Object} options The options config object passed to the {@link #request} method.
11156          */
11157         "requestexception" : true
11158     });
11159     Roo.data.Connection.superclass.constructor.call(this);
11160 };
11161
11162 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11163     /**
11164      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11165      */
11166     /**
11167      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11168      * extra parameters to each request made by this object. (defaults to undefined)
11169      */
11170     /**
11171      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11172      *  to each request made by this object. (defaults to undefined)
11173      */
11174     /**
11175      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11176      */
11177     /**
11178      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11179      */
11180     timeout : 30000,
11181     /**
11182      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11183      * @type Boolean
11184      */
11185     autoAbort:false,
11186
11187     /**
11188      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11189      * @type Boolean
11190      */
11191     disableCaching: true,
11192
11193     /**
11194      * Sends an HTTP request to a remote server.
11195      * @param {Object} options An object which may contain the following properties:<ul>
11196      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11197      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11198      * request, a url encoded string or a function to call to get either.</li>
11199      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11200      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11201      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11202      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11203      * <li>options {Object} The parameter to the request call.</li>
11204      * <li>success {Boolean} True if the request succeeded.</li>
11205      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11206      * </ul></li>
11207      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11208      * The callback is passed the following parameters:<ul>
11209      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11210      * <li>options {Object} The parameter to the request call.</li>
11211      * </ul></li>
11212      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11213      * The callback is passed the following parameters:<ul>
11214      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11215      * <li>options {Object} The parameter to the request call.</li>
11216      * </ul></li>
11217      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11218      * for the callback function. Defaults to the browser window.</li>
11219      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11220      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11221      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11222      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11223      * params for the post data. Any params will be appended to the URL.</li>
11224      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11225      * </ul>
11226      * @return {Number} transactionId
11227      */
11228     request : function(o){
11229         if(this.fireEvent("beforerequest", this, o) !== false){
11230             var p = o.params;
11231
11232             if(typeof p == "function"){
11233                 p = p.call(o.scope||window, o);
11234             }
11235             if(typeof p == "object"){
11236                 p = Roo.urlEncode(o.params);
11237             }
11238             if(this.extraParams){
11239                 var extras = Roo.urlEncode(this.extraParams);
11240                 p = p ? (p + '&' + extras) : extras;
11241             }
11242
11243             var url = o.url || this.url;
11244             if(typeof url == 'function'){
11245                 url = url.call(o.scope||window, o);
11246             }
11247
11248             if(o.form){
11249                 var form = Roo.getDom(o.form);
11250                 url = url || form.action;
11251
11252                 var enctype = form.getAttribute("enctype");
11253                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11254                     return this.doFormUpload(o, p, url);
11255                 }
11256                 var f = Roo.lib.Ajax.serializeForm(form);
11257                 p = p ? (p + '&' + f) : f;
11258             }
11259
11260             var hs = o.headers;
11261             if(this.defaultHeaders){
11262                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11263                 if(!o.headers){
11264                     o.headers = hs;
11265                 }
11266             }
11267
11268             var cb = {
11269                 success: this.handleResponse,
11270                 failure: this.handleFailure,
11271                 scope: this,
11272                 argument: {options: o},
11273                 timeout : this.timeout
11274             };
11275
11276             var method = o.method||this.method||(p ? "POST" : "GET");
11277
11278             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11279                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11280             }
11281
11282             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11283                 if(o.autoAbort){
11284                     this.abort();
11285                 }
11286             }else if(this.autoAbort !== false){
11287                 this.abort();
11288             }
11289
11290             if((method == 'GET' && p) || o.xmlData){
11291                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11292                 p = '';
11293             }
11294             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11295             return this.transId;
11296         }else{
11297             Roo.callback(o.callback, o.scope, [o, null, null]);
11298             return null;
11299         }
11300     },
11301
11302     /**
11303      * Determine whether this object has a request outstanding.
11304      * @param {Number} transactionId (Optional) defaults to the last transaction
11305      * @return {Boolean} True if there is an outstanding request.
11306      */
11307     isLoading : function(transId){
11308         if(transId){
11309             return Roo.lib.Ajax.isCallInProgress(transId);
11310         }else{
11311             return this.transId ? true : false;
11312         }
11313     },
11314
11315     /**
11316      * Aborts any outstanding request.
11317      * @param {Number} transactionId (Optional) defaults to the last transaction
11318      */
11319     abort : function(transId){
11320         if(transId || this.isLoading()){
11321             Roo.lib.Ajax.abort(transId || this.transId);
11322         }
11323     },
11324
11325     // private
11326     handleResponse : function(response){
11327         this.transId = false;
11328         var options = response.argument.options;
11329         response.argument = options ? options.argument : null;
11330         this.fireEvent("requestcomplete", this, response, options);
11331         Roo.callback(options.success, options.scope, [response, options]);
11332         Roo.callback(options.callback, options.scope, [options, true, response]);
11333     },
11334
11335     // private
11336     handleFailure : function(response, e){
11337         this.transId = false;
11338         var options = response.argument.options;
11339         response.argument = options ? options.argument : null;
11340         this.fireEvent("requestexception", this, response, options, e);
11341         Roo.callback(options.failure, options.scope, [response, options]);
11342         Roo.callback(options.callback, options.scope, [options, false, response]);
11343     },
11344
11345     // private
11346     doFormUpload : function(o, ps, url){
11347         var id = Roo.id();
11348         var frame = document.createElement('iframe');
11349         frame.id = id;
11350         frame.name = id;
11351         frame.className = 'x-hidden';
11352         if(Roo.isIE){
11353             frame.src = Roo.SSL_SECURE_URL;
11354         }
11355         document.body.appendChild(frame);
11356
11357         if(Roo.isIE){
11358            document.frames[id].name = id;
11359         }
11360
11361         var form = Roo.getDom(o.form);
11362         form.target = id;
11363         form.method = 'POST';
11364         form.enctype = form.encoding = 'multipart/form-data';
11365         if(url){
11366             form.action = url;
11367         }
11368
11369         var hiddens, hd;
11370         if(ps){ // add dynamic params
11371             hiddens = [];
11372             ps = Roo.urlDecode(ps, false);
11373             for(var k in ps){
11374                 if(ps.hasOwnProperty(k)){
11375                     hd = document.createElement('input');
11376                     hd.type = 'hidden';
11377                     hd.name = k;
11378                     hd.value = ps[k];
11379                     form.appendChild(hd);
11380                     hiddens.push(hd);
11381                 }
11382             }
11383         }
11384
11385         function cb(){
11386             var r = {  // bogus response object
11387                 responseText : '',
11388                 responseXML : null
11389             };
11390
11391             r.argument = o ? o.argument : null;
11392
11393             try { //
11394                 var doc;
11395                 if(Roo.isIE){
11396                     doc = frame.contentWindow.document;
11397                 }else {
11398                     doc = (frame.contentDocument || window.frames[id].document);
11399                 }
11400                 if(doc && doc.body){
11401                     r.responseText = doc.body.innerHTML;
11402                 }
11403                 if(doc && doc.XMLDocument){
11404                     r.responseXML = doc.XMLDocument;
11405                 }else {
11406                     r.responseXML = doc;
11407                 }
11408             }
11409             catch(e) {
11410                 // ignore
11411             }
11412
11413             Roo.EventManager.removeListener(frame, 'load', cb, this);
11414
11415             this.fireEvent("requestcomplete", this, r, o);
11416             Roo.callback(o.success, o.scope, [r, o]);
11417             Roo.callback(o.callback, o.scope, [o, true, r]);
11418
11419             setTimeout(function(){document.body.removeChild(frame);}, 100);
11420         }
11421
11422         Roo.EventManager.on(frame, 'load', cb, this);
11423         form.submit();
11424
11425         if(hiddens){ // remove dynamic params
11426             for(var i = 0, len = hiddens.length; i < len; i++){
11427                 form.removeChild(hiddens[i]);
11428             }
11429         }
11430     }
11431 });
11432
11433 /**
11434  * @class Roo.Ajax
11435  * @extends Roo.data.Connection
11436  * Global Ajax request class.
11437  *
11438  * @singleton
11439  */
11440 Roo.Ajax = new Roo.data.Connection({
11441     // fix up the docs
11442    /**
11443      * @cfg {String} url @hide
11444      */
11445     /**
11446      * @cfg {Object} extraParams @hide
11447      */
11448     /**
11449      * @cfg {Object} defaultHeaders @hide
11450      */
11451     /**
11452      * @cfg {String} method (Optional) @hide
11453      */
11454     /**
11455      * @cfg {Number} timeout (Optional) @hide
11456      */
11457     /**
11458      * @cfg {Boolean} autoAbort (Optional) @hide
11459      */
11460
11461     /**
11462      * @cfg {Boolean} disableCaching (Optional) @hide
11463      */
11464
11465     /**
11466      * @property  disableCaching
11467      * True to add a unique cache-buster param to GET requests. (defaults to true)
11468      * @type Boolean
11469      */
11470     /**
11471      * @property  url
11472      * The default URL to be used for requests to the server. (defaults to undefined)
11473      * @type String
11474      */
11475     /**
11476      * @property  extraParams
11477      * An object containing properties which are used as
11478      * extra parameters to each request made by this object. (defaults to undefined)
11479      * @type Object
11480      */
11481     /**
11482      * @property  defaultHeaders
11483      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11484      * @type Object
11485      */
11486     /**
11487      * @property  method
11488      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11489      * @type String
11490      */
11491     /**
11492      * @property  timeout
11493      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11494      * @type Number
11495      */
11496
11497     /**
11498      * @property  autoAbort
11499      * Whether a new request should abort any pending requests. (defaults to false)
11500      * @type Boolean
11501      */
11502     autoAbort : false,
11503
11504     /**
11505      * Serialize the passed form into a url encoded string
11506      * @param {String/HTMLElement} form
11507      * @return {String}
11508      */
11509     serializeForm : function(form){
11510         return Roo.lib.Ajax.serializeForm(form);
11511     }
11512 });/*
11513  * Based on:
11514  * Ext JS Library 1.1.1
11515  * Copyright(c) 2006-2007, Ext JS, LLC.
11516  *
11517  * Originally Released Under LGPL - original licence link has changed is not relivant.
11518  *
11519  * Fork - LGPL
11520  * <script type="text/javascript">
11521  */
11522  
11523 /**
11524  * @class Roo.Ajax
11525  * @extends Roo.data.Connection
11526  * Global Ajax request class.
11527  *
11528  * @instanceOf  Roo.data.Connection
11529  */
11530 Roo.Ajax = new Roo.data.Connection({
11531     // fix up the docs
11532     
11533     /**
11534      * fix up scoping
11535      * @scope Roo.Ajax
11536      */
11537     
11538    /**
11539      * @cfg {String} url @hide
11540      */
11541     /**
11542      * @cfg {Object} extraParams @hide
11543      */
11544     /**
11545      * @cfg {Object} defaultHeaders @hide
11546      */
11547     /**
11548      * @cfg {String} method (Optional) @hide
11549      */
11550     /**
11551      * @cfg {Number} timeout (Optional) @hide
11552      */
11553     /**
11554      * @cfg {Boolean} autoAbort (Optional) @hide
11555      */
11556
11557     /**
11558      * @cfg {Boolean} disableCaching (Optional) @hide
11559      */
11560
11561     /**
11562      * @property  disableCaching
11563      * True to add a unique cache-buster param to GET requests. (defaults to true)
11564      * @type Boolean
11565      */
11566     /**
11567      * @property  url
11568      * The default URL to be used for requests to the server. (defaults to undefined)
11569      * @type String
11570      */
11571     /**
11572      * @property  extraParams
11573      * An object containing properties which are used as
11574      * extra parameters to each request made by this object. (defaults to undefined)
11575      * @type Object
11576      */
11577     /**
11578      * @property  defaultHeaders
11579      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11580      * @type Object
11581      */
11582     /**
11583      * @property  method
11584      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11585      * @type String
11586      */
11587     /**
11588      * @property  timeout
11589      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11590      * @type Number
11591      */
11592
11593     /**
11594      * @property  autoAbort
11595      * Whether a new request should abort any pending requests. (defaults to false)
11596      * @type Boolean
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @param {String/HTMLElement} form
11603      * @return {String}
11604      */
11605     serializeForm : function(form){
11606         return Roo.lib.Ajax.serializeForm(form);
11607     }
11608 });/*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618
11619  
11620 /**
11621  * @class Roo.UpdateManager
11622  * @extends Roo.util.Observable
11623  * Provides AJAX-style update for Element object.<br><br>
11624  * Usage:<br>
11625  * <pre><code>
11626  * // Get it from a Roo.Element object
11627  * var el = Roo.get("foo");
11628  * var mgr = el.getUpdateManager();
11629  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11630  * ...
11631  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11632  * <br>
11633  * // or directly (returns the same UpdateManager instance)
11634  * var mgr = new Roo.UpdateManager("myElementId");
11635  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11636  * mgr.on("update", myFcnNeedsToKnow);
11637  * <br>
11638    // short handed call directly from the element object
11639    Roo.get("foo").load({
11640         url: "bar.php",
11641         scripts:true,
11642         params: "for=bar",
11643         text: "Loading Foo..."
11644    });
11645  * </code></pre>
11646  * @constructor
11647  * Create new UpdateManager directly.
11648  * @param {String/HTMLElement/Roo.Element} el The element to update
11649  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11650  */
11651 Roo.UpdateManager = function(el, forceNew){
11652     el = Roo.get(el);
11653     if(!forceNew && el.updateManager){
11654         return el.updateManager;
11655     }
11656     /**
11657      * The Element object
11658      * @type Roo.Element
11659      */
11660     this.el = el;
11661     /**
11662      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11663      * @type String
11664      */
11665     this.defaultUrl = null;
11666
11667     this.addEvents({
11668         /**
11669          * @event beforeupdate
11670          * Fired before an update is made, return false from your handler and the update is cancelled.
11671          * @param {Roo.Element} el
11672          * @param {String/Object/Function} url
11673          * @param {String/Object} params
11674          */
11675         "beforeupdate": true,
11676         /**
11677          * @event update
11678          * Fired after successful update is made.
11679          * @param {Roo.Element} el
11680          * @param {Object} oResponseObject The response Object
11681          */
11682         "update": true,
11683         /**
11684          * @event failure
11685          * Fired on update failure.
11686          * @param {Roo.Element} el
11687          * @param {Object} oResponseObject The response Object
11688          */
11689         "failure": true
11690     });
11691     var d = Roo.UpdateManager.defaults;
11692     /**
11693      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11694      * @type String
11695      */
11696     this.sslBlankUrl = d.sslBlankUrl;
11697     /**
11698      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11699      * @type Boolean
11700      */
11701     this.disableCaching = d.disableCaching;
11702     /**
11703      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11704      * @type String
11705      */
11706     this.indicatorText = d.indicatorText;
11707     /**
11708      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11709      * @type String
11710      */
11711     this.showLoadIndicator = d.showLoadIndicator;
11712     /**
11713      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11714      * @type Number
11715      */
11716     this.timeout = d.timeout;
11717
11718     /**
11719      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11720      * @type Boolean
11721      */
11722     this.loadScripts = d.loadScripts;
11723
11724     /**
11725      * Transaction object of current executing transaction
11726      */
11727     this.transaction = null;
11728
11729     /**
11730      * @private
11731      */
11732     this.autoRefreshProcId = null;
11733     /**
11734      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11735      * @type Function
11736      */
11737     this.refreshDelegate = this.refresh.createDelegate(this);
11738     /**
11739      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11740      * @type Function
11741      */
11742     this.updateDelegate = this.update.createDelegate(this);
11743     /**
11744      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11745      * @type Function
11746      */
11747     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11748     /**
11749      * @private
11750      */
11751     this.successDelegate = this.processSuccess.createDelegate(this);
11752     /**
11753      * @private
11754      */
11755     this.failureDelegate = this.processFailure.createDelegate(this);
11756
11757     if(!this.renderer){
11758      /**
11759       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11760       */
11761     this.renderer = new Roo.UpdateManager.BasicRenderer();
11762     }
11763     
11764     Roo.UpdateManager.superclass.constructor.call(this);
11765 };
11766
11767 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11768     /**
11769      * Get the Element this UpdateManager is bound to
11770      * @return {Roo.Element} The element
11771      */
11772     getEl : function(){
11773         return this.el;
11774     },
11775     /**
11776      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11777      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11778 <pre><code>
11779 um.update({<br/>
11780     url: "your-url.php",<br/>
11781     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11782     callback: yourFunction,<br/>
11783     scope: yourObject, //(optional scope)  <br/>
11784     discardUrl: false, <br/>
11785     nocache: false,<br/>
11786     text: "Loading...",<br/>
11787     timeout: 30,<br/>
11788     scripts: false<br/>
11789 });
11790 </code></pre>
11791      * The only required property is url. The optional properties nocache, text and scripts
11792      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11793      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11794      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11795      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11796      */
11797     update : function(url, params, callback, discardUrl){
11798         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11799             var method = this.method, cfg;
11800             if(typeof url == "object"){ // must be config object
11801                 cfg = url;
11802                 url = cfg.url;
11803                 params = params || cfg.params;
11804                 callback = callback || cfg.callback;
11805                 discardUrl = discardUrl || cfg.discardUrl;
11806                 if(callback && cfg.scope){
11807                     callback = callback.createDelegate(cfg.scope);
11808                 }
11809                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11810                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11811                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11812                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11813                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11814             }
11815             this.showLoading();
11816             if(!discardUrl){
11817                 this.defaultUrl = url;
11818             }
11819             if(typeof url == "function"){
11820                 url = url.call(this);
11821             }
11822
11823             method = method || (params ? "POST" : "GET");
11824             if(method == "GET"){
11825                 url = this.prepareUrl(url);
11826             }
11827
11828             var o = Roo.apply(cfg ||{}, {
11829                 url : url,
11830                 params: params,
11831                 success: this.successDelegate,
11832                 failure: this.failureDelegate,
11833                 callback: undefined,
11834                 timeout: (this.timeout*1000),
11835                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11836             });
11837
11838             this.transaction = Roo.Ajax.request(o);
11839         }
11840     },
11841
11842     /**
11843      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11844      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11845      * @param {String/HTMLElement} form The form Id or form element
11846      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11847      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11848      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11849      */
11850     formUpdate : function(form, url, reset, callback){
11851         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855             form = Roo.getDom(form);
11856             this.transaction = Roo.Ajax.request({
11857                 form: form,
11858                 url:url,
11859                 success: this.successDelegate,
11860                 failure: this.failureDelegate,
11861                 timeout: (this.timeout*1000),
11862                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11863             });
11864             this.showLoading.defer(1, this);
11865         }
11866     },
11867
11868     /**
11869      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11870      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11871      */
11872     refresh : function(callback){
11873         if(this.defaultUrl == null){
11874             return;
11875         }
11876         this.update(this.defaultUrl, null, callback, true);
11877     },
11878
11879     /**
11880      * Set this element to auto refresh.
11881      * @param {Number} interval How often to update (in seconds).
11882      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11883      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11885      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11886      */
11887     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11888         if(refreshNow){
11889             this.update(url || this.defaultUrl, params, callback, true);
11890         }
11891         if(this.autoRefreshProcId){
11892             clearInterval(this.autoRefreshProcId);
11893         }
11894         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11895     },
11896
11897     /**
11898      * Stop auto refresh on this element.
11899      */
11900      stopAutoRefresh : function(){
11901         if(this.autoRefreshProcId){
11902             clearInterval(this.autoRefreshProcId);
11903             delete this.autoRefreshProcId;
11904         }
11905     },
11906
11907     isAutoRefreshing : function(){
11908        return this.autoRefreshProcId ? true : false;
11909     },
11910     /**
11911      * Called to update the element to "Loading" state. Override to perform custom action.
11912      */
11913     showLoading : function(){
11914         if(this.showLoadIndicator){
11915             this.el.update(this.indicatorText);
11916         }
11917     },
11918
11919     /**
11920      * Adds unique parameter to query string if disableCaching = true
11921      * @private
11922      */
11923     prepareUrl : function(url){
11924         if(this.disableCaching){
11925             var append = "_dc=" + (new Date().getTime());
11926             if(url.indexOf("?") !== -1){
11927                 url += "&" + append;
11928             }else{
11929                 url += "?" + append;
11930             }
11931         }
11932         return url;
11933     },
11934
11935     /**
11936      * @private
11937      */
11938     processSuccess : function(response){
11939         this.transaction = null;
11940         if(response.argument.form && response.argument.reset){
11941             try{ // put in try/catch since some older FF releases had problems with this
11942                 response.argument.form.reset();
11943             }catch(e){}
11944         }
11945         if(this.loadScripts){
11946             this.renderer.render(this.el, response, this,
11947                 this.updateComplete.createDelegate(this, [response]));
11948         }else{
11949             this.renderer.render(this.el, response, this);
11950             this.updateComplete(response);
11951         }
11952     },
11953
11954     updateComplete : function(response){
11955         this.fireEvent("update", this.el, response);
11956         if(typeof response.argument.callback == "function"){
11957             response.argument.callback(this.el, true, response);
11958         }
11959     },
11960
11961     /**
11962      * @private
11963      */
11964     processFailure : function(response){
11965         this.transaction = null;
11966         this.fireEvent("failure", this.el, response);
11967         if(typeof response.argument.callback == "function"){
11968             response.argument.callback(this.el, false, response);
11969         }
11970     },
11971
11972     /**
11973      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11974      * @param {Object} renderer The object implementing the render() method
11975      */
11976     setRenderer : function(renderer){
11977         this.renderer = renderer;
11978     },
11979
11980     getRenderer : function(){
11981        return this.renderer;
11982     },
11983
11984     /**
11985      * Set the defaultUrl used for updates
11986      * @param {String/Function} defaultUrl The url or a function to call to get the url
11987      */
11988     setDefaultUrl : function(defaultUrl){
11989         this.defaultUrl = defaultUrl;
11990     },
11991
11992     /**
11993      * Aborts the executing transaction
11994      */
11995     abort : function(){
11996         if(this.transaction){
11997             Roo.Ajax.abort(this.transaction);
11998         }
11999     },
12000
12001     /**
12002      * Returns true if an update is in progress
12003      * @return {Boolean}
12004      */
12005     isUpdating : function(){
12006         if(this.transaction){
12007             return Roo.Ajax.isLoading(this.transaction);
12008         }
12009         return false;
12010     }
12011 });
12012
12013 /**
12014  * @class Roo.UpdateManager.defaults
12015  * @static (not really - but it helps the doc tool)
12016  * The defaults collection enables customizing the default properties of UpdateManager
12017  */
12018    Roo.UpdateManager.defaults = {
12019        /**
12020          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12021          * @type Number
12022          */
12023          timeout : 30,
12024
12025          /**
12026          * True to process scripts by default (Defaults to false).
12027          * @type Boolean
12028          */
12029         loadScripts : false,
12030
12031         /**
12032         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12033         * @type String
12034         */
12035         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12036         /**
12037          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12038          * @type Boolean
12039          */
12040         disableCaching : false,
12041         /**
12042          * Whether to show indicatorText when loading (Defaults to true).
12043          * @type Boolean
12044          */
12045         showLoadIndicator : true,
12046         /**
12047          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12048          * @type String
12049          */
12050         indicatorText : '<div class="loading-indicator">Loading...</div>'
12051    };
12052
12053 /**
12054  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12055  *Usage:
12056  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12057  * @param {String/HTMLElement/Roo.Element} el The element to update
12058  * @param {String} url The url
12059  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12060  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12061  * @static
12062  * @deprecated
12063  * @member Roo.UpdateManager
12064  */
12065 Roo.UpdateManager.updateElement = function(el, url, params, options){
12066     var um = Roo.get(el, true).getUpdateManager();
12067     Roo.apply(um, options);
12068     um.update(url, params, options ? options.callback : null);
12069 };
12070 // alias for backwards compat
12071 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12072 /**
12073  * @class Roo.UpdateManager.BasicRenderer
12074  * Default Content renderer. Updates the elements innerHTML with the responseText.
12075  */
12076 Roo.UpdateManager.BasicRenderer = function(){};
12077
12078 Roo.UpdateManager.BasicRenderer.prototype = {
12079     /**
12080      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12081      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12082      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12083      * @param {Roo.Element} el The element being rendered
12084      * @param {Object} response The YUI Connect response object
12085      * @param {UpdateManager} updateManager The calling update manager
12086      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12087      */
12088      render : function(el, response, updateManager, callback){
12089         el.update(response.responseText, updateManager.loadScripts, callback);
12090     }
12091 };
12092 /*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103 /**
12104  * @class Roo.util.DelayedTask
12105  * Provides a convenient method of performing setTimeout where a new
12106  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12107  * You can use this class to buffer
12108  * the keypress events for a certain number of milliseconds, and perform only if they stop
12109  * for that amount of time.
12110  * @constructor The parameters to this constructor serve as defaults and are not required.
12111  * @param {Function} fn (optional) The default function to timeout
12112  * @param {Object} scope (optional) The default scope of that timeout
12113  * @param {Array} args (optional) The default Array of arguments
12114  */
12115 Roo.util.DelayedTask = function(fn, scope, args){
12116     var id = null, d, t;
12117
12118     var call = function(){
12119         var now = new Date().getTime();
12120         if(now - t >= d){
12121             clearInterval(id);
12122             id = null;
12123             fn.apply(scope, args || []);
12124         }
12125     };
12126     /**
12127      * Cancels any pending timeout and queues a new one
12128      * @param {Number} delay The milliseconds to delay
12129      * @param {Function} newFn (optional) Overrides function passed to constructor
12130      * @param {Object} newScope (optional) Overrides scope passed to constructor
12131      * @param {Array} newArgs (optional) Overrides args passed to constructor
12132      */
12133     this.delay = function(delay, newFn, newScope, newArgs){
12134         if(id && delay != d){
12135             this.cancel();
12136         }
12137         d = delay;
12138         t = new Date().getTime();
12139         fn = newFn || fn;
12140         scope = newScope || scope;
12141         args = newArgs || args;
12142         if(!id){
12143             id = setInterval(call, d);
12144         }
12145     };
12146
12147     /**
12148      * Cancel the last queued timeout
12149      */
12150     this.cancel = function(){
12151         if(id){
12152             clearInterval(id);
12153             id = null;
12154         }
12155     };
12156 };/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166  
12167  
12168 Roo.util.TaskRunner = function(interval){
12169     interval = interval || 10;
12170     var tasks = [], removeQueue = [];
12171     var id = 0;
12172     var running = false;
12173
12174     var stopThread = function(){
12175         running = false;
12176         clearInterval(id);
12177         id = 0;
12178     };
12179
12180     var startThread = function(){
12181         if(!running){
12182             running = true;
12183             id = setInterval(runTasks, interval);
12184         }
12185     };
12186
12187     var removeTask = function(task){
12188         removeQueue.push(task);
12189         if(task.onStop){
12190             task.onStop();
12191         }
12192     };
12193
12194     var runTasks = function(){
12195         if(removeQueue.length > 0){
12196             for(var i = 0, len = removeQueue.length; i < len; i++){
12197                 tasks.remove(removeQueue[i]);
12198             }
12199             removeQueue = [];
12200             if(tasks.length < 1){
12201                 stopThread();
12202                 return;
12203             }
12204         }
12205         var now = new Date().getTime();
12206         for(var i = 0, len = tasks.length; i < len; ++i){
12207             var t = tasks[i];
12208             var itime = now - t.taskRunTime;
12209             if(t.interval <= itime){
12210                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12211                 t.taskRunTime = now;
12212                 if(rt === false || t.taskRunCount === t.repeat){
12213                     removeTask(t);
12214                     return;
12215                 }
12216             }
12217             if(t.duration && t.duration <= (now - t.taskStartTime)){
12218                 removeTask(t);
12219             }
12220         }
12221     };
12222
12223     /**
12224      * Queues a new task.
12225      * @param {Object} task
12226      */
12227     this.start = function(task){
12228         tasks.push(task);
12229         task.taskStartTime = new Date().getTime();
12230         task.taskRunTime = 0;
12231         task.taskRunCount = 0;
12232         startThread();
12233         return task;
12234     };
12235
12236     this.stop = function(task){
12237         removeTask(task);
12238         return task;
12239     };
12240
12241     this.stopAll = function(){
12242         stopThread();
12243         for(var i = 0, len = tasks.length; i < len; i++){
12244             if(tasks[i].onStop){
12245                 tasks[i].onStop();
12246             }
12247         }
12248         tasks = [];
12249         removeQueue = [];
12250     };
12251 };
12252
12253 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12254  * Based on:
12255  * Ext JS Library 1.1.1
12256  * Copyright(c) 2006-2007, Ext JS, LLC.
12257  *
12258  * Originally Released Under LGPL - original licence link has changed is not relivant.
12259  *
12260  * Fork - LGPL
12261  * <script type="text/javascript">
12262  */
12263
12264  
12265 /**
12266  * @class Roo.util.MixedCollection
12267  * @extends Roo.util.Observable
12268  * A Collection class that maintains both numeric indexes and keys and exposes events.
12269  * @constructor
12270  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12271  * collection (defaults to false)
12272  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12273  * and return the key value for that item.  This is used when available to look up the key on items that
12274  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12275  * equivalent to providing an implementation for the {@link #getKey} method.
12276  */
12277 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12278     this.items = [];
12279     this.map = {};
12280     this.keys = [];
12281     this.length = 0;
12282     this.addEvents({
12283         /**
12284          * @event clear
12285          * Fires when the collection is cleared.
12286          */
12287         "clear" : true,
12288         /**
12289          * @event add
12290          * Fires when an item is added to the collection.
12291          * @param {Number} index The index at which the item was added.
12292          * @param {Object} o The item added.
12293          * @param {String} key The key associated with the added item.
12294          */
12295         "add" : true,
12296         /**
12297          * @event replace
12298          * Fires when an item is replaced in the collection.
12299          * @param {String} key he key associated with the new added.
12300          * @param {Object} old The item being replaced.
12301          * @param {Object} new The new item.
12302          */
12303         "replace" : true,
12304         /**
12305          * @event remove
12306          * Fires when an item is removed from the collection.
12307          * @param {Object} o The item being removed.
12308          * @param {String} key (optional) The key associated with the removed item.
12309          */
12310         "remove" : true,
12311         "sort" : true
12312     });
12313     this.allowFunctions = allowFunctions === true;
12314     if(keyFn){
12315         this.getKey = keyFn;
12316     }
12317     Roo.util.MixedCollection.superclass.constructor.call(this);
12318 };
12319
12320 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12321     allowFunctions : false,
12322     
12323 /**
12324  * Adds an item to the collection.
12325  * @param {String} key The key to associate with the item
12326  * @param {Object} o The item to add.
12327  * @return {Object} The item added.
12328  */
12329     add : function(key, o){
12330         if(arguments.length == 1){
12331             o = arguments[0];
12332             key = this.getKey(o);
12333         }
12334         if(typeof key == "undefined" || key === null){
12335             this.length++;
12336             this.items.push(o);
12337             this.keys.push(null);
12338         }else{
12339             var old = this.map[key];
12340             if(old){
12341                 return this.replace(key, o);
12342             }
12343             this.length++;
12344             this.items.push(o);
12345             this.map[key] = o;
12346             this.keys.push(key);
12347         }
12348         this.fireEvent("add", this.length-1, o, key);
12349         return o;
12350     },
12351        
12352 /**
12353   * MixedCollection has a generic way to fetch keys if you implement getKey.
12354 <pre><code>
12355 // normal way
12356 var mc = new Roo.util.MixedCollection();
12357 mc.add(someEl.dom.id, someEl);
12358 mc.add(otherEl.dom.id, otherEl);
12359 //and so on
12360
12361 // using getKey
12362 var mc = new Roo.util.MixedCollection();
12363 mc.getKey = function(el){
12364    return el.dom.id;
12365 };
12366 mc.add(someEl);
12367 mc.add(otherEl);
12368
12369 // or via the constructor
12370 var mc = new Roo.util.MixedCollection(false, function(el){
12371    return el.dom.id;
12372 });
12373 mc.add(someEl);
12374 mc.add(otherEl);
12375 </code></pre>
12376  * @param o {Object} The item for which to find the key.
12377  * @return {Object} The key for the passed item.
12378  */
12379     getKey : function(o){
12380          return o.id; 
12381     },
12382    
12383 /**
12384  * Replaces an item in the collection.
12385  * @param {String} key The key associated with the item to replace, or the item to replace.
12386  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12387  * @return {Object}  The new item.
12388  */
12389     replace : function(key, o){
12390         if(arguments.length == 1){
12391             o = arguments[0];
12392             key = this.getKey(o);
12393         }
12394         var old = this.item(key);
12395         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12396              return this.add(key, o);
12397         }
12398         var index = this.indexOfKey(key);
12399         this.items[index] = o;
12400         this.map[key] = o;
12401         this.fireEvent("replace", key, old, o);
12402         return o;
12403     },
12404    
12405 /**
12406  * Adds all elements of an Array or an Object to the collection.
12407  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12408  * an Array of values, each of which are added to the collection.
12409  */
12410     addAll : function(objs){
12411         if(arguments.length > 1 || objs instanceof Array){
12412             var args = arguments.length > 1 ? arguments : objs;
12413             for(var i = 0, len = args.length; i < len; i++){
12414                 this.add(args[i]);
12415             }
12416         }else{
12417             for(var key in objs){
12418                 if(this.allowFunctions || typeof objs[key] != "function"){
12419                     this.add(key, objs[key]);
12420                 }
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every item in the collection, passing each
12427  * item as the first and only parameter. returning false from the function will stop the iteration.
12428  * @param {Function} fn The function to execute for each item.
12429  * @param {Object} scope (optional) The scope in which to execute the function.
12430  */
12431     each : function(fn, scope){
12432         var items = [].concat(this.items); // each safe for removal
12433         for(var i = 0, len = items.length; i < len; i++){
12434             if(fn.call(scope || items[i], items[i], i, len) === false){
12435                 break;
12436             }
12437         }
12438     },
12439    
12440 /**
12441  * Executes the specified function once for every key in the collection, passing each
12442  * key, and its associated item as the first two parameters.
12443  * @param {Function} fn The function to execute for each item.
12444  * @param {Object} scope (optional) The scope in which to execute the function.
12445  */
12446     eachKey : function(fn, scope){
12447         for(var i = 0, len = this.keys.length; i < len; i++){
12448             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12449         }
12450     },
12451    
12452 /**
12453  * Returns the first item in the collection which elicits a true return value from the
12454  * passed selection function.
12455  * @param {Function} fn The selection function to execute for each item.
12456  * @param {Object} scope (optional) The scope in which to execute the function.
12457  * @return {Object} The first item in the collection which returned true from the selection function.
12458  */
12459     find : function(fn, scope){
12460         for(var i = 0, len = this.items.length; i < len; i++){
12461             if(fn.call(scope || window, this.items[i], this.keys[i])){
12462                 return this.items[i];
12463             }
12464         }
12465         return null;
12466     },
12467    
12468 /**
12469  * Inserts an item at the specified index in the collection.
12470  * @param {Number} index The index to insert the item at.
12471  * @param {String} key The key to associate with the new item, or the item itself.
12472  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12473  * @return {Object} The item inserted.
12474  */
12475     insert : function(index, key, o){
12476         if(arguments.length == 2){
12477             o = arguments[1];
12478             key = this.getKey(o);
12479         }
12480         if(index >= this.length){
12481             return this.add(key, o);
12482         }
12483         this.length++;
12484         this.items.splice(index, 0, o);
12485         if(typeof key != "undefined" && key != null){
12486             this.map[key] = o;
12487         }
12488         this.keys.splice(index, 0, key);
12489         this.fireEvent("add", index, o, key);
12490         return o;
12491     },
12492    
12493 /**
12494  * Removed an item from the collection.
12495  * @param {Object} o The item to remove.
12496  * @return {Object} The item removed.
12497  */
12498     remove : function(o){
12499         return this.removeAt(this.indexOf(o));
12500     },
12501    
12502 /**
12503  * Remove an item from a specified index in the collection.
12504  * @param {Number} index The index within the collection of the item to remove.
12505  */
12506     removeAt : function(index){
12507         if(index < this.length && index >= 0){
12508             this.length--;
12509             var o = this.items[index];
12510             this.items.splice(index, 1);
12511             var key = this.keys[index];
12512             if(typeof key != "undefined"){
12513                 delete this.map[key];
12514             }
12515             this.keys.splice(index, 1);
12516             this.fireEvent("remove", o, key);
12517         }
12518     },
12519    
12520 /**
12521  * Removed an item associated with the passed key fom the collection.
12522  * @param {String} key The key of the item to remove.
12523  */
12524     removeKey : function(key){
12525         return this.removeAt(this.indexOfKey(key));
12526     },
12527    
12528 /**
12529  * Returns the number of items in the collection.
12530  * @return {Number} the number of items in the collection.
12531  */
12532     getCount : function(){
12533         return this.length; 
12534     },
12535    
12536 /**
12537  * Returns index within the collection of the passed Object.
12538  * @param {Object} o The item to find the index of.
12539  * @return {Number} index of the item.
12540  */
12541     indexOf : function(o){
12542         if(!this.items.indexOf){
12543             for(var i = 0, len = this.items.length; i < len; i++){
12544                 if(this.items[i] == o) return i;
12545             }
12546             return -1;
12547         }else{
12548             return this.items.indexOf(o);
12549         }
12550     },
12551    
12552 /**
12553  * Returns index within the collection of the passed key.
12554  * @param {String} key The key to find the index of.
12555  * @return {Number} index of the key.
12556  */
12557     indexOfKey : function(key){
12558         if(!this.keys.indexOf){
12559             for(var i = 0, len = this.keys.length; i < len; i++){
12560                 if(this.keys[i] == key) return i;
12561             }
12562             return -1;
12563         }else{
12564             return this.keys.indexOf(key);
12565         }
12566     },
12567    
12568 /**
12569  * Returns the item associated with the passed key OR index. Key has priority over index.
12570  * @param {String/Number} key The key or index of the item.
12571  * @return {Object} The item associated with the passed key.
12572  */
12573     item : function(key){
12574         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12575         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12576     },
12577     
12578 /**
12579  * Returns the item at the specified index.
12580  * @param {Number} index The index of the item.
12581  * @return {Object}
12582  */
12583     itemAt : function(index){
12584         return this.items[index];
12585     },
12586     
12587 /**
12588  * Returns the item associated with the passed key.
12589  * @param {String/Number} key The key of the item.
12590  * @return {Object} The item associated with the passed key.
12591  */
12592     key : function(key){
12593         return this.map[key];
12594     },
12595    
12596 /**
12597  * Returns true if the collection contains the passed Object as an item.
12598  * @param {Object} o  The Object to look for in the collection.
12599  * @return {Boolean} True if the collection contains the Object as an item.
12600  */
12601     contains : function(o){
12602         return this.indexOf(o) != -1;
12603     },
12604    
12605 /**
12606  * Returns true if the collection contains the passed Object as a key.
12607  * @param {String} key The key to look for in the collection.
12608  * @return {Boolean} True if the collection contains the Object as a key.
12609  */
12610     containsKey : function(key){
12611         return typeof this.map[key] != "undefined";
12612     },
12613    
12614 /**
12615  * Removes all items from the collection.
12616  */
12617     clear : function(){
12618         this.length = 0;
12619         this.items = [];
12620         this.keys = [];
12621         this.map = {};
12622         this.fireEvent("clear");
12623     },
12624    
12625 /**
12626  * Returns the first item in the collection.
12627  * @return {Object} the first item in the collection..
12628  */
12629     first : function(){
12630         return this.items[0]; 
12631     },
12632    
12633 /**
12634  * Returns the last item in the collection.
12635  * @return {Object} the last item in the collection..
12636  */
12637     last : function(){
12638         return this.items[this.length-1];   
12639     },
12640     
12641     _sort : function(property, dir, fn){
12642         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12643         fn = fn || function(a, b){
12644             return a-b;
12645         };
12646         var c = [], k = this.keys, items = this.items;
12647         for(var i = 0, len = items.length; i < len; i++){
12648             c[c.length] = {key: k[i], value: items[i], index: i};
12649         }
12650         c.sort(function(a, b){
12651             var v = fn(a[property], b[property]) * dsc;
12652             if(v == 0){
12653                 v = (a.index < b.index ? -1 : 1);
12654             }
12655             return v;
12656         });
12657         for(var i = 0, len = c.length; i < len; i++){
12658             items[i] = c[i].value;
12659             k[i] = c[i].key;
12660         }
12661         this.fireEvent("sort", this);
12662     },
12663     
12664     /**
12665      * Sorts this collection with the passed comparison function
12666      * @param {String} direction (optional) "ASC" or "DESC"
12667      * @param {Function} fn (optional) comparison function
12668      */
12669     sort : function(dir, fn){
12670         this._sort("value", dir, fn);
12671     },
12672     
12673     /**
12674      * Sorts this collection by keys
12675      * @param {String} direction (optional) "ASC" or "DESC"
12676      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12677      */
12678     keySort : function(dir, fn){
12679         this._sort("key", dir, fn || function(a, b){
12680             return String(a).toUpperCase()-String(b).toUpperCase();
12681         });
12682     },
12683     
12684     /**
12685      * Returns a range of items in this collection
12686      * @param {Number} startIndex (optional) defaults to 0
12687      * @param {Number} endIndex (optional) default to the last item
12688      * @return {Array} An array of items
12689      */
12690     getRange : function(start, end){
12691         var items = this.items;
12692         if(items.length < 1){
12693             return [];
12694         }
12695         start = start || 0;
12696         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12697         var r = [];
12698         if(start <= end){
12699             for(var i = start; i <= end; i++) {
12700                     r[r.length] = items[i];
12701             }
12702         }else{
12703             for(var i = start; i >= end; i--) {
12704                     r[r.length] = items[i];
12705             }
12706         }
12707         return r;
12708     },
12709         
12710     /**
12711      * Filter the <i>objects</i> in this collection by a specific property. 
12712      * Returns a new collection that has been filtered.
12713      * @param {String} property A property on your objects
12714      * @param {String/RegExp} value Either string that the property values 
12715      * should start with or a RegExp to test against the property
12716      * @return {MixedCollection} The new filtered collection
12717      */
12718     filter : function(property, value){
12719         if(!value.exec){ // not a regex
12720             value = String(value);
12721             if(value.length == 0){
12722                 return this.clone();
12723             }
12724             value = new RegExp("^" + Roo.escapeRe(value), "i");
12725         }
12726         return this.filterBy(function(o){
12727             return o && value.test(o[property]);
12728         });
12729         },
12730     
12731     /**
12732      * Filter by a function. * Returns a new collection that has been filtered.
12733      * The passed function will be called with each 
12734      * object in the collection. If the function returns true, the value is included 
12735      * otherwise it is filtered.
12736      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12737      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12738      * @return {MixedCollection} The new filtered collection
12739      */
12740     filterBy : function(fn, scope){
12741         var r = new Roo.util.MixedCollection();
12742         r.getKey = this.getKey;
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             if(fn.call(scope||this, it[i], k[i])){
12746                                 r.add(k[i], it[i]);
12747                         }
12748         }
12749         return r;
12750     },
12751     
12752     /**
12753      * Creates a duplicate of this collection
12754      * @return {MixedCollection}
12755      */
12756     clone : function(){
12757         var r = new Roo.util.MixedCollection();
12758         var k = this.keys, it = this.items;
12759         for(var i = 0, len = it.length; i < len; i++){
12760             r.add(k[i], it[i]);
12761         }
12762         r.getKey = this.getKey;
12763         return r;
12764     }
12765 });
12766 /**
12767  * Returns the item associated with the passed key or index.
12768  * @method
12769  * @param {String/Number} key The key or index of the item.
12770  * @return {Object} The item associated with the passed key.
12771  */
12772 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782 /**
12783  * @class Roo.util.JSON
12784  * Modified version of Douglas Crockford"s json.js that doesn"t
12785  * mess with the Object prototype 
12786  * http://www.json.org/js.html
12787  * @singleton
12788  */
12789 Roo.util.JSON = new (function(){
12790     var useHasOwn = {}.hasOwnProperty ? true : false;
12791     
12792     // crashes Safari in some instances
12793     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12794     
12795     var pad = function(n) {
12796         return n < 10 ? "0" + n : n;
12797     };
12798     
12799     var m = {
12800         "\b": '\\b',
12801         "\t": '\\t',
12802         "\n": '\\n',
12803         "\f": '\\f',
12804         "\r": '\\r',
12805         '"' : '\\"',
12806         "\\": '\\\\'
12807     };
12808
12809     var encodeString = function(s){
12810         if (/["\\\x00-\x1f]/.test(s)) {
12811             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12812                 var c = m[b];
12813                 if(c){
12814                     return c;
12815                 }
12816                 c = b.charCodeAt();
12817                 return "\\u00" +
12818                     Math.floor(c / 16).toString(16) +
12819                     (c % 16).toString(16);
12820             }) + '"';
12821         }
12822         return '"' + s + '"';
12823     };
12824     
12825     var encodeArray = function(o){
12826         var a = ["["], b, i, l = o.length, v;
12827             for (i = 0; i < l; i += 1) {
12828                 v = o[i];
12829                 switch (typeof v) {
12830                     case "undefined":
12831                     case "function":
12832                     case "unknown":
12833                         break;
12834                     default:
12835                         if (b) {
12836                             a.push(',');
12837                         }
12838                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12839                         b = true;
12840                 }
12841             }
12842             a.push("]");
12843             return a.join("");
12844     };
12845     
12846     var encodeDate = function(o){
12847         return '"' + o.getFullYear() + "-" +
12848                 pad(o.getMonth() + 1) + "-" +
12849                 pad(o.getDate()) + "T" +
12850                 pad(o.getHours()) + ":" +
12851                 pad(o.getMinutes()) + ":" +
12852                 pad(o.getSeconds()) + '"';
12853     };
12854     
12855     /**
12856      * Encodes an Object, Array or other value
12857      * @param {Mixed} o The variable to encode
12858      * @return {String} The JSON string
12859      */
12860     this.encode = function(o)
12861     {
12862         // should this be extended to fully wrap stringify..
12863         
12864         if(typeof o == "undefined" || o === null){
12865             return "null";
12866         }else if(o instanceof Array){
12867             return encodeArray(o);
12868         }else if(o instanceof Date){
12869             return encodeDate(o);
12870         }else if(typeof o == "string"){
12871             return encodeString(o);
12872         }else if(typeof o == "number"){
12873             return isFinite(o) ? String(o) : "null";
12874         }else if(typeof o == "boolean"){
12875             return String(o);
12876         }else {
12877             var a = ["{"], b, i, v;
12878             for (i in o) {
12879                 if(!useHasOwn || o.hasOwnProperty(i)) {
12880                     v = o[i];
12881                     switch (typeof v) {
12882                     case "undefined":
12883                     case "function":
12884                     case "unknown":
12885                         break;
12886                     default:
12887                         if(b){
12888                             a.push(',');
12889                         }
12890                         a.push(this.encode(i), ":",
12891                                 v === null ? "null" : this.encode(v));
12892                         b = true;
12893                     }
12894                 }
12895             }
12896             a.push("}");
12897             return a.join("");
12898         }
12899     };
12900     
12901     /**
12902      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12903      * @param {String} json The JSON string
12904      * @return {Object} The resulting object
12905      */
12906     this.decode = function(json){
12907         
12908         return  /** eval:var:json */ eval("(" + json + ')');
12909     };
12910 })();
12911 /** 
12912  * Shorthand for {@link Roo.util.JSON#encode}
12913  * @member Roo encode 
12914  * @method */
12915 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12916 /** 
12917  * Shorthand for {@link Roo.util.JSON#decode}
12918  * @member Roo decode 
12919  * @method */
12920 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12921 /*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931  
12932 /**
12933  * @class Roo.util.Format
12934  * Reusable data formatting functions
12935  * @singleton
12936  */
12937 Roo.util.Format = function(){
12938     var trimRe = /^\s+|\s+$/g;
12939     return {
12940         /**
12941          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12942          * @param {String} value The string to truncate
12943          * @param {Number} length The maximum length to allow before truncating
12944          * @return {String} The converted text
12945          */
12946         ellipsis : function(value, len){
12947             if(value && value.length > len){
12948                 return value.substr(0, len-3)+"...";
12949             }
12950             return value;
12951         },
12952
12953         /**
12954          * Checks a reference and converts it to empty string if it is undefined
12955          * @param {Mixed} value Reference to check
12956          * @return {Mixed} Empty string if converted, otherwise the original value
12957          */
12958         undef : function(value){
12959             return typeof value != "undefined" ? value : "";
12960         },
12961
12962         /**
12963          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12964          * @param {String} value The string to encode
12965          * @return {String} The encoded text
12966          */
12967         htmlEncode : function(value){
12968             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12969         },
12970
12971         /**
12972          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12973          * @param {String} value The string to decode
12974          * @return {String} The decoded text
12975          */
12976         htmlDecode : function(value){
12977             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12978         },
12979
12980         /**
12981          * Trims any whitespace from either side of a string
12982          * @param {String} value The text to trim
12983          * @return {String} The trimmed text
12984          */
12985         trim : function(value){
12986             return String(value).replace(trimRe, "");
12987         },
12988
12989         /**
12990          * Returns a substring from within an original string
12991          * @param {String} value The original text
12992          * @param {Number} start The start index of the substring
12993          * @param {Number} length The length of the substring
12994          * @return {String} The substring
12995          */
12996         substr : function(value, start, length){
12997             return String(value).substr(start, length);
12998         },
12999
13000         /**
13001          * Converts a string to all lower case letters
13002          * @param {String} value The text to convert
13003          * @return {String} The converted text
13004          */
13005         lowercase : function(value){
13006             return String(value).toLowerCase();
13007         },
13008
13009         /**
13010          * Converts a string to all upper case letters
13011          * @param {String} value The text to convert
13012          * @return {String} The converted text
13013          */
13014         uppercase : function(value){
13015             return String(value).toUpperCase();
13016         },
13017
13018         /**
13019          * Converts the first character only of a string to upper case
13020          * @param {String} value The text to convert
13021          * @return {String} The converted text
13022          */
13023         capitalize : function(value){
13024             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13025         },
13026
13027         // private
13028         call : function(value, fn){
13029             if(arguments.length > 2){
13030                 var args = Array.prototype.slice.call(arguments, 2);
13031                 args.unshift(value);
13032                  
13033                 return /** eval:var:value */  eval(fn).apply(window, args);
13034             }else{
13035                 /** eval:var:value */
13036                 return /** eval:var:value */ eval(fn).call(window, value);
13037             }
13038         },
13039
13040        
13041         /**
13042          * safer version of Math.toFixed..??/
13043          * @param {Number/String} value The numeric value to format
13044          * @param {Number/String} value Decimal places 
13045          * @return {String} The formatted currency string
13046          */
13047         toFixed : function(v, n)
13048         {
13049             // why not use to fixed - precision is buggered???
13050             if (!n) {
13051                 return Math.round(v-0);
13052             }
13053             var fact = Math.pow(10,n+1);
13054             v = (Math.round((v-0)*fact))/fact;
13055             var z = (''+fact).substring(2);
13056             if (v == Math.floor(v)) {
13057                 return Math.floor(v) + '.' + z;
13058             }
13059             
13060             // now just padd decimals..
13061             var ps = String(v).split('.');
13062             var fd = (ps[1] + z);
13063             var r = fd.substring(0,n); 
13064             var rm = fd.substring(n); 
13065             if (rm < 5) {
13066                 return ps[0] + '.' + r;
13067             }
13068             r*=1; // turn it into a number;
13069             r++;
13070             if (String(r).length != n) {
13071                 ps[0]*=1;
13072                 ps[0]++;
13073                 r = String(r).substring(1); // chop the end off.
13074             }
13075             
13076             return ps[0] + '.' + r;
13077              
13078         },
13079         
13080         /**
13081          * Format a number as US currency
13082          * @param {Number/String} value The numeric value to format
13083          * @return {String} The formatted currency string
13084          */
13085         usMoney : function(v){
13086             v = (Math.round((v-0)*100))/100;
13087             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13088             v = String(v);
13089             var ps = v.split('.');
13090             var whole = ps[0];
13091             var sub = ps[1] ? '.'+ ps[1] : '.00';
13092             var r = /(\d+)(\d{3})/;
13093             while (r.test(whole)) {
13094                 whole = whole.replace(r, '$1' + ',' + '$2');
13095             }
13096             return "$" + whole + sub ;
13097         },
13098         
13099         /**
13100          * Parse a value into a formatted date using the specified format pattern.
13101          * @param {Mixed} value The value to format
13102          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13103          * @return {String} The formatted date string
13104          */
13105         date : function(v, format){
13106             if(!v){
13107                 return "";
13108             }
13109             if(!(v instanceof Date)){
13110                 v = new Date(Date.parse(v));
13111             }
13112             return v.dateFormat(format || "m/d/Y");
13113         },
13114
13115         /**
13116          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13117          * @param {String} format Any valid date format string
13118          * @return {Function} The date formatting function
13119          */
13120         dateRenderer : function(format){
13121             return function(v){
13122                 return Roo.util.Format.date(v, format);  
13123             };
13124         },
13125
13126         // private
13127         stripTagsRE : /<\/?[^>]+>/gi,
13128         
13129         /**
13130          * Strips all HTML tags
13131          * @param {Mixed} value The text from which to strip tags
13132          * @return {String} The stripped text
13133          */
13134         stripTags : function(v){
13135             return !v ? v : String(v).replace(this.stripTagsRE, "");
13136         }
13137     };
13138 }();/*
13139  * Based on:
13140  * Ext JS Library 1.1.1
13141  * Copyright(c) 2006-2007, Ext JS, LLC.
13142  *
13143  * Originally Released Under LGPL - original licence link has changed is not relivant.
13144  *
13145  * Fork - LGPL
13146  * <script type="text/javascript">
13147  */
13148
13149
13150  
13151
13152 /**
13153  * @class Roo.MasterTemplate
13154  * @extends Roo.Template
13155  * Provides a template that can have child templates. The syntax is:
13156 <pre><code>
13157 var t = new Roo.MasterTemplate(
13158         '&lt;select name="{name}"&gt;',
13159                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13160         '&lt;/select&gt;'
13161 );
13162 t.add('options', {value: 'foo', text: 'bar'});
13163 // or you can add multiple child elements in one shot
13164 t.addAll('options', [
13165     {value: 'foo', text: 'bar'},
13166     {value: 'foo2', text: 'bar2'},
13167     {value: 'foo3', text: 'bar3'}
13168 ]);
13169 // then append, applying the master template values
13170 t.append('my-form', {name: 'my-select'});
13171 </code></pre>
13172 * A name attribute for the child template is not required if you have only one child
13173 * template or you want to refer to them by index.
13174  */
13175 Roo.MasterTemplate = function(){
13176     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13177     this.originalHtml = this.html;
13178     var st = {};
13179     var m, re = this.subTemplateRe;
13180     re.lastIndex = 0;
13181     var subIndex = 0;
13182     while(m = re.exec(this.html)){
13183         var name = m[1], content = m[2];
13184         st[subIndex] = {
13185             name: name,
13186             index: subIndex,
13187             buffer: [],
13188             tpl : new Roo.Template(content)
13189         };
13190         if(name){
13191             st[name] = st[subIndex];
13192         }
13193         st[subIndex].tpl.compile();
13194         st[subIndex].tpl.call = this.call.createDelegate(this);
13195         subIndex++;
13196     }
13197     this.subCount = subIndex;
13198     this.subs = st;
13199 };
13200 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13201     /**
13202     * The regular expression used to match sub templates
13203     * @type RegExp
13204     * @property
13205     */
13206     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13207
13208     /**
13209      * Applies the passed values to a child template.
13210      * @param {String/Number} name (optional) The name or index of the child template
13211      * @param {Array/Object} values The values to be applied to the template
13212      * @return {MasterTemplate} this
13213      */
13214      add : function(name, values){
13215         if(arguments.length == 1){
13216             values = arguments[0];
13217             name = 0;
13218         }
13219         var s = this.subs[name];
13220         s.buffer[s.buffer.length] = s.tpl.apply(values);
13221         return this;
13222     },
13223
13224     /**
13225      * Applies all the passed values to a child template.
13226      * @param {String/Number} name (optional) The name or index of the child template
13227      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13228      * @param {Boolean} reset (optional) True to reset the template first
13229      * @return {MasterTemplate} this
13230      */
13231     fill : function(name, values, reset){
13232         var a = arguments;
13233         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13234             values = a[0];
13235             name = 0;
13236             reset = a[1];
13237         }
13238         if(reset){
13239             this.reset();
13240         }
13241         for(var i = 0, len = values.length; i < len; i++){
13242             this.add(name, values[i]);
13243         }
13244         return this;
13245     },
13246
13247     /**
13248      * Resets the template for reuse
13249      * @return {MasterTemplate} this
13250      */
13251      reset : function(){
13252         var s = this.subs;
13253         for(var i = 0; i < this.subCount; i++){
13254             s[i].buffer = [];
13255         }
13256         return this;
13257     },
13258
13259     applyTemplate : function(values){
13260         var s = this.subs;
13261         var replaceIndex = -1;
13262         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13263             return s[++replaceIndex].buffer.join("");
13264         });
13265         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13266     },
13267
13268     apply : function(){
13269         return this.applyTemplate.apply(this, arguments);
13270     },
13271
13272     compile : function(){return this;}
13273 });
13274
13275 /**
13276  * Alias for fill().
13277  * @method
13278  */
13279 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13280  /**
13281  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13282  * var tpl = Roo.MasterTemplate.from('element-id');
13283  * @param {String/HTMLElement} el
13284  * @param {Object} config
13285  * @static
13286  */
13287 Roo.MasterTemplate.from = function(el, config){
13288     el = Roo.getDom(el);
13289     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13290 };/*
13291  * Based on:
13292  * Ext JS Library 1.1.1
13293  * Copyright(c) 2006-2007, Ext JS, LLC.
13294  *
13295  * Originally Released Under LGPL - original licence link has changed is not relivant.
13296  *
13297  * Fork - LGPL
13298  * <script type="text/javascript">
13299  */
13300
13301  
13302 /**
13303  * @class Roo.util.CSS
13304  * Utility class for manipulating CSS rules
13305  * @singleton
13306  */
13307 Roo.util.CSS = function(){
13308         var rules = null;
13309         var doc = document;
13310
13311     var camelRe = /(-[a-z])/gi;
13312     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13313
13314    return {
13315    /**
13316     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13317     * tag and appended to the HEAD of the document.
13318     * @param {String|Object} cssText The text containing the css rules
13319     * @param {String} id An id to add to the stylesheet for later removal
13320     * @return {StyleSheet}
13321     */
13322     createStyleSheet : function(cssText, id){
13323         var ss;
13324         var head = doc.getElementsByTagName("head")[0];
13325         var nrules = doc.createElement("style");
13326         nrules.setAttribute("type", "text/css");
13327         if(id){
13328             nrules.setAttribute("id", id);
13329         }
13330         if (typeof(cssText) != 'string') {
13331             // support object maps..
13332             // not sure if this a good idea.. 
13333             // perhaps it should be merged with the general css handling
13334             // and handle js style props.
13335             var cssTextNew = [];
13336             for(var n in cssText) {
13337                 var citems = [];
13338                 for(var k in cssText[n]) {
13339                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13340                 }
13341                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13342                 
13343             }
13344             cssText = cssTextNew.join("\n");
13345             
13346         }
13347        
13348        
13349        if(Roo.isIE){
13350            head.appendChild(nrules);
13351            ss = nrules.styleSheet;
13352            ss.cssText = cssText;
13353        }else{
13354            try{
13355                 nrules.appendChild(doc.createTextNode(cssText));
13356            }catch(e){
13357                nrules.cssText = cssText; 
13358            }
13359            head.appendChild(nrules);
13360            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13361        }
13362        this.cacheStyleSheet(ss);
13363        return ss;
13364    },
13365
13366    /**
13367     * Removes a style or link tag by id
13368     * @param {String} id The id of the tag
13369     */
13370    removeStyleSheet : function(id){
13371        var existing = doc.getElementById(id);
13372        if(existing){
13373            existing.parentNode.removeChild(existing);
13374        }
13375    },
13376
13377    /**
13378     * Dynamically swaps an existing stylesheet reference for a new one
13379     * @param {String} id The id of an existing link tag to remove
13380     * @param {String} url The href of the new stylesheet to include
13381     */
13382    swapStyleSheet : function(id, url){
13383        this.removeStyleSheet(id);
13384        var ss = doc.createElement("link");
13385        ss.setAttribute("rel", "stylesheet");
13386        ss.setAttribute("type", "text/css");
13387        ss.setAttribute("id", id);
13388        ss.setAttribute("href", url);
13389        doc.getElementsByTagName("head")[0].appendChild(ss);
13390    },
13391    
13392    /**
13393     * Refresh the rule cache if you have dynamically added stylesheets
13394     * @return {Object} An object (hash) of rules indexed by selector
13395     */
13396    refreshCache : function(){
13397        return this.getRules(true);
13398    },
13399
13400    // private
13401    cacheStyleSheet : function(stylesheet){
13402        if(!rules){
13403            rules = {};
13404        }
13405        try{// try catch for cross domain access issue
13406            var ssRules = stylesheet.cssRules || stylesheet.rules;
13407            for(var j = ssRules.length-1; j >= 0; --j){
13408                rules[ssRules[j].selectorText] = ssRules[j];
13409            }
13410        }catch(e){}
13411    },
13412    
13413    /**
13414     * Gets all css rules for the document
13415     * @param {Boolean} refreshCache true to refresh the internal cache
13416     * @return {Object} An object (hash) of rules indexed by selector
13417     */
13418    getRules : function(refreshCache){
13419                 if(rules == null || refreshCache){
13420                         rules = {};
13421                         var ds = doc.styleSheets;
13422                         for(var i =0, len = ds.length; i < len; i++){
13423                             try{
13424                         this.cacheStyleSheet(ds[i]);
13425                     }catch(e){} 
13426                 }
13427                 }
13428                 return rules;
13429         },
13430         
13431         /**
13432     * Gets an an individual CSS rule by selector(s)
13433     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13434     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13435     * @return {CSSRule} The CSS rule or null if one is not found
13436     */
13437    getRule : function(selector, refreshCache){
13438                 var rs = this.getRules(refreshCache);
13439                 if(!(selector instanceof Array)){
13440                     return rs[selector];
13441                 }
13442                 for(var i = 0; i < selector.length; i++){
13443                         if(rs[selector[i]]){
13444                                 return rs[selector[i]];
13445                         }
13446                 }
13447                 return null;
13448         },
13449         
13450         
13451         /**
13452     * Updates a rule property
13453     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13454     * @param {String} property The css property
13455     * @param {String} value The new value for the property
13456     * @return {Boolean} true If a rule was found and updated
13457     */
13458    updateRule : function(selector, property, value){
13459                 if(!(selector instanceof Array)){
13460                         var rule = this.getRule(selector);
13461                         if(rule){
13462                                 rule.style[property.replace(camelRe, camelFn)] = value;
13463                                 return true;
13464                         }
13465                 }else{
13466                         for(var i = 0; i < selector.length; i++){
13467                                 if(this.updateRule(selector[i], property, value)){
13468                                         return true;
13469                                 }
13470                         }
13471                 }
13472                 return false;
13473         }
13474    };   
13475 }();/*
13476  * Based on:
13477  * Ext JS Library 1.1.1
13478  * Copyright(c) 2006-2007, Ext JS, LLC.
13479  *
13480  * Originally Released Under LGPL - original licence link has changed is not relivant.
13481  *
13482  * Fork - LGPL
13483  * <script type="text/javascript">
13484  */
13485
13486  
13487
13488 /**
13489  * @class Roo.util.ClickRepeater
13490  * @extends Roo.util.Observable
13491  * 
13492  * A wrapper class which can be applied to any element. Fires a "click" event while the
13493  * mouse is pressed. The interval between firings may be specified in the config but
13494  * defaults to 10 milliseconds.
13495  * 
13496  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13497  * 
13498  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13499  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13500  * Similar to an autorepeat key delay.
13501  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13502  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13503  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13504  *           "interval" and "delay" are ignored. "immediate" is honored.
13505  * @cfg {Boolean} preventDefault True to prevent the default click event
13506  * @cfg {Boolean} stopDefault True to stop the default click event
13507  * 
13508  * @history
13509  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13510  *     2007-02-02 jvs Renamed to ClickRepeater
13511  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13512  *
13513  *  @constructor
13514  * @param {String/HTMLElement/Element} el The element to listen on
13515  * @param {Object} config
13516  **/
13517 Roo.util.ClickRepeater = function(el, config)
13518 {
13519     this.el = Roo.get(el);
13520     this.el.unselectable();
13521
13522     Roo.apply(this, config);
13523
13524     this.addEvents({
13525     /**
13526      * @event mousedown
13527      * Fires when the mouse button is depressed.
13528      * @param {Roo.util.ClickRepeater} this
13529      */
13530         "mousedown" : true,
13531     /**
13532      * @event click
13533      * Fires on a specified interval during the time the element is pressed.
13534      * @param {Roo.util.ClickRepeater} this
13535      */
13536         "click" : true,
13537     /**
13538      * @event mouseup
13539      * Fires when the mouse key is released.
13540      * @param {Roo.util.ClickRepeater} this
13541      */
13542         "mouseup" : true
13543     });
13544
13545     this.el.on("mousedown", this.handleMouseDown, this);
13546     if(this.preventDefault || this.stopDefault){
13547         this.el.on("click", function(e){
13548             if(this.preventDefault){
13549                 e.preventDefault();
13550             }
13551             if(this.stopDefault){
13552                 e.stopEvent();
13553             }
13554         }, this);
13555     }
13556
13557     // allow inline handler
13558     if(this.handler){
13559         this.on("click", this.handler,  this.scope || this);
13560     }
13561
13562     Roo.util.ClickRepeater.superclass.constructor.call(this);
13563 };
13564
13565 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13566     interval : 20,
13567     delay: 250,
13568     preventDefault : true,
13569     stopDefault : false,
13570     timer : 0,
13571
13572     // private
13573     handleMouseDown : function(){
13574         clearTimeout(this.timer);
13575         this.el.blur();
13576         if(this.pressClass){
13577             this.el.addClass(this.pressClass);
13578         }
13579         this.mousedownTime = new Date();
13580
13581         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13582         this.el.on("mouseout", this.handleMouseOut, this);
13583
13584         this.fireEvent("mousedown", this);
13585         this.fireEvent("click", this);
13586         
13587         this.timer = this.click.defer(this.delay || this.interval, this);
13588     },
13589
13590     // private
13591     click : function(){
13592         this.fireEvent("click", this);
13593         this.timer = this.click.defer(this.getInterval(), this);
13594     },
13595
13596     // private
13597     getInterval: function(){
13598         if(!this.accelerate){
13599             return this.interval;
13600         }
13601         var pressTime = this.mousedownTime.getElapsed();
13602         if(pressTime < 500){
13603             return 400;
13604         }else if(pressTime < 1700){
13605             return 320;
13606         }else if(pressTime < 2600){
13607             return 250;
13608         }else if(pressTime < 3500){
13609             return 180;
13610         }else if(pressTime < 4400){
13611             return 140;
13612         }else if(pressTime < 5300){
13613             return 80;
13614         }else if(pressTime < 6200){
13615             return 50;
13616         }else{
13617             return 10;
13618         }
13619     },
13620
13621     // private
13622     handleMouseOut : function(){
13623         clearTimeout(this.timer);
13624         if(this.pressClass){
13625             this.el.removeClass(this.pressClass);
13626         }
13627         this.el.on("mouseover", this.handleMouseReturn, this);
13628     },
13629
13630     // private
13631     handleMouseReturn : function(){
13632         this.el.un("mouseover", this.handleMouseReturn);
13633         if(this.pressClass){
13634             this.el.addClass(this.pressClass);
13635         }
13636         this.click();
13637     },
13638
13639     // private
13640     handleMouseUp : function(){
13641         clearTimeout(this.timer);
13642         this.el.un("mouseover", this.handleMouseReturn);
13643         this.el.un("mouseout", this.handleMouseOut);
13644         Roo.get(document).un("mouseup", this.handleMouseUp);
13645         this.el.removeClass(this.pressClass);
13646         this.fireEvent("mouseup", this);
13647     }
13648 });/*
13649  * Based on:
13650  * Ext JS Library 1.1.1
13651  * Copyright(c) 2006-2007, Ext JS, LLC.
13652  *
13653  * Originally Released Under LGPL - original licence link has changed is not relivant.
13654  *
13655  * Fork - LGPL
13656  * <script type="text/javascript">
13657  */
13658
13659  
13660 /**
13661  * @class Roo.KeyNav
13662  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13663  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13664  * way to implement custom navigation schemes for any UI component.</p>
13665  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13666  * pageUp, pageDown, del, home, end.  Usage:</p>
13667  <pre><code>
13668 var nav = new Roo.KeyNav("my-element", {
13669     "left" : function(e){
13670         this.moveLeft(e.ctrlKey);
13671     },
13672     "right" : function(e){
13673         this.moveRight(e.ctrlKey);
13674     },
13675     "enter" : function(e){
13676         this.save();
13677     },
13678     scope : this
13679 });
13680 </code></pre>
13681  * @constructor
13682  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13683  * @param {Object} config The config
13684  */
13685 Roo.KeyNav = function(el, config){
13686     this.el = Roo.get(el);
13687     Roo.apply(this, config);
13688     if(!this.disabled){
13689         this.disabled = true;
13690         this.enable();
13691     }
13692 };
13693
13694 Roo.KeyNav.prototype = {
13695     /**
13696      * @cfg {Boolean} disabled
13697      * True to disable this KeyNav instance (defaults to false)
13698      */
13699     disabled : false,
13700     /**
13701      * @cfg {String} defaultEventAction
13702      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13703      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13704      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13705      */
13706     defaultEventAction: "stopEvent",
13707     /**
13708      * @cfg {Boolean} forceKeyDown
13709      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13710      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13711      * handle keydown instead of keypress.
13712      */
13713     forceKeyDown : false,
13714
13715     // private
13716     prepareEvent : function(e){
13717         var k = e.getKey();
13718         var h = this.keyToHandler[k];
13719         //if(h && this[h]){
13720         //    e.stopPropagation();
13721         //}
13722         if(Roo.isSafari && h && k >= 37 && k <= 40){
13723             e.stopEvent();
13724         }
13725     },
13726
13727     // private
13728     relay : function(e){
13729         var k = e.getKey();
13730         var h = this.keyToHandler[k];
13731         if(h && this[h]){
13732             if(this.doRelay(e, this[h], h) !== true){
13733                 e[this.defaultEventAction]();
13734             }
13735         }
13736     },
13737
13738     // private
13739     doRelay : function(e, h, hname){
13740         return h.call(this.scope || this, e);
13741     },
13742
13743     // possible handlers
13744     enter : false,
13745     left : false,
13746     right : false,
13747     up : false,
13748     down : false,
13749     tab : false,
13750     esc : false,
13751     pageUp : false,
13752     pageDown : false,
13753     del : false,
13754     home : false,
13755     end : false,
13756
13757     // quick lookup hash
13758     keyToHandler : {
13759         37 : "left",
13760         39 : "right",
13761         38 : "up",
13762         40 : "down",
13763         33 : "pageUp",
13764         34 : "pageDown",
13765         46 : "del",
13766         36 : "home",
13767         35 : "end",
13768         13 : "enter",
13769         27 : "esc",
13770         9  : "tab"
13771     },
13772
13773         /**
13774          * Enable this KeyNav
13775          */
13776         enable: function(){
13777                 if(this.disabled){
13778             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13779             // the EventObject will normalize Safari automatically
13780             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.on("keydown", this.relay,  this);
13782             }else{
13783                 this.el.on("keydown", this.prepareEvent,  this);
13784                 this.el.on("keypress", this.relay,  this);
13785             }
13786                     this.disabled = false;
13787                 }
13788         },
13789
13790         /**
13791          * Disable this KeyNav
13792          */
13793         disable: function(){
13794                 if(!this.disabled){
13795                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13796                 this.el.un("keydown", this.relay);
13797             }else{
13798                 this.el.un("keydown", this.prepareEvent);
13799                 this.el.un("keypress", this.relay);
13800             }
13801                     this.disabled = true;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.KeyMap
13818  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13819  * The constructor accepts the same config object as defined by {@link #addBinding}.
13820  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13821  * combination it will call the function with this signature (if the match is a multi-key
13822  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13823  * A KeyMap can also handle a string representation of keys.<br />
13824  * Usage:
13825  <pre><code>
13826 // map one key by key code
13827 var map = new Roo.KeyMap("my-element", {
13828     key: 13, // or Roo.EventObject.ENTER
13829     fn: myHandler,
13830     scope: myObject
13831 });
13832
13833 // map multiple keys to one action by string
13834 var map = new Roo.KeyMap("my-element", {
13835     key: "a\r\n\t",
13836     fn: myHandler,
13837     scope: myObject
13838 });
13839
13840 // map multiple keys to multiple actions by strings and array of codes
13841 var map = new Roo.KeyMap("my-element", [
13842     {
13843         key: [10,13],
13844         fn: function(){ alert("Return was pressed"); }
13845     }, {
13846         key: "abc",
13847         fn: function(){ alert('a, b or c was pressed'); }
13848     }, {
13849         key: "\t",
13850         ctrl:true,
13851         shift:true,
13852         fn: function(){ alert('Control + shift + tab was pressed.'); }
13853     }
13854 ]);
13855 </code></pre>
13856  * <b>Note: A KeyMap starts enabled</b>
13857  * @constructor
13858  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13859  * @param {Object} config The config (see {@link #addBinding})
13860  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13861  */
13862 Roo.KeyMap = function(el, config, eventName){
13863     this.el  = Roo.get(el);
13864     this.eventName = eventName || "keydown";
13865     this.bindings = [];
13866     if(config){
13867         this.addBinding(config);
13868     }
13869     this.enable();
13870 };
13871
13872 Roo.KeyMap.prototype = {
13873     /**
13874      * True to stop the event from bubbling and prevent the default browser action if the
13875      * key was handled by the KeyMap (defaults to false)
13876      * @type Boolean
13877      */
13878     stopEvent : false,
13879
13880     /**
13881      * Add a new binding to this KeyMap. The following config object properties are supported:
13882      * <pre>
13883 Property    Type             Description
13884 ----------  ---------------  ----------------------------------------------------------------------
13885 key         String/Array     A single keycode or an array of keycodes to handle
13886 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13887 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13888 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13889 fn          Function         The function to call when KeyMap finds the expected key combination
13890 scope       Object           The scope of the callback function
13891 </pre>
13892      *
13893      * Usage:
13894      * <pre><code>
13895 // Create a KeyMap
13896 var map = new Roo.KeyMap(document, {
13897     key: Roo.EventObject.ENTER,
13898     fn: handleKey,
13899     scope: this
13900 });
13901
13902 //Add a new binding to the existing KeyMap later
13903 map.addBinding({
13904     key: 'abc',
13905     shift: true,
13906     fn: handleKey,
13907     scope: this
13908 });
13909 </code></pre>
13910      * @param {Object/Array} config A single KeyMap config or an array of configs
13911      */
13912         addBinding : function(config){
13913         if(config instanceof Array){
13914             for(var i = 0, len = config.length; i < len; i++){
13915                 this.addBinding(config[i]);
13916             }
13917             return;
13918         }
13919         var keyCode = config.key,
13920             shift = config.shift, 
13921             ctrl = config.ctrl, 
13922             alt = config.alt,
13923             fn = config.fn,
13924             scope = config.scope;
13925         if(typeof keyCode == "string"){
13926             var ks = [];
13927             var keyString = keyCode.toUpperCase();
13928             for(var j = 0, len = keyString.length; j < len; j++){
13929                 ks.push(keyString.charCodeAt(j));
13930             }
13931             keyCode = ks;
13932         }
13933         var keyArray = keyCode instanceof Array;
13934         var handler = function(e){
13935             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13936                 var k = e.getKey();
13937                 if(keyArray){
13938                     for(var i = 0, len = keyCode.length; i < len; i++){
13939                         if(keyCode[i] == k){
13940                           if(this.stopEvent){
13941                               e.stopEvent();
13942                           }
13943                           fn.call(scope || window, k, e);
13944                           return;
13945                         }
13946                     }
13947                 }else{
13948                     if(k == keyCode){
13949                         if(this.stopEvent){
13950                            e.stopEvent();
13951                         }
13952                         fn.call(scope || window, k, e);
13953                     }
13954                 }
13955             }
13956         };
13957         this.bindings.push(handler);  
13958         },
13959
13960     /**
13961      * Shorthand for adding a single key listener
13962      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13963      * following options:
13964      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13965      * @param {Function} fn The function to call
13966      * @param {Object} scope (optional) The scope of the function
13967      */
13968     on : function(key, fn, scope){
13969         var keyCode, shift, ctrl, alt;
13970         if(typeof key == "object" && !(key instanceof Array)){
13971             keyCode = key.key;
13972             shift = key.shift;
13973             ctrl = key.ctrl;
13974             alt = key.alt;
13975         }else{
13976             keyCode = key;
13977         }
13978         this.addBinding({
13979             key: keyCode,
13980             shift: shift,
13981             ctrl: ctrl,
13982             alt: alt,
13983             fn: fn,
13984             scope: scope
13985         })
13986     },
13987
13988     // private
13989     handleKeyDown : function(e){
13990             if(this.enabled){ //just in case
13991             var b = this.bindings;
13992             for(var i = 0, len = b.length; i < len; i++){
13993                 b[i].call(this, e);
13994             }
13995             }
13996         },
13997         
13998         /**
13999          * Returns true if this KeyMap is enabled
14000          * @return {Boolean} 
14001          */
14002         isEnabled : function(){
14003             return this.enabled;  
14004         },
14005         
14006         /**
14007          * Enables this KeyMap
14008          */
14009         enable: function(){
14010                 if(!this.enabled){
14011                     this.el.on(this.eventName, this.handleKeyDown, this);
14012                     this.enabled = true;
14013                 }
14014         },
14015
14016         /**
14017          * Disable this KeyMap
14018          */
14019         disable: function(){
14020                 if(this.enabled){
14021                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14022                     this.enabled = false;
14023                 }
14024         }
14025 };/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037 /**
14038  * @class Roo.util.TextMetrics
14039  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14040  * wide, in pixels, a given block of text will be.
14041  * @singleton
14042  */
14043 Roo.util.TextMetrics = function(){
14044     var shared;
14045     return {
14046         /**
14047          * Measures the size of the specified text
14048          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14049          * that can affect the size of the rendered text
14050          * @param {String} text The text to measure
14051          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14052          * in order to accurately measure the text height
14053          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14054          */
14055         measure : function(el, text, fixedWidth){
14056             if(!shared){
14057                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14058             }
14059             shared.bind(el);
14060             shared.setFixedWidth(fixedWidth || 'auto');
14061             return shared.getSize(text);
14062         },
14063
14064         /**
14065          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14066          * the overhead of multiple calls to initialize the style properties on each measurement.
14067          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14068          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14069          * in order to accurately measure the text height
14070          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14071          */
14072         createInstance : function(el, fixedWidth){
14073             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14074         }
14075     };
14076 }();
14077
14078  
14079
14080 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14081     var ml = new Roo.Element(document.createElement('div'));
14082     document.body.appendChild(ml.dom);
14083     ml.position('absolute');
14084     ml.setLeftTop(-1000, -1000);
14085     ml.hide();
14086
14087     if(fixedWidth){
14088         ml.setWidth(fixedWidth);
14089     }
14090      
14091     var instance = {
14092         /**
14093          * Returns the size of the specified text based on the internal element's style and width properties
14094          * @memberOf Roo.util.TextMetrics.Instance#
14095          * @param {String} text The text to measure
14096          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14097          */
14098         getSize : function(text){
14099             ml.update(text);
14100             var s = ml.getSize();
14101             ml.update('');
14102             return s;
14103         },
14104
14105         /**
14106          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14107          * that can affect the size of the rendered text
14108          * @memberOf Roo.util.TextMetrics.Instance#
14109          * @param {String/HTMLElement} el The element, dom node or id
14110          */
14111         bind : function(el){
14112             ml.setStyle(
14113                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14114             );
14115         },
14116
14117         /**
14118          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14119          * to set a fixed width in order to accurately measure the text height.
14120          * @memberOf Roo.util.TextMetrics.Instance#
14121          * @param {Number} width The width to set on the element
14122          */
14123         setFixedWidth : function(width){
14124             ml.setWidth(width);
14125         },
14126
14127         /**
14128          * Returns the measured width of the specified text
14129          * @memberOf Roo.util.TextMetrics.Instance#
14130          * @param {String} text The text to measure
14131          * @return {Number} width The width in pixels
14132          */
14133         getWidth : function(text){
14134             ml.dom.style.width = 'auto';
14135             return this.getSize(text).width;
14136         },
14137
14138         /**
14139          * Returns the measured height of the specified text.  For multiline text, be sure to call
14140          * {@link #setFixedWidth} if necessary.
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String} text The text to measure
14143          * @return {Number} height The height in pixels
14144          */
14145         getHeight : function(text){
14146             return this.getSize(text).height;
14147         }
14148     };
14149
14150     instance.bind(bindTo);
14151
14152     return instance;
14153 };
14154
14155 // backwards compat
14156 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14157  * Based on:
14158  * Ext JS Library 1.1.1
14159  * Copyright(c) 2006-2007, Ext JS, LLC.
14160  *
14161  * Originally Released Under LGPL - original licence link has changed is not relivant.
14162  *
14163  * Fork - LGPL
14164  * <script type="text/javascript">
14165  */
14166
14167 /**
14168  * @class Roo.state.Provider
14169  * Abstract base class for state provider implementations. This class provides methods
14170  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14171  * Provider interface.
14172  */
14173 Roo.state.Provider = function(){
14174     /**
14175      * @event statechange
14176      * Fires when a state change occurs.
14177      * @param {Provider} this This state provider
14178      * @param {String} key The state key which was changed
14179      * @param {String} value The encoded value for the state
14180      */
14181     this.addEvents({
14182         "statechange": true
14183     });
14184     this.state = {};
14185     Roo.state.Provider.superclass.constructor.call(this);
14186 };
14187 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14188     /**
14189      * Returns the current value for a key
14190      * @param {String} name The key name
14191      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14192      * @return {Mixed} The state data
14193      */
14194     get : function(name, defaultValue){
14195         return typeof this.state[name] == "undefined" ?
14196             defaultValue : this.state[name];
14197     },
14198     
14199     /**
14200      * Clears a value from the state
14201      * @param {String} name The key name
14202      */
14203     clear : function(name){
14204         delete this.state[name];
14205         this.fireEvent("statechange", this, name, null);
14206     },
14207     
14208     /**
14209      * Sets the value for a key
14210      * @param {String} name The key name
14211      * @param {Mixed} value The value to set
14212      */
14213     set : function(name, value){
14214         this.state[name] = value;
14215         this.fireEvent("statechange", this, name, value);
14216     },
14217     
14218     /**
14219      * Decodes a string previously encoded with {@link #encodeValue}.
14220      * @param {String} value The value to decode
14221      * @return {Mixed} The decoded value
14222      */
14223     decodeValue : function(cookie){
14224         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14225         var matches = re.exec(unescape(cookie));
14226         if(!matches || !matches[1]) return; // non state cookie
14227         var type = matches[1];
14228         var v = matches[2];
14229         switch(type){
14230             case "n":
14231                 return parseFloat(v);
14232             case "d":
14233                 return new Date(Date.parse(v));
14234             case "b":
14235                 return (v == "1");
14236             case "a":
14237                 var all = [];
14238                 var values = v.split("^");
14239                 for(var i = 0, len = values.length; i < len; i++){
14240                     all.push(this.decodeValue(values[i]));
14241                 }
14242                 return all;
14243            case "o":
14244                 var all = {};
14245                 var values = v.split("^");
14246                 for(var i = 0, len = values.length; i < len; i++){
14247                     var kv = values[i].split("=");
14248                     all[kv[0]] = this.decodeValue(kv[1]);
14249                 }
14250                 return all;
14251            default:
14252                 return v;
14253         }
14254     },
14255     
14256     /**
14257      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14258      * @param {Mixed} value The value to encode
14259      * @return {String} The encoded value
14260      */
14261     encodeValue : function(v){
14262         var enc;
14263         if(typeof v == "number"){
14264             enc = "n:" + v;
14265         }else if(typeof v == "boolean"){
14266             enc = "b:" + (v ? "1" : "0");
14267         }else if(v instanceof Date){
14268             enc = "d:" + v.toGMTString();
14269         }else if(v instanceof Array){
14270             var flat = "";
14271             for(var i = 0, len = v.length; i < len; i++){
14272                 flat += this.encodeValue(v[i]);
14273                 if(i != len-1) flat += "^";
14274             }
14275             enc = "a:" + flat;
14276         }else if(typeof v == "object"){
14277             var flat = "";
14278             for(var key in v){
14279                 if(typeof v[key] != "function"){
14280                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14281                 }
14282             }
14283             enc = "o:" + flat.substring(0, flat.length-1);
14284         }else{
14285             enc = "s:" + v;
14286         }
14287         return escape(enc);        
14288     }
14289 });
14290
14291 /*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301 /**
14302  * @class Roo.state.Manager
14303  * This is the global state manager. By default all components that are "state aware" check this class
14304  * for state information if you don't pass them a custom state provider. In order for this class
14305  * to be useful, it must be initialized with a provider when your application initializes.
14306  <pre><code>
14307 // in your initialization function
14308 init : function(){
14309    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14310    ...
14311    // supposed you have a {@link Roo.BorderLayout}
14312    var layout = new Roo.BorderLayout(...);
14313    layout.restoreState();
14314    // or a {Roo.BasicDialog}
14315    var dialog = new Roo.BasicDialog(...);
14316    dialog.restoreState();
14317  </code></pre>
14318  * @singleton
14319  */
14320 Roo.state.Manager = function(){
14321     var provider = new Roo.state.Provider();
14322     
14323     return {
14324         /**
14325          * Configures the default state provider for your application
14326          * @param {Provider} stateProvider The state provider to set
14327          */
14328         setProvider : function(stateProvider){
14329             provider = stateProvider;
14330         },
14331         
14332         /**
14333          * Returns the current value for a key
14334          * @param {String} name The key name
14335          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14336          * @return {Mixed} The state data
14337          */
14338         get : function(key, defaultValue){
14339             return provider.get(key, defaultValue);
14340         },
14341         
14342         /**
14343          * Sets the value for a key
14344          * @param {String} name The key name
14345          * @param {Mixed} value The state data
14346          */
14347          set : function(key, value){
14348             provider.set(key, value);
14349         },
14350         
14351         /**
14352          * Clears a value from the state
14353          * @param {String} name The key name
14354          */
14355         clear : function(key){
14356             provider.clear(key);
14357         },
14358         
14359         /**
14360          * Gets the currently configured state provider
14361          * @return {Provider} The state provider
14362          */
14363         getProvider : function(){
14364             return provider;
14365         }
14366     };
14367 }();
14368 /*
14369  * Based on:
14370  * Ext JS Library 1.1.1
14371  * Copyright(c) 2006-2007, Ext JS, LLC.
14372  *
14373  * Originally Released Under LGPL - original licence link has changed is not relivant.
14374  *
14375  * Fork - LGPL
14376  * <script type="text/javascript">
14377  */
14378 /**
14379  * @class Roo.state.CookieProvider
14380  * @extends Roo.state.Provider
14381  * The default Provider implementation which saves state via cookies.
14382  * <br />Usage:
14383  <pre><code>
14384    var cp = new Roo.state.CookieProvider({
14385        path: "/cgi-bin/",
14386        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14387        domain: "roojs.com"
14388    })
14389    Roo.state.Manager.setProvider(cp);
14390  </code></pre>
14391  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14392  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14393  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14394  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14395  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14396  * domain the page is running on including the 'www' like 'www.roojs.com')
14397  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14398  * @constructor
14399  * Create a new CookieProvider
14400  * @param {Object} config The configuration object
14401  */
14402 Roo.state.CookieProvider = function(config){
14403     Roo.state.CookieProvider.superclass.constructor.call(this);
14404     this.path = "/";
14405     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14406     this.domain = null;
14407     this.secure = false;
14408     Roo.apply(this, config);
14409     this.state = this.readCookies();
14410 };
14411
14412 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14413     // private
14414     set : function(name, value){
14415         if(typeof value == "undefined" || value === null){
14416             this.clear(name);
14417             return;
14418         }
14419         this.setCookie(name, value);
14420         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14421     },
14422
14423     // private
14424     clear : function(name){
14425         this.clearCookie(name);
14426         Roo.state.CookieProvider.superclass.clear.call(this, name);
14427     },
14428
14429     // private
14430     readCookies : function(){
14431         var cookies = {};
14432         var c = document.cookie + ";";
14433         var re = /\s?(.*?)=(.*?);/g;
14434         var matches;
14435         while((matches = re.exec(c)) != null){
14436             var name = matches[1];
14437             var value = matches[2];
14438             if(name && name.substring(0,3) == "ys-"){
14439                 cookies[name.substr(3)] = this.decodeValue(value);
14440             }
14441         }
14442         return cookies;
14443     },
14444
14445     // private
14446     setCookie : function(name, value){
14447         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14448            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14449            ((this.path == null) ? "" : ("; path=" + this.path)) +
14450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14451            ((this.secure == true) ? "; secure" : "");
14452     },
14453
14454     // private
14455     clearCookie : function(name){
14456         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14457            ((this.path == null) ? "" : ("; path=" + this.path)) +
14458            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14459            ((this.secure == true) ? "; secure" : "");
14460     }
14461 });/*
14462  * Based on:
14463  * Ext JS Library 1.1.1
14464  * Copyright(c) 2006-2007, Ext JS, LLC.
14465  *
14466  * Originally Released Under LGPL - original licence link has changed is not relivant.
14467  *
14468  * Fork - LGPL
14469  * <script type="text/javascript">
14470  */
14471
14472
14473
14474 /*
14475  * These classes are derivatives of the similarly named classes in the YUI Library.
14476  * The original license:
14477  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14478  * Code licensed under the BSD License:
14479  * http://developer.yahoo.net/yui/license.txt
14480  */
14481
14482 (function() {
14483
14484 var Event=Roo.EventManager;
14485 var Dom=Roo.lib.Dom;
14486
14487 /**
14488  * @class Roo.dd.DragDrop
14489  * @extends Roo.util.Observable
14490  * Defines the interface and base operation of items that that can be
14491  * dragged or can be drop targets.  It was designed to be extended, overriding
14492  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14493  * Up to three html elements can be associated with a DragDrop instance:
14494  * <ul>
14495  * <li>linked element: the element that is passed into the constructor.
14496  * This is the element which defines the boundaries for interaction with
14497  * other DragDrop objects.</li>
14498  * <li>handle element(s): The drag operation only occurs if the element that
14499  * was clicked matches a handle element.  By default this is the linked
14500  * element, but there are times that you will want only a portion of the
14501  * linked element to initiate the drag operation, and the setHandleElId()
14502  * method provides a way to define this.</li>
14503  * <li>drag element: this represents the element that would be moved along
14504  * with the cursor during a drag operation.  By default, this is the linked
14505  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14506  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14507  * </li>
14508  * </ul>
14509  * This class should not be instantiated until the onload event to ensure that
14510  * the associated elements are available.
14511  * The following would define a DragDrop obj that would interact with any
14512  * other DragDrop obj in the "group1" group:
14513  * <pre>
14514  *  dd = new Roo.dd.DragDrop("div1", "group1");
14515  * </pre>
14516  * Since none of the event handlers have been implemented, nothing would
14517  * actually happen if you were to run the code above.  Normally you would
14518  * override this class or one of the default implementations, but you can
14519  * also override the methods you want on an instance of the class...
14520  * <pre>
14521  *  dd.onDragDrop = function(e, id) {
14522  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14523  *  }
14524  * </pre>
14525  * @constructor
14526  * @param {String} id of the element that is linked to this instance
14527  * @param {String} sGroup the group of related DragDrop objects
14528  * @param {object} config an object containing configurable attributes
14529  *                Valid properties for DragDrop:
14530  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14531  */
14532 Roo.dd.DragDrop = function(id, sGroup, config) {
14533     if (id) {
14534         this.init(id, sGroup, config);
14535     }
14536     
14537 };
14538
14539 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14540
14541     /**
14542      * The id of the element associated with this object.  This is what we
14543      * refer to as the "linked element" because the size and position of
14544      * this element is used to determine when the drag and drop objects have
14545      * interacted.
14546      * @property id
14547      * @type String
14548      */
14549     id: null,
14550
14551     /**
14552      * Configuration attributes passed into the constructor
14553      * @property config
14554      * @type object
14555      */
14556     config: null,
14557
14558     /**
14559      * The id of the element that will be dragged.  By default this is same
14560      * as the linked element , but could be changed to another element. Ex:
14561      * Roo.dd.DDProxy
14562      * @property dragElId
14563      * @type String
14564      * @private
14565      */
14566     dragElId: null,
14567
14568     /**
14569      * the id of the element that initiates the drag operation.  By default
14570      * this is the linked element, but could be changed to be a child of this
14571      * element.  This lets us do things like only starting the drag when the
14572      * header element within the linked html element is clicked.
14573      * @property handleElId
14574      * @type String
14575      * @private
14576      */
14577     handleElId: null,
14578
14579     /**
14580      * An associative array of HTML tags that will be ignored if clicked.
14581      * @property invalidHandleTypes
14582      * @type {string: string}
14583      */
14584     invalidHandleTypes: null,
14585
14586     /**
14587      * An associative array of ids for elements that will be ignored if clicked
14588      * @property invalidHandleIds
14589      * @type {string: string}
14590      */
14591     invalidHandleIds: null,
14592
14593     /**
14594      * An indexted array of css class names for elements that will be ignored
14595      * if clicked.
14596      * @property invalidHandleClasses
14597      * @type string[]
14598      */
14599     invalidHandleClasses: null,
14600
14601     /**
14602      * The linked element's absolute X position at the time the drag was
14603      * started
14604      * @property startPageX
14605      * @type int
14606      * @private
14607      */
14608     startPageX: 0,
14609
14610     /**
14611      * The linked element's absolute X position at the time the drag was
14612      * started
14613      * @property startPageY
14614      * @type int
14615      * @private
14616      */
14617     startPageY: 0,
14618
14619     /**
14620      * The group defines a logical collection of DragDrop objects that are
14621      * related.  Instances only get events when interacting with other
14622      * DragDrop object in the same group.  This lets us define multiple
14623      * groups using a single DragDrop subclass if we want.
14624      * @property groups
14625      * @type {string: string}
14626      */
14627     groups: null,
14628
14629     /**
14630      * Individual drag/drop instances can be locked.  This will prevent
14631      * onmousedown start drag.
14632      * @property locked
14633      * @type boolean
14634      * @private
14635      */
14636     locked: false,
14637
14638     /**
14639      * Lock this instance
14640      * @method lock
14641      */
14642     lock: function() { this.locked = true; },
14643
14644     /**
14645      * Unlock this instace
14646      * @method unlock
14647      */
14648     unlock: function() { this.locked = false; },
14649
14650     /**
14651      * By default, all insances can be a drop target.  This can be disabled by
14652      * setting isTarget to false.
14653      * @method isTarget
14654      * @type boolean
14655      */
14656     isTarget: true,
14657
14658     /**
14659      * The padding configured for this drag and drop object for calculating
14660      * the drop zone intersection with this object.
14661      * @method padding
14662      * @type int[]
14663      */
14664     padding: null,
14665
14666     /**
14667      * Cached reference to the linked element
14668      * @property _domRef
14669      * @private
14670      */
14671     _domRef: null,
14672
14673     /**
14674      * Internal typeof flag
14675      * @property __ygDragDrop
14676      * @private
14677      */
14678     __ygDragDrop: true,
14679
14680     /**
14681      * Set to true when horizontal contraints are applied
14682      * @property constrainX
14683      * @type boolean
14684      * @private
14685      */
14686     constrainX: false,
14687
14688     /**
14689      * Set to true when vertical contraints are applied
14690      * @property constrainY
14691      * @type boolean
14692      * @private
14693      */
14694     constrainY: false,
14695
14696     /**
14697      * The left constraint
14698      * @property minX
14699      * @type int
14700      * @private
14701      */
14702     minX: 0,
14703
14704     /**
14705      * The right constraint
14706      * @property maxX
14707      * @type int
14708      * @private
14709      */
14710     maxX: 0,
14711
14712     /**
14713      * The up constraint
14714      * @property minY
14715      * @type int
14716      * @type int
14717      * @private
14718      */
14719     minY: 0,
14720
14721     /**
14722      * The down constraint
14723      * @property maxY
14724      * @type int
14725      * @private
14726      */
14727     maxY: 0,
14728
14729     /**
14730      * Maintain offsets when we resetconstraints.  Set to true when you want
14731      * the position of the element relative to its parent to stay the same
14732      * when the page changes
14733      *
14734      * @property maintainOffset
14735      * @type boolean
14736      */
14737     maintainOffset: false,
14738
14739     /**
14740      * Array of pixel locations the element will snap to if we specified a
14741      * horizontal graduation/interval.  This array is generated automatically
14742      * when you define a tick interval.
14743      * @property xTicks
14744      * @type int[]
14745      */
14746     xTicks: null,
14747
14748     /**
14749      * Array of pixel locations the element will snap to if we specified a
14750      * vertical graduation/interval.  This array is generated automatically
14751      * when you define a tick interval.
14752      * @property yTicks
14753      * @type int[]
14754      */
14755     yTicks: null,
14756
14757     /**
14758      * By default the drag and drop instance will only respond to the primary
14759      * button click (left button for a right-handed mouse).  Set to true to
14760      * allow drag and drop to start with any mouse click that is propogated
14761      * by the browser
14762      * @property primaryButtonOnly
14763      * @type boolean
14764      */
14765     primaryButtonOnly: true,
14766
14767     /**
14768      * The availabe property is false until the linked dom element is accessible.
14769      * @property available
14770      * @type boolean
14771      */
14772     available: false,
14773
14774     /**
14775      * By default, drags can only be initiated if the mousedown occurs in the
14776      * region the linked element is.  This is done in part to work around a
14777      * bug in some browsers that mis-report the mousedown if the previous
14778      * mouseup happened outside of the window.  This property is set to true
14779      * if outer handles are defined.
14780      *
14781      * @property hasOuterHandles
14782      * @type boolean
14783      * @default false
14784      */
14785     hasOuterHandles: false,
14786
14787     /**
14788      * Code that executes immediately before the startDrag event
14789      * @method b4StartDrag
14790      * @private
14791      */
14792     b4StartDrag: function(x, y) { },
14793
14794     /**
14795      * Abstract method called after a drag/drop object is clicked
14796      * and the drag or mousedown time thresholds have beeen met.
14797      * @method startDrag
14798      * @param {int} X click location
14799      * @param {int} Y click location
14800      */
14801     startDrag: function(x, y) { /* override this */ },
14802
14803     /**
14804      * Code that executes immediately before the onDrag event
14805      * @method b4Drag
14806      * @private
14807      */
14808     b4Drag: function(e) { },
14809
14810     /**
14811      * Abstract method called during the onMouseMove event while dragging an
14812      * object.
14813      * @method onDrag
14814      * @param {Event} e the mousemove event
14815      */
14816     onDrag: function(e) { /* override this */ },
14817
14818     /**
14819      * Abstract method called when this element fist begins hovering over
14820      * another DragDrop obj
14821      * @method onDragEnter
14822      * @param {Event} e the mousemove event
14823      * @param {String|DragDrop[]} id In POINT mode, the element
14824      * id this is hovering over.  In INTERSECT mode, an array of one or more
14825      * dragdrop items being hovered over.
14826      */
14827     onDragEnter: function(e, id) { /* override this */ },
14828
14829     /**
14830      * Code that executes immediately before the onDragOver event
14831      * @method b4DragOver
14832      * @private
14833      */
14834     b4DragOver: function(e) { },
14835
14836     /**
14837      * Abstract method called when this element is hovering over another
14838      * DragDrop obj
14839      * @method onDragOver
14840      * @param {Event} e the mousemove event
14841      * @param {String|DragDrop[]} id In POINT mode, the element
14842      * id this is hovering over.  In INTERSECT mode, an array of dd items
14843      * being hovered over.
14844      */
14845     onDragOver: function(e, id) { /* override this */ },
14846
14847     /**
14848      * Code that executes immediately before the onDragOut event
14849      * @method b4DragOut
14850      * @private
14851      */
14852     b4DragOut: function(e) { },
14853
14854     /**
14855      * Abstract method called when we are no longer hovering over an element
14856      * @method onDragOut
14857      * @param {Event} e the mousemove event
14858      * @param {String|DragDrop[]} id In POINT mode, the element
14859      * id this was hovering over.  In INTERSECT mode, an array of dd items
14860      * that the mouse is no longer over.
14861      */
14862     onDragOut: function(e, id) { /* override this */ },
14863
14864     /**
14865      * Code that executes immediately before the onDragDrop event
14866      * @method b4DragDrop
14867      * @private
14868      */
14869     b4DragDrop: function(e) { },
14870
14871     /**
14872      * Abstract method called when this item is dropped on another DragDrop
14873      * obj
14874      * @method onDragDrop
14875      * @param {Event} e the mouseup event
14876      * @param {String|DragDrop[]} id In POINT mode, the element
14877      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14878      * was dropped on.
14879      */
14880     onDragDrop: function(e, id) { /* override this */ },
14881
14882     /**
14883      * Abstract method called when this item is dropped on an area with no
14884      * drop target
14885      * @method onInvalidDrop
14886      * @param {Event} e the mouseup event
14887      */
14888     onInvalidDrop: function(e) { /* override this */ },
14889
14890     /**
14891      * Code that executes immediately before the endDrag event
14892      * @method b4EndDrag
14893      * @private
14894      */
14895     b4EndDrag: function(e) { },
14896
14897     /**
14898      * Fired when we are done dragging the object
14899      * @method endDrag
14900      * @param {Event} e the mouseup event
14901      */
14902     endDrag: function(e) { /* override this */ },
14903
14904     /**
14905      * Code executed immediately before the onMouseDown event
14906      * @method b4MouseDown
14907      * @param {Event} e the mousedown event
14908      * @private
14909      */
14910     b4MouseDown: function(e) {  },
14911
14912     /**
14913      * Event handler that fires when a drag/drop obj gets a mousedown
14914      * @method onMouseDown
14915      * @param {Event} e the mousedown event
14916      */
14917     onMouseDown: function(e) { /* override this */ },
14918
14919     /**
14920      * Event handler that fires when a drag/drop obj gets a mouseup
14921      * @method onMouseUp
14922      * @param {Event} e the mouseup event
14923      */
14924     onMouseUp: function(e) { /* override this */ },
14925
14926     /**
14927      * Override the onAvailable method to do what is needed after the initial
14928      * position was determined.
14929      * @method onAvailable
14930      */
14931     onAvailable: function () {
14932     },
14933
14934     /*
14935      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14936      * @type Object
14937      */
14938     defaultPadding : {left:0, right:0, top:0, bottom:0},
14939
14940     /*
14941      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14942  *
14943  * Usage:
14944  <pre><code>
14945  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14946                 { dragElId: "existingProxyDiv" });
14947  dd.startDrag = function(){
14948      this.constrainTo("parent-id");
14949  };
14950  </code></pre>
14951  * Or you can initalize it using the {@link Roo.Element} object:
14952  <pre><code>
14953  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14954      startDrag : function(){
14955          this.constrainTo("parent-id");
14956      }
14957  });
14958  </code></pre>
14959      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14960      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14961      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14962      * an object containing the sides to pad. For example: {right:10, bottom:10}
14963      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14964      */
14965     constrainTo : function(constrainTo, pad, inContent){
14966         if(typeof pad == "number"){
14967             pad = {left: pad, right:pad, top:pad, bottom:pad};
14968         }
14969         pad = pad || this.defaultPadding;
14970         var b = Roo.get(this.getEl()).getBox();
14971         var ce = Roo.get(constrainTo);
14972         var s = ce.getScroll();
14973         var c, cd = ce.dom;
14974         if(cd == document.body){
14975             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14976         }else{
14977             xy = ce.getXY();
14978             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14979         }
14980
14981
14982         var topSpace = b.y - c.y;
14983         var leftSpace = b.x - c.x;
14984
14985         this.resetConstraints();
14986         this.setXConstraint(leftSpace - (pad.left||0), // left
14987                 c.width - leftSpace - b.width - (pad.right||0) //right
14988         );
14989         this.setYConstraint(topSpace - (pad.top||0), //top
14990                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14991         );
14992     },
14993
14994     /**
14995      * Returns a reference to the linked element
14996      * @method getEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getEl: function() {
15000         if (!this._domRef) {
15001             this._domRef = Roo.getDom(this.id);
15002         }
15003
15004         return this._domRef;
15005     },
15006
15007     /**
15008      * Returns a reference to the actual element to drag.  By default this is
15009      * the same as the html element, but it can be assigned to another
15010      * element. An example of this can be found in Roo.dd.DDProxy
15011      * @method getDragEl
15012      * @return {HTMLElement} the html element
15013      */
15014     getDragEl: function() {
15015         return Roo.getDom(this.dragElId);
15016     },
15017
15018     /**
15019      * Sets up the DragDrop object.  Must be called in the constructor of any
15020      * Roo.dd.DragDrop subclass
15021      * @method init
15022      * @param id the id of the linked element
15023      * @param {String} sGroup the group of related items
15024      * @param {object} config configuration attributes
15025      */
15026     init: function(id, sGroup, config) {
15027         this.initTarget(id, sGroup, config);
15028         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15029         // Event.on(this.id, "selectstart", Event.preventDefault);
15030     },
15031
15032     /**
15033      * Initializes Targeting functionality only... the object does not
15034      * get a mousedown handler.
15035      * @method initTarget
15036      * @param id the id of the linked element
15037      * @param {String} sGroup the group of related items
15038      * @param {object} config configuration attributes
15039      */
15040     initTarget: function(id, sGroup, config) {
15041
15042         // configuration attributes
15043         this.config = config || {};
15044
15045         // create a local reference to the drag and drop manager
15046         this.DDM = Roo.dd.DDM;
15047         // initialize the groups array
15048         this.groups = {};
15049
15050         // assume that we have an element reference instead of an id if the
15051         // parameter is not a string
15052         if (typeof id !== "string") {
15053             id = Roo.id(id);
15054         }
15055
15056         // set the id
15057         this.id = id;
15058
15059         // add to an interaction group
15060         this.addToGroup((sGroup) ? sGroup : "default");
15061
15062         // We don't want to register this as the handle with the manager
15063         // so we just set the id rather than calling the setter.
15064         this.handleElId = id;
15065
15066         // the linked element is the element that gets dragged by default
15067         this.setDragElId(id);
15068
15069         // by default, clicked anchors will not start drag operations.
15070         this.invalidHandleTypes = { A: "A" };
15071         this.invalidHandleIds = {};
15072         this.invalidHandleClasses = [];
15073
15074         this.applyConfig();
15075
15076         this.handleOnAvailable();
15077     },
15078
15079     /**
15080      * Applies the configuration parameters that were passed into the constructor.
15081      * This is supposed to happen at each level through the inheritance chain.  So
15082      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15083      * DragDrop in order to get all of the parameters that are available in
15084      * each object.
15085      * @method applyConfig
15086      */
15087     applyConfig: function() {
15088
15089         // configurable properties:
15090         //    padding, isTarget, maintainOffset, primaryButtonOnly
15091         this.padding           = this.config.padding || [0, 0, 0, 0];
15092         this.isTarget          = (this.config.isTarget !== false);
15093         this.maintainOffset    = (this.config.maintainOffset);
15094         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15095
15096     },
15097
15098     /**
15099      * Executed when the linked element is available
15100      * @method handleOnAvailable
15101      * @private
15102      */
15103     handleOnAvailable: function() {
15104         this.available = true;
15105         this.resetConstraints();
15106         this.onAvailable();
15107     },
15108
15109      /**
15110      * Configures the padding for the target zone in px.  Effectively expands
15111      * (or reduces) the virtual object size for targeting calculations.
15112      * Supports css-style shorthand; if only one parameter is passed, all sides
15113      * will have that padding, and if only two are passed, the top and bottom
15114      * will have the first param, the left and right the second.
15115      * @method setPadding
15116      * @param {int} iTop    Top pad
15117      * @param {int} iRight  Right pad
15118      * @param {int} iBot    Bot pad
15119      * @param {int} iLeft   Left pad
15120      */
15121     setPadding: function(iTop, iRight, iBot, iLeft) {
15122         // this.padding = [iLeft, iRight, iTop, iBot];
15123         if (!iRight && 0 !== iRight) {
15124             this.padding = [iTop, iTop, iTop, iTop];
15125         } else if (!iBot && 0 !== iBot) {
15126             this.padding = [iTop, iRight, iTop, iRight];
15127         } else {
15128             this.padding = [iTop, iRight, iBot, iLeft];
15129         }
15130     },
15131
15132     /**
15133      * Stores the initial placement of the linked element.
15134      * @method setInitialPosition
15135      * @param {int} diffX   the X offset, default 0
15136      * @param {int} diffY   the Y offset, default 0
15137      */
15138     setInitPosition: function(diffX, diffY) {
15139         var el = this.getEl();
15140
15141         if (!this.DDM.verifyEl(el)) {
15142             return;
15143         }
15144
15145         var dx = diffX || 0;
15146         var dy = diffY || 0;
15147
15148         var p = Dom.getXY( el );
15149
15150         this.initPageX = p[0] - dx;
15151         this.initPageY = p[1] - dy;
15152
15153         this.lastPageX = p[0];
15154         this.lastPageY = p[1];
15155
15156
15157         this.setStartPosition(p);
15158     },
15159
15160     /**
15161      * Sets the start position of the element.  This is set when the obj
15162      * is initialized, the reset when a drag is started.
15163      * @method setStartPosition
15164      * @param pos current position (from previous lookup)
15165      * @private
15166      */
15167     setStartPosition: function(pos) {
15168         var p = pos || Dom.getXY( this.getEl() );
15169         this.deltaSetXY = null;
15170
15171         this.startPageX = p[0];
15172         this.startPageY = p[1];
15173     },
15174
15175     /**
15176      * Add this instance to a group of related drag/drop objects.  All
15177      * instances belong to at least one group, and can belong to as many
15178      * groups as needed.
15179      * @method addToGroup
15180      * @param sGroup {string} the name of the group
15181      */
15182     addToGroup: function(sGroup) {
15183         this.groups[sGroup] = true;
15184         this.DDM.regDragDrop(this, sGroup);
15185     },
15186
15187     /**
15188      * Remove's this instance from the supplied interaction group
15189      * @method removeFromGroup
15190      * @param {string}  sGroup  The group to drop
15191      */
15192     removeFromGroup: function(sGroup) {
15193         if (this.groups[sGroup]) {
15194             delete this.groups[sGroup];
15195         }
15196
15197         this.DDM.removeDDFromGroup(this, sGroup);
15198     },
15199
15200     /**
15201      * Allows you to specify that an element other than the linked element
15202      * will be moved with the cursor during a drag
15203      * @method setDragElId
15204      * @param id {string} the id of the element that will be used to initiate the drag
15205      */
15206     setDragElId: function(id) {
15207         this.dragElId = id;
15208     },
15209
15210     /**
15211      * Allows you to specify a child of the linked element that should be
15212      * used to initiate the drag operation.  An example of this would be if
15213      * you have a content div with text and links.  Clicking anywhere in the
15214      * content area would normally start the drag operation.  Use this method
15215      * to specify that an element inside of the content div is the element
15216      * that starts the drag operation.
15217      * @method setHandleElId
15218      * @param id {string} the id of the element that will be used to
15219      * initiate the drag.
15220      */
15221     setHandleElId: function(id) {
15222         if (typeof id !== "string") {
15223             id = Roo.id(id);
15224         }
15225         this.handleElId = id;
15226         this.DDM.regHandle(this.id, id);
15227     },
15228
15229     /**
15230      * Allows you to set an element outside of the linked element as a drag
15231      * handle
15232      * @method setOuterHandleElId
15233      * @param id the id of the element that will be used to initiate the drag
15234      */
15235     setOuterHandleElId: function(id) {
15236         if (typeof id !== "string") {
15237             id = Roo.id(id);
15238         }
15239         Event.on(id, "mousedown",
15240                 this.handleMouseDown, this);
15241         this.setHandleElId(id);
15242
15243         this.hasOuterHandles = true;
15244     },
15245
15246     /**
15247      * Remove all drag and drop hooks for this element
15248      * @method unreg
15249      */
15250     unreg: function() {
15251         Event.un(this.id, "mousedown",
15252                 this.handleMouseDown);
15253         this._domRef = null;
15254         this.DDM._remove(this);
15255     },
15256
15257     destroy : function(){
15258         this.unreg();
15259     },
15260
15261     /**
15262      * Returns true if this instance is locked, or the drag drop mgr is locked
15263      * (meaning that all drag/drop is disabled on the page.)
15264      * @method isLocked
15265      * @return {boolean} true if this obj or all drag/drop is locked, else
15266      * false
15267      */
15268     isLocked: function() {
15269         return (this.DDM.isLocked() || this.locked);
15270     },
15271
15272     /**
15273      * Fired when this object is clicked
15274      * @method handleMouseDown
15275      * @param {Event} e
15276      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15277      * @private
15278      */
15279     handleMouseDown: function(e, oDD){
15280         if (this.primaryButtonOnly && e.button != 0) {
15281             return;
15282         }
15283
15284         if (this.isLocked()) {
15285             return;
15286         }
15287
15288         this.DDM.refreshCache(this.groups);
15289
15290         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15291         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15292         } else {
15293             if (this.clickValidator(e)) {
15294
15295                 // set the initial element position
15296                 this.setStartPosition();
15297
15298
15299                 this.b4MouseDown(e);
15300                 this.onMouseDown(e);
15301
15302                 this.DDM.handleMouseDown(e, this);
15303
15304                 this.DDM.stopEvent(e);
15305             } else {
15306
15307
15308             }
15309         }
15310     },
15311
15312     clickValidator: function(e) {
15313         var target = e.getTarget();
15314         return ( this.isValidHandleChild(target) &&
15315                     (this.id == this.handleElId ||
15316                         this.DDM.handleWasClicked(target, this.id)) );
15317     },
15318
15319     /**
15320      * Allows you to specify a tag name that should not start a drag operation
15321      * when clicked.  This is designed to facilitate embedding links within a
15322      * drag handle that do something other than start the drag.
15323      * @method addInvalidHandleType
15324      * @param {string} tagName the type of element to exclude
15325      */
15326     addInvalidHandleType: function(tagName) {
15327         var type = tagName.toUpperCase();
15328         this.invalidHandleTypes[type] = type;
15329     },
15330
15331     /**
15332      * Lets you to specify an element id for a child of a drag handle
15333      * that should not initiate a drag
15334      * @method addInvalidHandleId
15335      * @param {string} id the element id of the element you wish to ignore
15336      */
15337     addInvalidHandleId: function(id) {
15338         if (typeof id !== "string") {
15339             id = Roo.id(id);
15340         }
15341         this.invalidHandleIds[id] = id;
15342     },
15343
15344     /**
15345      * Lets you specify a css class of elements that will not initiate a drag
15346      * @method addInvalidHandleClass
15347      * @param {string} cssClass the class of the elements you wish to ignore
15348      */
15349     addInvalidHandleClass: function(cssClass) {
15350         this.invalidHandleClasses.push(cssClass);
15351     },
15352
15353     /**
15354      * Unsets an excluded tag name set by addInvalidHandleType
15355      * @method removeInvalidHandleType
15356      * @param {string} tagName the type of element to unexclude
15357      */
15358     removeInvalidHandleType: function(tagName) {
15359         var type = tagName.toUpperCase();
15360         // this.invalidHandleTypes[type] = null;
15361         delete this.invalidHandleTypes[type];
15362     },
15363
15364     /**
15365      * Unsets an invalid handle id
15366      * @method removeInvalidHandleId
15367      * @param {string} id the id of the element to re-enable
15368      */
15369     removeInvalidHandleId: function(id) {
15370         if (typeof id !== "string") {
15371             id = Roo.id(id);
15372         }
15373         delete this.invalidHandleIds[id];
15374     },
15375
15376     /**
15377      * Unsets an invalid css class
15378      * @method removeInvalidHandleClass
15379      * @param {string} cssClass the class of the element(s) you wish to
15380      * re-enable
15381      */
15382     removeInvalidHandleClass: function(cssClass) {
15383         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15384             if (this.invalidHandleClasses[i] == cssClass) {
15385                 delete this.invalidHandleClasses[i];
15386             }
15387         }
15388     },
15389
15390     /**
15391      * Checks the tag exclusion list to see if this click should be ignored
15392      * @method isValidHandleChild
15393      * @param {HTMLElement} node the HTMLElement to evaluate
15394      * @return {boolean} true if this is a valid tag type, false if not
15395      */
15396     isValidHandleChild: function(node) {
15397
15398         var valid = true;
15399         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15400         var nodeName;
15401         try {
15402             nodeName = node.nodeName.toUpperCase();
15403         } catch(e) {
15404             nodeName = node.nodeName;
15405         }
15406         valid = valid && !this.invalidHandleTypes[nodeName];
15407         valid = valid && !this.invalidHandleIds[node.id];
15408
15409         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15410             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15411         }
15412
15413
15414         return valid;
15415
15416     },
15417
15418     /**
15419      * Create the array of horizontal tick marks if an interval was specified
15420      * in setXConstraint().
15421      * @method setXTicks
15422      * @private
15423      */
15424     setXTicks: function(iStartX, iTickSize) {
15425         this.xTicks = [];
15426         this.xTickSize = iTickSize;
15427
15428         var tickMap = {};
15429
15430         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15431             if (!tickMap[i]) {
15432                 this.xTicks[this.xTicks.length] = i;
15433                 tickMap[i] = true;
15434             }
15435         }
15436
15437         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15438             if (!tickMap[i]) {
15439                 this.xTicks[this.xTicks.length] = i;
15440                 tickMap[i] = true;
15441             }
15442         }
15443
15444         this.xTicks.sort(this.DDM.numericSort) ;
15445     },
15446
15447     /**
15448      * Create the array of vertical tick marks if an interval was specified in
15449      * setYConstraint().
15450      * @method setYTicks
15451      * @private
15452      */
15453     setYTicks: function(iStartY, iTickSize) {
15454         this.yTicks = [];
15455         this.yTickSize = iTickSize;
15456
15457         var tickMap = {};
15458
15459         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15460             if (!tickMap[i]) {
15461                 this.yTicks[this.yTicks.length] = i;
15462                 tickMap[i] = true;
15463             }
15464         }
15465
15466         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15467             if (!tickMap[i]) {
15468                 this.yTicks[this.yTicks.length] = i;
15469                 tickMap[i] = true;
15470             }
15471         }
15472
15473         this.yTicks.sort(this.DDM.numericSort) ;
15474     },
15475
15476     /**
15477      * By default, the element can be dragged any place on the screen.  Use
15478      * this method to limit the horizontal travel of the element.  Pass in
15479      * 0,0 for the parameters if you want to lock the drag to the y axis.
15480      * @method setXConstraint
15481      * @param {int} iLeft the number of pixels the element can move to the left
15482      * @param {int} iRight the number of pixels the element can move to the
15483      * right
15484      * @param {int} iTickSize optional parameter for specifying that the
15485      * element
15486      * should move iTickSize pixels at a time.
15487      */
15488     setXConstraint: function(iLeft, iRight, iTickSize) {
15489         this.leftConstraint = iLeft;
15490         this.rightConstraint = iRight;
15491
15492         this.minX = this.initPageX - iLeft;
15493         this.maxX = this.initPageX + iRight;
15494         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15495
15496         this.constrainX = true;
15497     },
15498
15499     /**
15500      * Clears any constraints applied to this instance.  Also clears ticks
15501      * since they can't exist independent of a constraint at this time.
15502      * @method clearConstraints
15503      */
15504     clearConstraints: function() {
15505         this.constrainX = false;
15506         this.constrainY = false;
15507         this.clearTicks();
15508     },
15509
15510     /**
15511      * Clears any tick interval defined for this instance
15512      * @method clearTicks
15513      */
15514     clearTicks: function() {
15515         this.xTicks = null;
15516         this.yTicks = null;
15517         this.xTickSize = 0;
15518         this.yTickSize = 0;
15519     },
15520
15521     /**
15522      * By default, the element can be dragged any place on the screen.  Set
15523      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15524      * parameters if you want to lock the drag to the x axis.
15525      * @method setYConstraint
15526      * @param {int} iUp the number of pixels the element can move up
15527      * @param {int} iDown the number of pixels the element can move down
15528      * @param {int} iTickSize optional parameter for specifying that the
15529      * element should move iTickSize pixels at a time.
15530      */
15531     setYConstraint: function(iUp, iDown, iTickSize) {
15532         this.topConstraint = iUp;
15533         this.bottomConstraint = iDown;
15534
15535         this.minY = this.initPageY - iUp;
15536         this.maxY = this.initPageY + iDown;
15537         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15538
15539         this.constrainY = true;
15540
15541     },
15542
15543     /**
15544      * resetConstraints must be called if you manually reposition a dd element.
15545      * @method resetConstraints
15546      * @param {boolean} maintainOffset
15547      */
15548     resetConstraints: function() {
15549
15550
15551         // Maintain offsets if necessary
15552         if (this.initPageX || this.initPageX === 0) {
15553             // figure out how much this thing has moved
15554             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15555             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15556
15557             this.setInitPosition(dx, dy);
15558
15559         // This is the first time we have detected the element's position
15560         } else {
15561             this.setInitPosition();
15562         }
15563
15564         if (this.constrainX) {
15565             this.setXConstraint( this.leftConstraint,
15566                                  this.rightConstraint,
15567                                  this.xTickSize        );
15568         }
15569
15570         if (this.constrainY) {
15571             this.setYConstraint( this.topConstraint,
15572                                  this.bottomConstraint,
15573                                  this.yTickSize         );
15574         }
15575     },
15576
15577     /**
15578      * Normally the drag element is moved pixel by pixel, but we can specify
15579      * that it move a number of pixels at a time.  This method resolves the
15580      * location when we have it set up like this.
15581      * @method getTick
15582      * @param {int} val where we want to place the object
15583      * @param {int[]} tickArray sorted array of valid points
15584      * @return {int} the closest tick
15585      * @private
15586      */
15587     getTick: function(val, tickArray) {
15588
15589         if (!tickArray) {
15590             // If tick interval is not defined, it is effectively 1 pixel,
15591             // so we return the value passed to us.
15592             return val;
15593         } else if (tickArray[0] >= val) {
15594             // The value is lower than the first tick, so we return the first
15595             // tick.
15596             return tickArray[0];
15597         } else {
15598             for (var i=0, len=tickArray.length; i<len; ++i) {
15599                 var next = i + 1;
15600                 if (tickArray[next] && tickArray[next] >= val) {
15601                     var diff1 = val - tickArray[i];
15602                     var diff2 = tickArray[next] - val;
15603                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15604                 }
15605             }
15606
15607             // The value is larger than the last tick, so we return the last
15608             // tick.
15609             return tickArray[tickArray.length - 1];
15610         }
15611     },
15612
15613     /**
15614      * toString method
15615      * @method toString
15616      * @return {string} string representation of the dd obj
15617      */
15618     toString: function() {
15619         return ("DragDrop " + this.id);
15620     }
15621
15622 });
15623
15624 })();
15625 /*
15626  * Based on:
15627  * Ext JS Library 1.1.1
15628  * Copyright(c) 2006-2007, Ext JS, LLC.
15629  *
15630  * Originally Released Under LGPL - original licence link has changed is not relivant.
15631  *
15632  * Fork - LGPL
15633  * <script type="text/javascript">
15634  */
15635
15636
15637 /**
15638  * The drag and drop utility provides a framework for building drag and drop
15639  * applications.  In addition to enabling drag and drop for specific elements,
15640  * the drag and drop elements are tracked by the manager class, and the
15641  * interactions between the various elements are tracked during the drag and
15642  * the implementing code is notified about these important moments.
15643  */
15644
15645 // Only load the library once.  Rewriting the manager class would orphan
15646 // existing drag and drop instances.
15647 if (!Roo.dd.DragDropMgr) {
15648
15649 /**
15650  * @class Roo.dd.DragDropMgr
15651  * DragDropMgr is a singleton that tracks the element interaction for
15652  * all DragDrop items in the window.  Generally, you will not call
15653  * this class directly, but it does have helper methods that could
15654  * be useful in your DragDrop implementations.
15655  * @singleton
15656  */
15657 Roo.dd.DragDropMgr = function() {
15658
15659     var Event = Roo.EventManager;
15660
15661     return {
15662
15663         /**
15664          * Two dimensional Array of registered DragDrop objects.  The first
15665          * dimension is the DragDrop item group, the second the DragDrop
15666          * object.
15667          * @property ids
15668          * @type {string: string}
15669          * @private
15670          * @static
15671          */
15672         ids: {},
15673
15674         /**
15675          * Array of element ids defined as drag handles.  Used to determine
15676          * if the element that generated the mousedown event is actually the
15677          * handle and not the html element itself.
15678          * @property handleIds
15679          * @type {string: string}
15680          * @private
15681          * @static
15682          */
15683         handleIds: {},
15684
15685         /**
15686          * the DragDrop object that is currently being dragged
15687          * @property dragCurrent
15688          * @type DragDrop
15689          * @private
15690          * @static
15691          **/
15692         dragCurrent: null,
15693
15694         /**
15695          * the DragDrop object(s) that are being hovered over
15696          * @property dragOvers
15697          * @type Array
15698          * @private
15699          * @static
15700          */
15701         dragOvers: {},
15702
15703         /**
15704          * the X distance between the cursor and the object being dragged
15705          * @property deltaX
15706          * @type int
15707          * @private
15708          * @static
15709          */
15710         deltaX: 0,
15711
15712         /**
15713          * the Y distance between the cursor and the object being dragged
15714          * @property deltaY
15715          * @type int
15716          * @private
15717          * @static
15718          */
15719         deltaY: 0,
15720
15721         /**
15722          * Flag to determine if we should prevent the default behavior of the
15723          * events we define. By default this is true, but this can be set to
15724          * false if you need the default behavior (not recommended)
15725          * @property preventDefault
15726          * @type boolean
15727          * @static
15728          */
15729         preventDefault: true,
15730
15731         /**
15732          * Flag to determine if we should stop the propagation of the events
15733          * we generate. This is true by default but you may want to set it to
15734          * false if the html element contains other features that require the
15735          * mouse click.
15736          * @property stopPropagation
15737          * @type boolean
15738          * @static
15739          */
15740         stopPropagation: true,
15741
15742         /**
15743          * Internal flag that is set to true when drag and drop has been
15744          * intialized
15745          * @property initialized
15746          * @private
15747          * @static
15748          */
15749         initalized: false,
15750
15751         /**
15752          * All drag and drop can be disabled.
15753          * @property locked
15754          * @private
15755          * @static
15756          */
15757         locked: false,
15758
15759         /**
15760          * Called the first time an element is registered.
15761          * @method init
15762          * @private
15763          * @static
15764          */
15765         init: function() {
15766             this.initialized = true;
15767         },
15768
15769         /**
15770          * In point mode, drag and drop interaction is defined by the
15771          * location of the cursor during the drag/drop
15772          * @property POINT
15773          * @type int
15774          * @static
15775          */
15776         POINT: 0,
15777
15778         /**
15779          * In intersect mode, drag and drop interactio nis defined by the
15780          * overlap of two or more drag and drop objects.
15781          * @property INTERSECT
15782          * @type int
15783          * @static
15784          */
15785         INTERSECT: 1,
15786
15787         /**
15788          * The current drag and drop mode.  Default: POINT
15789          * @property mode
15790          * @type int
15791          * @static
15792          */
15793         mode: 0,
15794
15795         /**
15796          * Runs method on all drag and drop objects
15797          * @method _execOnAll
15798          * @private
15799          * @static
15800          */
15801         _execOnAll: function(sMethod, args) {
15802             for (var i in this.ids) {
15803                 for (var j in this.ids[i]) {
15804                     var oDD = this.ids[i][j];
15805                     if (! this.isTypeOfDD(oDD)) {
15806                         continue;
15807                     }
15808                     oDD[sMethod].apply(oDD, args);
15809                 }
15810             }
15811         },
15812
15813         /**
15814          * Drag and drop initialization.  Sets up the global event handlers
15815          * @method _onLoad
15816          * @private
15817          * @static
15818          */
15819         _onLoad: function() {
15820
15821             this.init();
15822
15823
15824             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15825             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15826             Event.on(window,   "unload",    this._onUnload, this, true);
15827             Event.on(window,   "resize",    this._onResize, this, true);
15828             // Event.on(window,   "mouseout",    this._test);
15829
15830         },
15831
15832         /**
15833          * Reset constraints on all drag and drop objs
15834          * @method _onResize
15835          * @private
15836          * @static
15837          */
15838         _onResize: function(e) {
15839             this._execOnAll("resetConstraints", []);
15840         },
15841
15842         /**
15843          * Lock all drag and drop functionality
15844          * @method lock
15845          * @static
15846          */
15847         lock: function() { this.locked = true; },
15848
15849         /**
15850          * Unlock all drag and drop functionality
15851          * @method unlock
15852          * @static
15853          */
15854         unlock: function() { this.locked = false; },
15855
15856         /**
15857          * Is drag and drop locked?
15858          * @method isLocked
15859          * @return {boolean} True if drag and drop is locked, false otherwise.
15860          * @static
15861          */
15862         isLocked: function() { return this.locked; },
15863
15864         /**
15865          * Location cache that is set for all drag drop objects when a drag is
15866          * initiated, cleared when the drag is finished.
15867          * @property locationCache
15868          * @private
15869          * @static
15870          */
15871         locationCache: {},
15872
15873         /**
15874          * Set useCache to false if you want to force object the lookup of each
15875          * drag and drop linked element constantly during a drag.
15876          * @property useCache
15877          * @type boolean
15878          * @static
15879          */
15880         useCache: true,
15881
15882         /**
15883          * The number of pixels that the mouse needs to move after the
15884          * mousedown before the drag is initiated.  Default=3;
15885          * @property clickPixelThresh
15886          * @type int
15887          * @static
15888          */
15889         clickPixelThresh: 3,
15890
15891         /**
15892          * The number of milliseconds after the mousedown event to initiate the
15893          * drag if we don't get a mouseup event. Default=1000
15894          * @property clickTimeThresh
15895          * @type int
15896          * @static
15897          */
15898         clickTimeThresh: 350,
15899
15900         /**
15901          * Flag that indicates that either the drag pixel threshold or the
15902          * mousdown time threshold has been met
15903          * @property dragThreshMet
15904          * @type boolean
15905          * @private
15906          * @static
15907          */
15908         dragThreshMet: false,
15909
15910         /**
15911          * Timeout used for the click time threshold
15912          * @property clickTimeout
15913          * @type Object
15914          * @private
15915          * @static
15916          */
15917         clickTimeout: null,
15918
15919         /**
15920          * The X position of the mousedown event stored for later use when a
15921          * drag threshold is met.
15922          * @property startX
15923          * @type int
15924          * @private
15925          * @static
15926          */
15927         startX: 0,
15928
15929         /**
15930          * The Y position of the mousedown event stored for later use when a
15931          * drag threshold is met.
15932          * @property startY
15933          * @type int
15934          * @private
15935          * @static
15936          */
15937         startY: 0,
15938
15939         /**
15940          * Each DragDrop instance must be registered with the DragDropMgr.
15941          * This is executed in DragDrop.init()
15942          * @method regDragDrop
15943          * @param {DragDrop} oDD the DragDrop object to register
15944          * @param {String} sGroup the name of the group this element belongs to
15945          * @static
15946          */
15947         regDragDrop: function(oDD, sGroup) {
15948             if (!this.initialized) { this.init(); }
15949
15950             if (!this.ids[sGroup]) {
15951                 this.ids[sGroup] = {};
15952             }
15953             this.ids[sGroup][oDD.id] = oDD;
15954         },
15955
15956         /**
15957          * Removes the supplied dd instance from the supplied group. Executed
15958          * by DragDrop.removeFromGroup, so don't call this function directly.
15959          * @method removeDDFromGroup
15960          * @private
15961          * @static
15962          */
15963         removeDDFromGroup: function(oDD, sGroup) {
15964             if (!this.ids[sGroup]) {
15965                 this.ids[sGroup] = {};
15966             }
15967
15968             var obj = this.ids[sGroup];
15969             if (obj && obj[oDD.id]) {
15970                 delete obj[oDD.id];
15971             }
15972         },
15973
15974         /**
15975          * Unregisters a drag and drop item.  This is executed in
15976          * DragDrop.unreg, use that method instead of calling this directly.
15977          * @method _remove
15978          * @private
15979          * @static
15980          */
15981         _remove: function(oDD) {
15982             for (var g in oDD.groups) {
15983                 if (g && this.ids[g][oDD.id]) {
15984                     delete this.ids[g][oDD.id];
15985                 }
15986             }
15987             delete this.handleIds[oDD.id];
15988         },
15989
15990         /**
15991          * Each DragDrop handle element must be registered.  This is done
15992          * automatically when executing DragDrop.setHandleElId()
15993          * @method regHandle
15994          * @param {String} sDDId the DragDrop id this element is a handle for
15995          * @param {String} sHandleId the id of the element that is the drag
15996          * handle
15997          * @static
15998          */
15999         regHandle: function(sDDId, sHandleId) {
16000             if (!this.handleIds[sDDId]) {
16001                 this.handleIds[sDDId] = {};
16002             }
16003             this.handleIds[sDDId][sHandleId] = sHandleId;
16004         },
16005
16006         /**
16007          * Utility function to determine if a given element has been
16008          * registered as a drag drop item.
16009          * @method isDragDrop
16010          * @param {String} id the element id to check
16011          * @return {boolean} true if this element is a DragDrop item,
16012          * false otherwise
16013          * @static
16014          */
16015         isDragDrop: function(id) {
16016             return ( this.getDDById(id) ) ? true : false;
16017         },
16018
16019         /**
16020          * Returns the drag and drop instances that are in all groups the
16021          * passed in instance belongs to.
16022          * @method getRelated
16023          * @param {DragDrop} p_oDD the obj to get related data for
16024          * @param {boolean} bTargetsOnly if true, only return targetable objs
16025          * @return {DragDrop[]} the related instances
16026          * @static
16027          */
16028         getRelated: function(p_oDD, bTargetsOnly) {
16029             var oDDs = [];
16030             for (var i in p_oDD.groups) {
16031                 for (j in this.ids[i]) {
16032                     var dd = this.ids[i][j];
16033                     if (! this.isTypeOfDD(dd)) {
16034                         continue;
16035                     }
16036                     if (!bTargetsOnly || dd.isTarget) {
16037                         oDDs[oDDs.length] = dd;
16038                     }
16039                 }
16040             }
16041
16042             return oDDs;
16043         },
16044
16045         /**
16046          * Returns true if the specified dd target is a legal target for
16047          * the specifice drag obj
16048          * @method isLegalTarget
16049          * @param {DragDrop} the drag obj
16050          * @param {DragDrop} the target
16051          * @return {boolean} true if the target is a legal target for the
16052          * dd obj
16053          * @static
16054          */
16055         isLegalTarget: function (oDD, oTargetDD) {
16056             var targets = this.getRelated(oDD, true);
16057             for (var i=0, len=targets.length;i<len;++i) {
16058                 if (targets[i].id == oTargetDD.id) {
16059                     return true;
16060                 }
16061             }
16062
16063             return false;
16064         },
16065
16066         /**
16067          * My goal is to be able to transparently determine if an object is
16068          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16069          * returns "object", oDD.constructor.toString() always returns
16070          * "DragDrop" and not the name of the subclass.  So for now it just
16071          * evaluates a well-known variable in DragDrop.
16072          * @method isTypeOfDD
16073          * @param {Object} the object to evaluate
16074          * @return {boolean} true if typeof oDD = DragDrop
16075          * @static
16076          */
16077         isTypeOfDD: function (oDD) {
16078             return (oDD && oDD.__ygDragDrop);
16079         },
16080
16081         /**
16082          * Utility function to determine if a given element has been
16083          * registered as a drag drop handle for the given Drag Drop object.
16084          * @method isHandle
16085          * @param {String} id the element id to check
16086          * @return {boolean} true if this element is a DragDrop handle, false
16087          * otherwise
16088          * @static
16089          */
16090         isHandle: function(sDDId, sHandleId) {
16091             return ( this.handleIds[sDDId] &&
16092                             this.handleIds[sDDId][sHandleId] );
16093         },
16094
16095         /**
16096          * Returns the DragDrop instance for a given id
16097          * @method getDDById
16098          * @param {String} id the id of the DragDrop object
16099          * @return {DragDrop} the drag drop object, null if it is not found
16100          * @static
16101          */
16102         getDDById: function(id) {
16103             for (var i in this.ids) {
16104                 if (this.ids[i][id]) {
16105                     return this.ids[i][id];
16106                 }
16107             }
16108             return null;
16109         },
16110
16111         /**
16112          * Fired after a registered DragDrop object gets the mousedown event.
16113          * Sets up the events required to track the object being dragged
16114          * @method handleMouseDown
16115          * @param {Event} e the event
16116          * @param oDD the DragDrop object being dragged
16117          * @private
16118          * @static
16119          */
16120         handleMouseDown: function(e, oDD) {
16121             if(Roo.QuickTips){
16122                 Roo.QuickTips.disable();
16123             }
16124             this.currentTarget = e.getTarget();
16125
16126             this.dragCurrent = oDD;
16127
16128             var el = oDD.getEl();
16129
16130             // track start position
16131             this.startX = e.getPageX();
16132             this.startY = e.getPageY();
16133
16134             this.deltaX = this.startX - el.offsetLeft;
16135             this.deltaY = this.startY - el.offsetTop;
16136
16137             this.dragThreshMet = false;
16138
16139             this.clickTimeout = setTimeout(
16140                     function() {
16141                         var DDM = Roo.dd.DDM;
16142                         DDM.startDrag(DDM.startX, DDM.startY);
16143                     },
16144                     this.clickTimeThresh );
16145         },
16146
16147         /**
16148          * Fired when either the drag pixel threshol or the mousedown hold
16149          * time threshold has been met.
16150          * @method startDrag
16151          * @param x {int} the X position of the original mousedown
16152          * @param y {int} the Y position of the original mousedown
16153          * @static
16154          */
16155         startDrag: function(x, y) {
16156             clearTimeout(this.clickTimeout);
16157             if (this.dragCurrent) {
16158                 this.dragCurrent.b4StartDrag(x, y);
16159                 this.dragCurrent.startDrag(x, y);
16160             }
16161             this.dragThreshMet = true;
16162         },
16163
16164         /**
16165          * Internal function to handle the mouseup event.  Will be invoked
16166          * from the context of the document.
16167          * @method handleMouseUp
16168          * @param {Event} e the event
16169          * @private
16170          * @static
16171          */
16172         handleMouseUp: function(e) {
16173
16174             if(Roo.QuickTips){
16175                 Roo.QuickTips.enable();
16176             }
16177             if (! this.dragCurrent) {
16178                 return;
16179             }
16180
16181             clearTimeout(this.clickTimeout);
16182
16183             if (this.dragThreshMet) {
16184                 this.fireEvents(e, true);
16185             } else {
16186             }
16187
16188             this.stopDrag(e);
16189
16190             this.stopEvent(e);
16191         },
16192
16193         /**
16194          * Utility to stop event propagation and event default, if these
16195          * features are turned on.
16196          * @method stopEvent
16197          * @param {Event} e the event as returned by this.getEvent()
16198          * @static
16199          */
16200         stopEvent: function(e){
16201             if(this.stopPropagation) {
16202                 e.stopPropagation();
16203             }
16204
16205             if (this.preventDefault) {
16206                 e.preventDefault();
16207             }
16208         },
16209
16210         /**
16211          * Internal function to clean up event handlers after the drag
16212          * operation is complete
16213          * @method stopDrag
16214          * @param {Event} e the event
16215          * @private
16216          * @static
16217          */
16218         stopDrag: function(e) {
16219             // Fire the drag end event for the item that was dragged
16220             if (this.dragCurrent) {
16221                 if (this.dragThreshMet) {
16222                     this.dragCurrent.b4EndDrag(e);
16223                     this.dragCurrent.endDrag(e);
16224                 }
16225
16226                 this.dragCurrent.onMouseUp(e);
16227             }
16228
16229             this.dragCurrent = null;
16230             this.dragOvers = {};
16231         },
16232
16233         /**
16234          * Internal function to handle the mousemove event.  Will be invoked
16235          * from the context of the html element.
16236          *
16237          * @TODO figure out what we can do about mouse events lost when the
16238          * user drags objects beyond the window boundary.  Currently we can
16239          * detect this in internet explorer by verifying that the mouse is
16240          * down during the mousemove event.  Firefox doesn't give us the
16241          * button state on the mousemove event.
16242          * @method handleMouseMove
16243          * @param {Event} e the event
16244          * @private
16245          * @static
16246          */
16247         handleMouseMove: function(e) {
16248             if (! this.dragCurrent) {
16249                 return true;
16250             }
16251
16252             // var button = e.which || e.button;
16253
16254             // check for IE mouseup outside of page boundary
16255             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16256                 this.stopEvent(e);
16257                 return this.handleMouseUp(e);
16258             }
16259
16260             if (!this.dragThreshMet) {
16261                 var diffX = Math.abs(this.startX - e.getPageX());
16262                 var diffY = Math.abs(this.startY - e.getPageY());
16263                 if (diffX > this.clickPixelThresh ||
16264                             diffY > this.clickPixelThresh) {
16265                     this.startDrag(this.startX, this.startY);
16266                 }
16267             }
16268
16269             if (this.dragThreshMet) {
16270                 this.dragCurrent.b4Drag(e);
16271                 this.dragCurrent.onDrag(e);
16272                 if(!this.dragCurrent.moveOnly){
16273                     this.fireEvents(e, false);
16274                 }
16275             }
16276
16277             this.stopEvent(e);
16278
16279             return true;
16280         },
16281
16282         /**
16283          * Iterates over all of the DragDrop elements to find ones we are
16284          * hovering over or dropping on
16285          * @method fireEvents
16286          * @param {Event} e the event
16287          * @param {boolean} isDrop is this a drop op or a mouseover op?
16288          * @private
16289          * @static
16290          */
16291         fireEvents: function(e, isDrop) {
16292             var dc = this.dragCurrent;
16293
16294             // If the user did the mouse up outside of the window, we could
16295             // get here even though we have ended the drag.
16296             if (!dc || dc.isLocked()) {
16297                 return;
16298             }
16299
16300             var pt = e.getPoint();
16301
16302             // cache the previous dragOver array
16303             var oldOvers = [];
16304
16305             var outEvts   = [];
16306             var overEvts  = [];
16307             var dropEvts  = [];
16308             var enterEvts = [];
16309
16310             // Check to see if the object(s) we were hovering over is no longer
16311             // being hovered over so we can fire the onDragOut event
16312             for (var i in this.dragOvers) {
16313
16314                 var ddo = this.dragOvers[i];
16315
16316                 if (! this.isTypeOfDD(ddo)) {
16317                     continue;
16318                 }
16319
16320                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16321                     outEvts.push( ddo );
16322                 }
16323
16324                 oldOvers[i] = true;
16325                 delete this.dragOvers[i];
16326             }
16327
16328             for (var sGroup in dc.groups) {
16329
16330                 if ("string" != typeof sGroup) {
16331                     continue;
16332                 }
16333
16334                 for (i in this.ids[sGroup]) {
16335                     var oDD = this.ids[sGroup][i];
16336                     if (! this.isTypeOfDD(oDD)) {
16337                         continue;
16338                     }
16339
16340                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16341                         if (this.isOverTarget(pt, oDD, this.mode)) {
16342                             // look for drop interactions
16343                             if (isDrop) {
16344                                 dropEvts.push( oDD );
16345                             // look for drag enter and drag over interactions
16346                             } else {
16347
16348                                 // initial drag over: dragEnter fires
16349                                 if (!oldOvers[oDD.id]) {
16350                                     enterEvts.push( oDD );
16351                                 // subsequent drag overs: dragOver fires
16352                                 } else {
16353                                     overEvts.push( oDD );
16354                                 }
16355
16356                                 this.dragOvers[oDD.id] = oDD;
16357                             }
16358                         }
16359                     }
16360                 }
16361             }
16362
16363             if (this.mode) {
16364                 if (outEvts.length) {
16365                     dc.b4DragOut(e, outEvts);
16366                     dc.onDragOut(e, outEvts);
16367                 }
16368
16369                 if (enterEvts.length) {
16370                     dc.onDragEnter(e, enterEvts);
16371                 }
16372
16373                 if (overEvts.length) {
16374                     dc.b4DragOver(e, overEvts);
16375                     dc.onDragOver(e, overEvts);
16376                 }
16377
16378                 if (dropEvts.length) {
16379                     dc.b4DragDrop(e, dropEvts);
16380                     dc.onDragDrop(e, dropEvts);
16381                 }
16382
16383             } else {
16384                 // fire dragout events
16385                 var len = 0;
16386                 for (i=0, len=outEvts.length; i<len; ++i) {
16387                     dc.b4DragOut(e, outEvts[i].id);
16388                     dc.onDragOut(e, outEvts[i].id);
16389                 }
16390
16391                 // fire enter events
16392                 for (i=0,len=enterEvts.length; i<len; ++i) {
16393                     // dc.b4DragEnter(e, oDD.id);
16394                     dc.onDragEnter(e, enterEvts[i].id);
16395                 }
16396
16397                 // fire over events
16398                 for (i=0,len=overEvts.length; i<len; ++i) {
16399                     dc.b4DragOver(e, overEvts[i].id);
16400                     dc.onDragOver(e, overEvts[i].id);
16401                 }
16402
16403                 // fire drop events
16404                 for (i=0, len=dropEvts.length; i<len; ++i) {
16405                     dc.b4DragDrop(e, dropEvts[i].id);
16406                     dc.onDragDrop(e, dropEvts[i].id);
16407                 }
16408
16409             }
16410
16411             // notify about a drop that did not find a target
16412             if (isDrop && !dropEvts.length) {
16413                 dc.onInvalidDrop(e);
16414             }
16415
16416         },
16417
16418         /**
16419          * Helper function for getting the best match from the list of drag
16420          * and drop objects returned by the drag and drop events when we are
16421          * in INTERSECT mode.  It returns either the first object that the
16422          * cursor is over, or the object that has the greatest overlap with
16423          * the dragged element.
16424          * @method getBestMatch
16425          * @param  {DragDrop[]} dds The array of drag and drop objects
16426          * targeted
16427          * @return {DragDrop}       The best single match
16428          * @static
16429          */
16430         getBestMatch: function(dds) {
16431             var winner = null;
16432             // Return null if the input is not what we expect
16433             //if (!dds || !dds.length || dds.length == 0) {
16434                // winner = null;
16435             // If there is only one item, it wins
16436             //} else if (dds.length == 1) {
16437
16438             var len = dds.length;
16439
16440             if (len == 1) {
16441                 winner = dds[0];
16442             } else {
16443                 // Loop through the targeted items
16444                 for (var i=0; i<len; ++i) {
16445                     var dd = dds[i];
16446                     // If the cursor is over the object, it wins.  If the
16447                     // cursor is over multiple matches, the first one we come
16448                     // to wins.
16449                     if (dd.cursorIsOver) {
16450                         winner = dd;
16451                         break;
16452                     // Otherwise the object with the most overlap wins
16453                     } else {
16454                         if (!winner ||
16455                             winner.overlap.getArea() < dd.overlap.getArea()) {
16456                             winner = dd;
16457                         }
16458                     }
16459                 }
16460             }
16461
16462             return winner;
16463         },
16464
16465         /**
16466          * Refreshes the cache of the top-left and bottom-right points of the
16467          * drag and drop objects in the specified group(s).  This is in the
16468          * format that is stored in the drag and drop instance, so typical
16469          * usage is:
16470          * <code>
16471          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16472          * </code>
16473          * Alternatively:
16474          * <code>
16475          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16476          * </code>
16477          * @TODO this really should be an indexed array.  Alternatively this
16478          * method could accept both.
16479          * @method refreshCache
16480          * @param {Object} groups an associative array of groups to refresh
16481          * @static
16482          */
16483         refreshCache: function(groups) {
16484             for (var sGroup in groups) {
16485                 if ("string" != typeof sGroup) {
16486                     continue;
16487                 }
16488                 for (var i in this.ids[sGroup]) {
16489                     var oDD = this.ids[sGroup][i];
16490
16491                     if (this.isTypeOfDD(oDD)) {
16492                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16493                         var loc = this.getLocation(oDD);
16494                         if (loc) {
16495                             this.locationCache[oDD.id] = loc;
16496                         } else {
16497                             delete this.locationCache[oDD.id];
16498                             // this will unregister the drag and drop object if
16499                             // the element is not in a usable state
16500                             // oDD.unreg();
16501                         }
16502                     }
16503                 }
16504             }
16505         },
16506
16507         /**
16508          * This checks to make sure an element exists and is in the DOM.  The
16509          * main purpose is to handle cases where innerHTML is used to remove
16510          * drag and drop objects from the DOM.  IE provides an 'unspecified
16511          * error' when trying to access the offsetParent of such an element
16512          * @method verifyEl
16513          * @param {HTMLElement} el the element to check
16514          * @return {boolean} true if the element looks usable
16515          * @static
16516          */
16517         verifyEl: function(el) {
16518             if (el) {
16519                 var parent;
16520                 if(Roo.isIE){
16521                     try{
16522                         parent = el.offsetParent;
16523                     }catch(e){}
16524                 }else{
16525                     parent = el.offsetParent;
16526                 }
16527                 if (parent) {
16528                     return true;
16529                 }
16530             }
16531
16532             return false;
16533         },
16534
16535         /**
16536          * Returns a Region object containing the drag and drop element's position
16537          * and size, including the padding configured for it
16538          * @method getLocation
16539          * @param {DragDrop} oDD the drag and drop object to get the
16540          *                       location for
16541          * @return {Roo.lib.Region} a Region object representing the total area
16542          *                             the element occupies, including any padding
16543          *                             the instance is configured for.
16544          * @static
16545          */
16546         getLocation: function(oDD) {
16547             if (! this.isTypeOfDD(oDD)) {
16548                 return null;
16549             }
16550
16551             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16552
16553             try {
16554                 pos= Roo.lib.Dom.getXY(el);
16555             } catch (e) { }
16556
16557             if (!pos) {
16558                 return null;
16559             }
16560
16561             x1 = pos[0];
16562             x2 = x1 + el.offsetWidth;
16563             y1 = pos[1];
16564             y2 = y1 + el.offsetHeight;
16565
16566             t = y1 - oDD.padding[0];
16567             r = x2 + oDD.padding[1];
16568             b = y2 + oDD.padding[2];
16569             l = x1 - oDD.padding[3];
16570
16571             return new Roo.lib.Region( t, r, b, l );
16572         },
16573
16574         /**
16575          * Checks the cursor location to see if it over the target
16576          * @method isOverTarget
16577          * @param {Roo.lib.Point} pt The point to evaluate
16578          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16579          * @return {boolean} true if the mouse is over the target
16580          * @private
16581          * @static
16582          */
16583         isOverTarget: function(pt, oTarget, intersect) {
16584             // use cache if available
16585             var loc = this.locationCache[oTarget.id];
16586             if (!loc || !this.useCache) {
16587                 loc = this.getLocation(oTarget);
16588                 this.locationCache[oTarget.id] = loc;
16589
16590             }
16591
16592             if (!loc) {
16593                 return false;
16594             }
16595
16596             oTarget.cursorIsOver = loc.contains( pt );
16597
16598             // DragDrop is using this as a sanity check for the initial mousedown
16599             // in this case we are done.  In POINT mode, if the drag obj has no
16600             // contraints, we are also done. Otherwise we need to evaluate the
16601             // location of the target as related to the actual location of the
16602             // dragged element.
16603             var dc = this.dragCurrent;
16604             if (!dc || !dc.getTargetCoord ||
16605                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16606                 return oTarget.cursorIsOver;
16607             }
16608
16609             oTarget.overlap = null;
16610
16611             // Get the current location of the drag element, this is the
16612             // location of the mouse event less the delta that represents
16613             // where the original mousedown happened on the element.  We
16614             // need to consider constraints and ticks as well.
16615             var pos = dc.getTargetCoord(pt.x, pt.y);
16616
16617             var el = dc.getDragEl();
16618             var curRegion = new Roo.lib.Region( pos.y,
16619                                                    pos.x + el.offsetWidth,
16620                                                    pos.y + el.offsetHeight,
16621                                                    pos.x );
16622
16623             var overlap = curRegion.intersect(loc);
16624
16625             if (overlap) {
16626                 oTarget.overlap = overlap;
16627                 return (intersect) ? true : oTarget.cursorIsOver;
16628             } else {
16629                 return false;
16630             }
16631         },
16632
16633         /**
16634          * unload event handler
16635          * @method _onUnload
16636          * @private
16637          * @static
16638          */
16639         _onUnload: function(e, me) {
16640             Roo.dd.DragDropMgr.unregAll();
16641         },
16642
16643         /**
16644          * Cleans up the drag and drop events and objects.
16645          * @method unregAll
16646          * @private
16647          * @static
16648          */
16649         unregAll: function() {
16650
16651             if (this.dragCurrent) {
16652                 this.stopDrag();
16653                 this.dragCurrent = null;
16654             }
16655
16656             this._execOnAll("unreg", []);
16657
16658             for (i in this.elementCache) {
16659                 delete this.elementCache[i];
16660             }
16661
16662             this.elementCache = {};
16663             this.ids = {};
16664         },
16665
16666         /**
16667          * A cache of DOM elements
16668          * @property elementCache
16669          * @private
16670          * @static
16671          */
16672         elementCache: {},
16673
16674         /**
16675          * Get the wrapper for the DOM element specified
16676          * @method getElWrapper
16677          * @param {String} id the id of the element to get
16678          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16679          * @private
16680          * @deprecated This wrapper isn't that useful
16681          * @static
16682          */
16683         getElWrapper: function(id) {
16684             var oWrapper = this.elementCache[id];
16685             if (!oWrapper || !oWrapper.el) {
16686                 oWrapper = this.elementCache[id] =
16687                     new this.ElementWrapper(Roo.getDom(id));
16688             }
16689             return oWrapper;
16690         },
16691
16692         /**
16693          * Returns the actual DOM element
16694          * @method getElement
16695          * @param {String} id the id of the elment to get
16696          * @return {Object} The element
16697          * @deprecated use Roo.getDom instead
16698          * @static
16699          */
16700         getElement: function(id) {
16701             return Roo.getDom(id);
16702         },
16703
16704         /**
16705          * Returns the style property for the DOM element (i.e.,
16706          * document.getElById(id).style)
16707          * @method getCss
16708          * @param {String} id the id of the elment to get
16709          * @return {Object} The style property of the element
16710          * @deprecated use Roo.getDom instead
16711          * @static
16712          */
16713         getCss: function(id) {
16714             var el = Roo.getDom(id);
16715             return (el) ? el.style : null;
16716         },
16717
16718         /**
16719          * Inner class for cached elements
16720          * @class DragDropMgr.ElementWrapper
16721          * @for DragDropMgr
16722          * @private
16723          * @deprecated
16724          */
16725         ElementWrapper: function(el) {
16726                 /**
16727                  * The element
16728                  * @property el
16729                  */
16730                 this.el = el || null;
16731                 /**
16732                  * The element id
16733                  * @property id
16734                  */
16735                 this.id = this.el && el.id;
16736                 /**
16737                  * A reference to the style property
16738                  * @property css
16739                  */
16740                 this.css = this.el && el.style;
16741             },
16742
16743         /**
16744          * Returns the X position of an html element
16745          * @method getPosX
16746          * @param el the element for which to get the position
16747          * @return {int} the X coordinate
16748          * @for DragDropMgr
16749          * @deprecated use Roo.lib.Dom.getX instead
16750          * @static
16751          */
16752         getPosX: function(el) {
16753             return Roo.lib.Dom.getX(el);
16754         },
16755
16756         /**
16757          * Returns the Y position of an html element
16758          * @method getPosY
16759          * @param el the element for which to get the position
16760          * @return {int} the Y coordinate
16761          * @deprecated use Roo.lib.Dom.getY instead
16762          * @static
16763          */
16764         getPosY: function(el) {
16765             return Roo.lib.Dom.getY(el);
16766         },
16767
16768         /**
16769          * Swap two nodes.  In IE, we use the native method, for others we
16770          * emulate the IE behavior
16771          * @method swapNode
16772          * @param n1 the first node to swap
16773          * @param n2 the other node to swap
16774          * @static
16775          */
16776         swapNode: function(n1, n2) {
16777             if (n1.swapNode) {
16778                 n1.swapNode(n2);
16779             } else {
16780                 var p = n2.parentNode;
16781                 var s = n2.nextSibling;
16782
16783                 if (s == n1) {
16784                     p.insertBefore(n1, n2);
16785                 } else if (n2 == n1.nextSibling) {
16786                     p.insertBefore(n2, n1);
16787                 } else {
16788                     n1.parentNode.replaceChild(n2, n1);
16789                     p.insertBefore(n1, s);
16790                 }
16791             }
16792         },
16793
16794         /**
16795          * Returns the current scroll position
16796          * @method getScroll
16797          * @private
16798          * @static
16799          */
16800         getScroll: function () {
16801             var t, l, dde=document.documentElement, db=document.body;
16802             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16803                 t = dde.scrollTop;
16804                 l = dde.scrollLeft;
16805             } else if (db) {
16806                 t = db.scrollTop;
16807                 l = db.scrollLeft;
16808             } else {
16809
16810             }
16811             return { top: t, left: l };
16812         },
16813
16814         /**
16815          * Returns the specified element style property
16816          * @method getStyle
16817          * @param {HTMLElement} el          the element
16818          * @param {string}      styleProp   the style property
16819          * @return {string} The value of the style property
16820          * @deprecated use Roo.lib.Dom.getStyle
16821          * @static
16822          */
16823         getStyle: function(el, styleProp) {
16824             return Roo.fly(el).getStyle(styleProp);
16825         },
16826
16827         /**
16828          * Gets the scrollTop
16829          * @method getScrollTop
16830          * @return {int} the document's scrollTop
16831          * @static
16832          */
16833         getScrollTop: function () { return this.getScroll().top; },
16834
16835         /**
16836          * Gets the scrollLeft
16837          * @method getScrollLeft
16838          * @return {int} the document's scrollTop
16839          * @static
16840          */
16841         getScrollLeft: function () { return this.getScroll().left; },
16842
16843         /**
16844          * Sets the x/y position of an element to the location of the
16845          * target element.
16846          * @method moveToEl
16847          * @param {HTMLElement} moveEl      The element to move
16848          * @param {HTMLElement} targetEl    The position reference element
16849          * @static
16850          */
16851         moveToEl: function (moveEl, targetEl) {
16852             var aCoord = Roo.lib.Dom.getXY(targetEl);
16853             Roo.lib.Dom.setXY(moveEl, aCoord);
16854         },
16855
16856         /**
16857          * Numeric array sort function
16858          * @method numericSort
16859          * @static
16860          */
16861         numericSort: function(a, b) { return (a - b); },
16862
16863         /**
16864          * Internal counter
16865          * @property _timeoutCount
16866          * @private
16867          * @static
16868          */
16869         _timeoutCount: 0,
16870
16871         /**
16872          * Trying to make the load order less important.  Without this we get
16873          * an error if this file is loaded before the Event Utility.
16874          * @method _addListeners
16875          * @private
16876          * @static
16877          */
16878         _addListeners: function() {
16879             var DDM = Roo.dd.DDM;
16880             if ( Roo.lib.Event && document ) {
16881                 DDM._onLoad();
16882             } else {
16883                 if (DDM._timeoutCount > 2000) {
16884                 } else {
16885                     setTimeout(DDM._addListeners, 10);
16886                     if (document && document.body) {
16887                         DDM._timeoutCount += 1;
16888                     }
16889                 }
16890             }
16891         },
16892
16893         /**
16894          * Recursively searches the immediate parent and all child nodes for
16895          * the handle element in order to determine wheter or not it was
16896          * clicked.
16897          * @method handleWasClicked
16898          * @param node the html element to inspect
16899          * @static
16900          */
16901         handleWasClicked: function(node, id) {
16902             if (this.isHandle(id, node.id)) {
16903                 return true;
16904             } else {
16905                 // check to see if this is a text node child of the one we want
16906                 var p = node.parentNode;
16907
16908                 while (p) {
16909                     if (this.isHandle(id, p.id)) {
16910                         return true;
16911                     } else {
16912                         p = p.parentNode;
16913                     }
16914                 }
16915             }
16916
16917             return false;
16918         }
16919
16920     };
16921
16922 }();
16923
16924 // shorter alias, save a few bytes
16925 Roo.dd.DDM = Roo.dd.DragDropMgr;
16926 Roo.dd.DDM._addListeners();
16927
16928 }/*
16929  * Based on:
16930  * Ext JS Library 1.1.1
16931  * Copyright(c) 2006-2007, Ext JS, LLC.
16932  *
16933  * Originally Released Under LGPL - original licence link has changed is not relivant.
16934  *
16935  * Fork - LGPL
16936  * <script type="text/javascript">
16937  */
16938
16939 /**
16940  * @class Roo.dd.DD
16941  * A DragDrop implementation where the linked element follows the
16942  * mouse cursor during a drag.
16943  * @extends Roo.dd.DragDrop
16944  * @constructor
16945  * @param {String} id the id of the linked element
16946  * @param {String} sGroup the group of related DragDrop items
16947  * @param {object} config an object containing configurable attributes
16948  *                Valid properties for DD:
16949  *                    scroll
16950  */
16951 Roo.dd.DD = function(id, sGroup, config) {
16952     if (id) {
16953         this.init(id, sGroup, config);
16954     }
16955 };
16956
16957 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16958
16959     /**
16960      * When set to true, the utility automatically tries to scroll the browser
16961      * window wehn a drag and drop element is dragged near the viewport boundary.
16962      * Defaults to true.
16963      * @property scroll
16964      * @type boolean
16965      */
16966     scroll: true,
16967
16968     /**
16969      * Sets the pointer offset to the distance between the linked element's top
16970      * left corner and the location the element was clicked
16971      * @method autoOffset
16972      * @param {int} iPageX the X coordinate of the click
16973      * @param {int} iPageY the Y coordinate of the click
16974      */
16975     autoOffset: function(iPageX, iPageY) {
16976         var x = iPageX - this.startPageX;
16977         var y = iPageY - this.startPageY;
16978         this.setDelta(x, y);
16979     },
16980
16981     /**
16982      * Sets the pointer offset.  You can call this directly to force the
16983      * offset to be in a particular location (e.g., pass in 0,0 to set it
16984      * to the center of the object)
16985      * @method setDelta
16986      * @param {int} iDeltaX the distance from the left
16987      * @param {int} iDeltaY the distance from the top
16988      */
16989     setDelta: function(iDeltaX, iDeltaY) {
16990         this.deltaX = iDeltaX;
16991         this.deltaY = iDeltaY;
16992     },
16993
16994     /**
16995      * Sets the drag element to the location of the mousedown or click event,
16996      * maintaining the cursor location relative to the location on the element
16997      * that was clicked.  Override this if you want to place the element in a
16998      * location other than where the cursor is.
16999      * @method setDragElPos
17000      * @param {int} iPageX the X coordinate of the mousedown or drag event
17001      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17002      */
17003     setDragElPos: function(iPageX, iPageY) {
17004         // the first time we do this, we are going to check to make sure
17005         // the element has css positioning
17006
17007         var el = this.getDragEl();
17008         this.alignElWithMouse(el, iPageX, iPageY);
17009     },
17010
17011     /**
17012      * Sets the element to the location of the mousedown or click event,
17013      * maintaining the cursor location relative to the location on the element
17014      * that was clicked.  Override this if you want to place the element in a
17015      * location other than where the cursor is.
17016      * @method alignElWithMouse
17017      * @param {HTMLElement} el the element to move
17018      * @param {int} iPageX the X coordinate of the mousedown or drag event
17019      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17020      */
17021     alignElWithMouse: function(el, iPageX, iPageY) {
17022         var oCoord = this.getTargetCoord(iPageX, iPageY);
17023         var fly = el.dom ? el : Roo.fly(el);
17024         if (!this.deltaSetXY) {
17025             var aCoord = [oCoord.x, oCoord.y];
17026             fly.setXY(aCoord);
17027             var newLeft = fly.getLeft(true);
17028             var newTop  = fly.getTop(true);
17029             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17030         } else {
17031             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17032         }
17033
17034         this.cachePosition(oCoord.x, oCoord.y);
17035         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17036         return oCoord;
17037     },
17038
17039     /**
17040      * Saves the most recent position so that we can reset the constraints and
17041      * tick marks on-demand.  We need to know this so that we can calculate the
17042      * number of pixels the element is offset from its original position.
17043      * @method cachePosition
17044      * @param iPageX the current x position (optional, this just makes it so we
17045      * don't have to look it up again)
17046      * @param iPageY the current y position (optional, this just makes it so we
17047      * don't have to look it up again)
17048      */
17049     cachePosition: function(iPageX, iPageY) {
17050         if (iPageX) {
17051             this.lastPageX = iPageX;
17052             this.lastPageY = iPageY;
17053         } else {
17054             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17055             this.lastPageX = aCoord[0];
17056             this.lastPageY = aCoord[1];
17057         }
17058     },
17059
17060     /**
17061      * Auto-scroll the window if the dragged object has been moved beyond the
17062      * visible window boundary.
17063      * @method autoScroll
17064      * @param {int} x the drag element's x position
17065      * @param {int} y the drag element's y position
17066      * @param {int} h the height of the drag element
17067      * @param {int} w the width of the drag element
17068      * @private
17069      */
17070     autoScroll: function(x, y, h, w) {
17071
17072         if (this.scroll) {
17073             // The client height
17074             var clientH = Roo.lib.Dom.getViewWidth();
17075
17076             // The client width
17077             var clientW = Roo.lib.Dom.getViewHeight();
17078
17079             // The amt scrolled down
17080             var st = this.DDM.getScrollTop();
17081
17082             // The amt scrolled right
17083             var sl = this.DDM.getScrollLeft();
17084
17085             // Location of the bottom of the element
17086             var bot = h + y;
17087
17088             // Location of the right of the element
17089             var right = w + x;
17090
17091             // The distance from the cursor to the bottom of the visible area,
17092             // adjusted so that we don't scroll if the cursor is beyond the
17093             // element drag constraints
17094             var toBot = (clientH + st - y - this.deltaY);
17095
17096             // The distance from the cursor to the right of the visible area
17097             var toRight = (clientW + sl - x - this.deltaX);
17098
17099
17100             // How close to the edge the cursor must be before we scroll
17101             // var thresh = (document.all) ? 100 : 40;
17102             var thresh = 40;
17103
17104             // How many pixels to scroll per autoscroll op.  This helps to reduce
17105             // clunky scrolling. IE is more sensitive about this ... it needs this
17106             // value to be higher.
17107             var scrAmt = (document.all) ? 80 : 30;
17108
17109             // Scroll down if we are near the bottom of the visible page and the
17110             // obj extends below the crease
17111             if ( bot > clientH && toBot < thresh ) {
17112                 window.scrollTo(sl, st + scrAmt);
17113             }
17114
17115             // Scroll up if the window is scrolled down and the top of the object
17116             // goes above the top border
17117             if ( y < st && st > 0 && y - st < thresh ) {
17118                 window.scrollTo(sl, st - scrAmt);
17119             }
17120
17121             // Scroll right if the obj is beyond the right border and the cursor is
17122             // near the border.
17123             if ( right > clientW && toRight < thresh ) {
17124                 window.scrollTo(sl + scrAmt, st);
17125             }
17126
17127             // Scroll left if the window has been scrolled to the right and the obj
17128             // extends past the left border
17129             if ( x < sl && sl > 0 && x - sl < thresh ) {
17130                 window.scrollTo(sl - scrAmt, st);
17131             }
17132         }
17133     },
17134
17135     /**
17136      * Finds the location the element should be placed if we want to move
17137      * it to where the mouse location less the click offset would place us.
17138      * @method getTargetCoord
17139      * @param {int} iPageX the X coordinate of the click
17140      * @param {int} iPageY the Y coordinate of the click
17141      * @return an object that contains the coordinates (Object.x and Object.y)
17142      * @private
17143      */
17144     getTargetCoord: function(iPageX, iPageY) {
17145
17146
17147         var x = iPageX - this.deltaX;
17148         var y = iPageY - this.deltaY;
17149
17150         if (this.constrainX) {
17151             if (x < this.minX) { x = this.minX; }
17152             if (x > this.maxX) { x = this.maxX; }
17153         }
17154
17155         if (this.constrainY) {
17156             if (y < this.minY) { y = this.minY; }
17157             if (y > this.maxY) { y = this.maxY; }
17158         }
17159
17160         x = this.getTick(x, this.xTicks);
17161         y = this.getTick(y, this.yTicks);
17162
17163
17164         return {x:x, y:y};
17165     },
17166
17167     /*
17168      * Sets up config options specific to this class. Overrides
17169      * Roo.dd.DragDrop, but all versions of this method through the
17170      * inheritance chain are called
17171      */
17172     applyConfig: function() {
17173         Roo.dd.DD.superclass.applyConfig.call(this);
17174         this.scroll = (this.config.scroll !== false);
17175     },
17176
17177     /*
17178      * Event that fires prior to the onMouseDown event.  Overrides
17179      * Roo.dd.DragDrop.
17180      */
17181     b4MouseDown: function(e) {
17182         // this.resetConstraints();
17183         this.autoOffset(e.getPageX(),
17184                             e.getPageY());
17185     },
17186
17187     /*
17188      * Event that fires prior to the onDrag event.  Overrides
17189      * Roo.dd.DragDrop.
17190      */
17191     b4Drag: function(e) {
17192         this.setDragElPos(e.getPageX(),
17193                             e.getPageY());
17194     },
17195
17196     toString: function() {
17197         return ("DD " + this.id);
17198     }
17199
17200     //////////////////////////////////////////////////////////////////////////
17201     // Debugging ygDragDrop events that can be overridden
17202     //////////////////////////////////////////////////////////////////////////
17203     /*
17204     startDrag: function(x, y) {
17205     },
17206
17207     onDrag: function(e) {
17208     },
17209
17210     onDragEnter: function(e, id) {
17211     },
17212
17213     onDragOver: function(e, id) {
17214     },
17215
17216     onDragOut: function(e, id) {
17217     },
17218
17219     onDragDrop: function(e, id) {
17220     },
17221
17222     endDrag: function(e) {
17223     }
17224
17225     */
17226
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237
17238 /**
17239  * @class Roo.dd.DDProxy
17240  * A DragDrop implementation that inserts an empty, bordered div into
17241  * the document that follows the cursor during drag operations.  At the time of
17242  * the click, the frame div is resized to the dimensions of the linked html
17243  * element, and moved to the exact location of the linked element.
17244  *
17245  * References to the "frame" element refer to the single proxy element that
17246  * was created to be dragged in place of all DDProxy elements on the
17247  * page.
17248  *
17249  * @extends Roo.dd.DD
17250  * @constructor
17251  * @param {String} id the id of the linked html element
17252  * @param {String} sGroup the group of related DragDrop objects
17253  * @param {object} config an object containing configurable attributes
17254  *                Valid properties for DDProxy in addition to those in DragDrop:
17255  *                   resizeFrame, centerFrame, dragElId
17256  */
17257 Roo.dd.DDProxy = function(id, sGroup, config) {
17258     if (id) {
17259         this.init(id, sGroup, config);
17260         this.initFrame();
17261     }
17262 };
17263
17264 /**
17265  * The default drag frame div id
17266  * @property Roo.dd.DDProxy.dragElId
17267  * @type String
17268  * @static
17269  */
17270 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17271
17272 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17273
17274     /**
17275      * By default we resize the drag frame to be the same size as the element
17276      * we want to drag (this is to get the frame effect).  We can turn it off
17277      * if we want a different behavior.
17278      * @property resizeFrame
17279      * @type boolean
17280      */
17281     resizeFrame: true,
17282
17283     /**
17284      * By default the frame is positioned exactly where the drag element is, so
17285      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17286      * you do not have constraints on the obj is to have the drag frame centered
17287      * around the cursor.  Set centerFrame to true for this effect.
17288      * @property centerFrame
17289      * @type boolean
17290      */
17291     centerFrame: false,
17292
17293     /**
17294      * Creates the proxy element if it does not yet exist
17295      * @method createFrame
17296      */
17297     createFrame: function() {
17298         var self = this;
17299         var body = document.body;
17300
17301         if (!body || !body.firstChild) {
17302             setTimeout( function() { self.createFrame(); }, 50 );
17303             return;
17304         }
17305
17306         var div = this.getDragEl();
17307
17308         if (!div) {
17309             div    = document.createElement("div");
17310             div.id = this.dragElId;
17311             var s  = div.style;
17312
17313             s.position   = "absolute";
17314             s.visibility = "hidden";
17315             s.cursor     = "move";
17316             s.border     = "2px solid #aaa";
17317             s.zIndex     = 999;
17318
17319             // appendChild can blow up IE if invoked prior to the window load event
17320             // while rendering a table.  It is possible there are other scenarios
17321             // that would cause this to happen as well.
17322             body.insertBefore(div, body.firstChild);
17323         }
17324     },
17325
17326     /**
17327      * Initialization for the drag frame element.  Must be called in the
17328      * constructor of all subclasses
17329      * @method initFrame
17330      */
17331     initFrame: function() {
17332         this.createFrame();
17333     },
17334
17335     applyConfig: function() {
17336         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17337
17338         this.resizeFrame = (this.config.resizeFrame !== false);
17339         this.centerFrame = (this.config.centerFrame);
17340         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17341     },
17342
17343     /**
17344      * Resizes the drag frame to the dimensions of the clicked object, positions
17345      * it over the object, and finally displays it
17346      * @method showFrame
17347      * @param {int} iPageX X click position
17348      * @param {int} iPageY Y click position
17349      * @private
17350      */
17351     showFrame: function(iPageX, iPageY) {
17352         var el = this.getEl();
17353         var dragEl = this.getDragEl();
17354         var s = dragEl.style;
17355
17356         this._resizeProxy();
17357
17358         if (this.centerFrame) {
17359             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17360                            Math.round(parseInt(s.height, 10)/2) );
17361         }
17362
17363         this.setDragElPos(iPageX, iPageY);
17364
17365         Roo.fly(dragEl).show();
17366     },
17367
17368     /**
17369      * The proxy is automatically resized to the dimensions of the linked
17370      * element when a drag is initiated, unless resizeFrame is set to false
17371      * @method _resizeProxy
17372      * @private
17373      */
17374     _resizeProxy: function() {
17375         if (this.resizeFrame) {
17376             var el = this.getEl();
17377             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17378         }
17379     },
17380
17381     // overrides Roo.dd.DragDrop
17382     b4MouseDown: function(e) {
17383         var x = e.getPageX();
17384         var y = e.getPageY();
17385         this.autoOffset(x, y);
17386         this.setDragElPos(x, y);
17387     },
17388
17389     // overrides Roo.dd.DragDrop
17390     b4StartDrag: function(x, y) {
17391         // show the drag frame
17392         this.showFrame(x, y);
17393     },
17394
17395     // overrides Roo.dd.DragDrop
17396     b4EndDrag: function(e) {
17397         Roo.fly(this.getDragEl()).hide();
17398     },
17399
17400     // overrides Roo.dd.DragDrop
17401     // By default we try to move the element to the last location of the frame.
17402     // This is so that the default behavior mirrors that of Roo.dd.DD.
17403     endDrag: function(e) {
17404
17405         var lel = this.getEl();
17406         var del = this.getDragEl();
17407
17408         // Show the drag frame briefly so we can get its position
17409         del.style.visibility = "";
17410
17411         this.beforeMove();
17412         // Hide the linked element before the move to get around a Safari
17413         // rendering bug.
17414         lel.style.visibility = "hidden";
17415         Roo.dd.DDM.moveToEl(lel, del);
17416         del.style.visibility = "hidden";
17417         lel.style.visibility = "";
17418
17419         this.afterDrag();
17420     },
17421
17422     beforeMove : function(){
17423
17424     },
17425
17426     afterDrag : function(){
17427
17428     },
17429
17430     toString: function() {
17431         return ("DDProxy " + this.id);
17432     }
17433
17434 });
17435 /*
17436  * Based on:
17437  * Ext JS Library 1.1.1
17438  * Copyright(c) 2006-2007, Ext JS, LLC.
17439  *
17440  * Originally Released Under LGPL - original licence link has changed is not relivant.
17441  *
17442  * Fork - LGPL
17443  * <script type="text/javascript">
17444  */
17445
17446  /**
17447  * @class Roo.dd.DDTarget
17448  * A DragDrop implementation that does not move, but can be a drop
17449  * target.  You would get the same result by simply omitting implementation
17450  * for the event callbacks, but this way we reduce the processing cost of the
17451  * event listener and the callbacks.
17452  * @extends Roo.dd.DragDrop
17453  * @constructor
17454  * @param {String} id the id of the element that is a drop target
17455  * @param {String} sGroup the group of related DragDrop objects
17456  * @param {object} config an object containing configurable attributes
17457  *                 Valid properties for DDTarget in addition to those in
17458  *                 DragDrop:
17459  *                    none
17460  */
17461 Roo.dd.DDTarget = function(id, sGroup, config) {
17462     if (id) {
17463         this.initTarget(id, sGroup, config);
17464     }
17465     if (config.listeners || config.events) { 
17466        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17467             listeners : config.listeners || {}, 
17468             events : config.events || {} 
17469         });    
17470     }
17471 };
17472
17473 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17474 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17475     toString: function() {
17476         return ("DDTarget " + this.id);
17477     }
17478 });
17479 /*
17480  * Based on:
17481  * Ext JS Library 1.1.1
17482  * Copyright(c) 2006-2007, Ext JS, LLC.
17483  *
17484  * Originally Released Under LGPL - original licence link has changed is not relivant.
17485  *
17486  * Fork - LGPL
17487  * <script type="text/javascript">
17488  */
17489  
17490
17491 /**
17492  * @class Roo.dd.ScrollManager
17493  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17494  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17495  * @singleton
17496  */
17497 Roo.dd.ScrollManager = function(){
17498     var ddm = Roo.dd.DragDropMgr;
17499     var els = {};
17500     var dragEl = null;
17501     var proc = {};
17502     
17503     var onStop = function(e){
17504         dragEl = null;
17505         clearProc();
17506     };
17507     
17508     var triggerRefresh = function(){
17509         if(ddm.dragCurrent){
17510              ddm.refreshCache(ddm.dragCurrent.groups);
17511         }
17512     };
17513     
17514     var doScroll = function(){
17515         if(ddm.dragCurrent){
17516             var dds = Roo.dd.ScrollManager;
17517             if(!dds.animate){
17518                 if(proc.el.scroll(proc.dir, dds.increment)){
17519                     triggerRefresh();
17520                 }
17521             }else{
17522                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17523             }
17524         }
17525     };
17526     
17527     var clearProc = function(){
17528         if(proc.id){
17529             clearInterval(proc.id);
17530         }
17531         proc.id = 0;
17532         proc.el = null;
17533         proc.dir = "";
17534     };
17535     
17536     var startProc = function(el, dir){
17537         clearProc();
17538         proc.el = el;
17539         proc.dir = dir;
17540         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17541     };
17542     
17543     var onFire = function(e, isDrop){
17544         if(isDrop || !ddm.dragCurrent){ return; }
17545         var dds = Roo.dd.ScrollManager;
17546         if(!dragEl || dragEl != ddm.dragCurrent){
17547             dragEl = ddm.dragCurrent;
17548             // refresh regions on drag start
17549             dds.refreshCache();
17550         }
17551         
17552         var xy = Roo.lib.Event.getXY(e);
17553         var pt = new Roo.lib.Point(xy[0], xy[1]);
17554         for(var id in els){
17555             var el = els[id], r = el._region;
17556             if(r && r.contains(pt) && el.isScrollable()){
17557                 if(r.bottom - pt.y <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "down");
17560                     }
17561                     return;
17562                 }else if(r.right - pt.x <= dds.thresh){
17563                     if(proc.el != el){
17564                         startProc(el, "left");
17565                     }
17566                     return;
17567                 }else if(pt.y - r.top <= dds.thresh){
17568                     if(proc.el != el){
17569                         startProc(el, "up");
17570                     }
17571                     return;
17572                 }else if(pt.x - r.left <= dds.thresh){
17573                     if(proc.el != el){
17574                         startProc(el, "right");
17575                     }
17576                     return;
17577                 }
17578             }
17579         }
17580         clearProc();
17581     };
17582     
17583     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17584     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17585     
17586     return {
17587         /**
17588          * Registers new overflow element(s) to auto scroll
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17590          */
17591         register : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.register(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 els[el.id] = el;
17599             }
17600         },
17601         
17602         /**
17603          * Unregisters overflow element(s) so they are no longer scrolled
17604          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17605          */
17606         unregister : function(el){
17607             if(el instanceof Array){
17608                 for(var i = 0, len = el.length; i < len; i++) {
17609                         this.unregister(el[i]);
17610                 }
17611             }else{
17612                 el = Roo.get(el);
17613                 delete els[el.id];
17614             }
17615         },
17616         
17617         /**
17618          * The number of pixels from the edge of a container the pointer needs to be to 
17619          * trigger scrolling (defaults to 25)
17620          * @type Number
17621          */
17622         thresh : 25,
17623         
17624         /**
17625          * The number of pixels to scroll in each scroll increment (defaults to 50)
17626          * @type Number
17627          */
17628         increment : 100,
17629         
17630         /**
17631          * The frequency of scrolls in milliseconds (defaults to 500)
17632          * @type Number
17633          */
17634         frequency : 500,
17635         
17636         /**
17637          * True to animate the scroll (defaults to true)
17638          * @type Boolean
17639          */
17640         animate: true,
17641         
17642         /**
17643          * The animation duration in seconds - 
17644          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17645          * @type Number
17646          */
17647         animDuration: .4,
17648         
17649         /**
17650          * Manually trigger a cache refresh.
17651          */
17652         refreshCache : function(){
17653             for(var id in els){
17654                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17655                     els[id]._region = els[id].getRegion();
17656                 }
17657             }
17658         }
17659     };
17660 }();/*
17661  * Based on:
17662  * Ext JS Library 1.1.1
17663  * Copyright(c) 2006-2007, Ext JS, LLC.
17664  *
17665  * Originally Released Under LGPL - original licence link has changed is not relivant.
17666  *
17667  * Fork - LGPL
17668  * <script type="text/javascript">
17669  */
17670  
17671
17672 /**
17673  * @class Roo.dd.Registry
17674  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17675  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17676  * @singleton
17677  */
17678 Roo.dd.Registry = function(){
17679     var elements = {}; 
17680     var handles = {}; 
17681     var autoIdSeed = 0;
17682
17683     var getId = function(el, autogen){
17684         if(typeof el == "string"){
17685             return el;
17686         }
17687         var id = el.id;
17688         if(!id && autogen !== false){
17689             id = "roodd-" + (++autoIdSeed);
17690             el.id = id;
17691         }
17692         return id;
17693     };
17694     
17695     return {
17696     /**
17697      * Register a drag drop element
17698      * @param {String|HTMLElement} element The id or DOM node to register
17699      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17700      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17701      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17702      * populated in the data object (if applicable):
17703      * <pre>
17704 Value      Description<br />
17705 ---------  ------------------------------------------<br />
17706 handles    Array of DOM nodes that trigger dragging<br />
17707            for the element being registered<br />
17708 isHandle   True if the element passed in triggers<br />
17709            dragging itself, else false
17710 </pre>
17711      */
17712         register : function(el, data){
17713             data = data || {};
17714             if(typeof el == "string"){
17715                 el = document.getElementById(el);
17716             }
17717             data.ddel = el;
17718             elements[getId(el)] = data;
17719             if(data.isHandle !== false){
17720                 handles[data.ddel.id] = data;
17721             }
17722             if(data.handles){
17723                 var hs = data.handles;
17724                 for(var i = 0, len = hs.length; i < len; i++){
17725                         handles[getId(hs[i])] = data;
17726                 }
17727             }
17728         },
17729
17730     /**
17731      * Unregister a drag drop element
17732      * @param {String|HTMLElement}  element The id or DOM node to unregister
17733      */
17734         unregister : function(el){
17735             var id = getId(el, false);
17736             var data = elements[id];
17737             if(data){
17738                 delete elements[id];
17739                 if(data.handles){
17740                     var hs = data.handles;
17741                     for(var i = 0, len = hs.length; i < len; i++){
17742                         delete handles[getId(hs[i], false)];
17743                     }
17744                 }
17745             }
17746         },
17747
17748     /**
17749      * Returns the handle registered for a DOM Node by id
17750      * @param {String|HTMLElement} id The DOM node or id to look up
17751      * @return {Object} handle The custom handle data
17752      */
17753         getHandle : function(id){
17754             if(typeof id != "string"){ // must be element?
17755                 id = id.id;
17756             }
17757             return handles[id];
17758         },
17759
17760     /**
17761      * Returns the handle that is registered for the DOM node that is the target of the event
17762      * @param {Event} e The event
17763      * @return {Object} handle The custom handle data
17764      */
17765         getHandleFromEvent : function(e){
17766             var t = Roo.lib.Event.getTarget(e);
17767             return t ? handles[t.id] : null;
17768         },
17769
17770     /**
17771      * Returns a custom data object that is registered for a DOM node by id
17772      * @param {String|HTMLElement} id The DOM node or id to look up
17773      * @return {Object} data The custom data
17774      */
17775         getTarget : function(id){
17776             if(typeof id != "string"){ // must be element?
17777                 id = id.id;
17778             }
17779             return elements[id];
17780         },
17781
17782     /**
17783      * Returns a custom data object that is registered for the DOM node that is the target of the event
17784      * @param {Event} e The event
17785      * @return {Object} data The custom data
17786      */
17787         getTargetFromEvent : function(e){
17788             var t = Roo.lib.Event.getTarget(e);
17789             return t ? elements[t.id] || handles[t.id] : null;
17790         }
17791     };
17792 }();/*
17793  * Based on:
17794  * Ext JS Library 1.1.1
17795  * Copyright(c) 2006-2007, Ext JS, LLC.
17796  *
17797  * Originally Released Under LGPL - original licence link has changed is not relivant.
17798  *
17799  * Fork - LGPL
17800  * <script type="text/javascript">
17801  */
17802  
17803
17804 /**
17805  * @class Roo.dd.StatusProxy
17806  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17807  * default drag proxy used by all Roo.dd components.
17808  * @constructor
17809  * @param {Object} config
17810  */
17811 Roo.dd.StatusProxy = function(config){
17812     Roo.apply(this, config);
17813     this.id = this.id || Roo.id();
17814     this.el = new Roo.Layer({
17815         dh: {
17816             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17817                 {tag: "div", cls: "x-dd-drop-icon"},
17818                 {tag: "div", cls: "x-dd-drag-ghost"}
17819             ]
17820         }, 
17821         shadow: !config || config.shadow !== false
17822     });
17823     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17824     this.dropStatus = this.dropNotAllowed;
17825 };
17826
17827 Roo.dd.StatusProxy.prototype = {
17828     /**
17829      * @cfg {String} dropAllowed
17830      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17831      */
17832     dropAllowed : "x-dd-drop-ok",
17833     /**
17834      * @cfg {String} dropNotAllowed
17835      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17836      */
17837     dropNotAllowed : "x-dd-drop-nodrop",
17838
17839     /**
17840      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17841      * over the current target element.
17842      * @param {String} cssClass The css class for the new drop status indicator image
17843      */
17844     setStatus : function(cssClass){
17845         cssClass = cssClass || this.dropNotAllowed;
17846         if(this.dropStatus != cssClass){
17847             this.el.replaceClass(this.dropStatus, cssClass);
17848             this.dropStatus = cssClass;
17849         }
17850     },
17851
17852     /**
17853      * Resets the status indicator to the default dropNotAllowed value
17854      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17855      */
17856     reset : function(clearGhost){
17857         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17858         this.dropStatus = this.dropNotAllowed;
17859         if(clearGhost){
17860             this.ghost.update("");
17861         }
17862     },
17863
17864     /**
17865      * Updates the contents of the ghost element
17866      * @param {String} html The html that will replace the current innerHTML of the ghost element
17867      */
17868     update : function(html){
17869         if(typeof html == "string"){
17870             this.ghost.update(html);
17871         }else{
17872             this.ghost.update("");
17873             html.style.margin = "0";
17874             this.ghost.dom.appendChild(html);
17875         }
17876         // ensure float = none set?? cant remember why though.
17877         var el = this.ghost.dom.firstChild;
17878                 if(el){
17879                         Roo.fly(el).setStyle('float', 'none');
17880                 }
17881     },
17882     
17883     /**
17884      * Returns the underlying proxy {@link Roo.Layer}
17885      * @return {Roo.Layer} el
17886     */
17887     getEl : function(){
17888         return this.el;
17889     },
17890
17891     /**
17892      * Returns the ghost element
17893      * @return {Roo.Element} el
17894      */
17895     getGhost : function(){
17896         return this.ghost;
17897     },
17898
17899     /**
17900      * Hides the proxy
17901      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17902      */
17903     hide : function(clear){
17904         this.el.hide();
17905         if(clear){
17906             this.reset(true);
17907         }
17908     },
17909
17910     /**
17911      * Stops the repair animation if it's currently running
17912      */
17913     stop : function(){
17914         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17915             this.anim.stop();
17916         }
17917     },
17918
17919     /**
17920      * Displays this proxy
17921      */
17922     show : function(){
17923         this.el.show();
17924     },
17925
17926     /**
17927      * Force the Layer to sync its shadow and shim positions to the element
17928      */
17929     sync : function(){
17930         this.el.sync();
17931     },
17932
17933     /**
17934      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17935      * invalid drop operation by the item being dragged.
17936      * @param {Array} xy The XY position of the element ([x, y])
17937      * @param {Function} callback The function to call after the repair is complete
17938      * @param {Object} scope The scope in which to execute the callback
17939      */
17940     repair : function(xy, callback, scope){
17941         this.callback = callback;
17942         this.scope = scope;
17943         if(xy && this.animRepair !== false){
17944             this.el.addClass("x-dd-drag-repair");
17945             this.el.hideUnders(true);
17946             this.anim = this.el.shift({
17947                 duration: this.repairDuration || .5,
17948                 easing: 'easeOut',
17949                 xy: xy,
17950                 stopFx: true,
17951                 callback: this.afterRepair,
17952                 scope: this
17953             });
17954         }else{
17955             this.afterRepair();
17956         }
17957     },
17958
17959     // private
17960     afterRepair : function(){
17961         this.hide(true);
17962         if(typeof this.callback == "function"){
17963             this.callback.call(this.scope || this);
17964         }
17965         this.callback = null;
17966         this.scope = null;
17967     }
17968 };/*
17969  * Based on:
17970  * Ext JS Library 1.1.1
17971  * Copyright(c) 2006-2007, Ext JS, LLC.
17972  *
17973  * Originally Released Under LGPL - original licence link has changed is not relivant.
17974  *
17975  * Fork - LGPL
17976  * <script type="text/javascript">
17977  */
17978
17979 /**
17980  * @class Roo.dd.DragSource
17981  * @extends Roo.dd.DDProxy
17982  * A simple class that provides the basic implementation needed to make any element draggable.
17983  * @constructor
17984  * @param {String/HTMLElement/Element} el The container element
17985  * @param {Object} config
17986  */
17987 Roo.dd.DragSource = function(el, config){
17988     this.el = Roo.get(el);
17989     this.dragData = {};
17990     
17991     Roo.apply(this, config);
17992     
17993     if(!this.proxy){
17994         this.proxy = new Roo.dd.StatusProxy();
17995     }
17996
17997     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17998           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17999     
18000     this.dragging = false;
18001 };
18002
18003 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18004     /**
18005      * @cfg {String} dropAllowed
18006      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18007      */
18008     dropAllowed : "x-dd-drop-ok",
18009     /**
18010      * @cfg {String} dropNotAllowed
18011      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18012      */
18013     dropNotAllowed : "x-dd-drop-nodrop",
18014
18015     /**
18016      * Returns the data object associated with this drag source
18017      * @return {Object} data An object containing arbitrary data
18018      */
18019     getDragData : function(e){
18020         return this.dragData;
18021     },
18022
18023     // private
18024     onDragEnter : function(e, id){
18025         var target = Roo.dd.DragDropMgr.getDDById(id);
18026         this.cachedTarget = target;
18027         if(this.beforeDragEnter(target, e, id) !== false){
18028             if(target.isNotifyTarget){
18029                 var status = target.notifyEnter(this, e, this.dragData);
18030                 this.proxy.setStatus(status);
18031             }else{
18032                 this.proxy.setStatus(this.dropAllowed);
18033             }
18034             
18035             if(this.afterDragEnter){
18036                 /**
18037                  * An empty function by default, but provided so that you can perform a custom action
18038                  * when the dragged item enters the drop target by providing an implementation.
18039                  * @param {Roo.dd.DragDrop} target The drop target
18040                  * @param {Event} e The event object
18041                  * @param {String} id The id of the dragged element
18042                  * @method afterDragEnter
18043                  */
18044                 this.afterDragEnter(target, e, id);
18045             }
18046         }
18047     },
18048
18049     /**
18050      * An empty function by default, but provided so that you can perform a custom action
18051      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18052      * @param {Roo.dd.DragDrop} target The drop target
18053      * @param {Event} e The event object
18054      * @param {String} id The id of the dragged element
18055      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18056      */
18057     beforeDragEnter : function(target, e, id){
18058         return true;
18059     },
18060
18061     // private
18062     alignElWithMouse: function() {
18063         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18064         this.proxy.sync();
18065     },
18066
18067     // private
18068     onDragOver : function(e, id){
18069         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18070         if(this.beforeDragOver(target, e, id) !== false){
18071             if(target.isNotifyTarget){
18072                 var status = target.notifyOver(this, e, this.dragData);
18073                 this.proxy.setStatus(status);
18074             }
18075
18076             if(this.afterDragOver){
18077                 /**
18078                  * An empty function by default, but provided so that you can perform a custom action
18079                  * while the dragged item is over the drop target by providing an implementation.
18080                  * @param {Roo.dd.DragDrop} target The drop target
18081                  * @param {Event} e The event object
18082                  * @param {String} id The id of the dragged element
18083                  * @method afterDragOver
18084                  */
18085                 this.afterDragOver(target, e, id);
18086             }
18087         }
18088     },
18089
18090     /**
18091      * An empty function by default, but provided so that you can perform a custom action
18092      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18093      * @param {Roo.dd.DragDrop} target The drop target
18094      * @param {Event} e The event object
18095      * @param {String} id The id of the dragged element
18096      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18097      */
18098     beforeDragOver : function(target, e, id){
18099         return true;
18100     },
18101
18102     // private
18103     onDragOut : function(e, id){
18104         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18105         if(this.beforeDragOut(target, e, id) !== false){
18106             if(target.isNotifyTarget){
18107                 target.notifyOut(this, e, this.dragData);
18108             }
18109             this.proxy.reset();
18110             if(this.afterDragOut){
18111                 /**
18112                  * An empty function by default, but provided so that you can perform a custom action
18113                  * after the dragged item is dragged out of the target without dropping.
18114                  * @param {Roo.dd.DragDrop} target The drop target
18115                  * @param {Event} e The event object
18116                  * @param {String} id The id of the dragged element
18117                  * @method afterDragOut
18118                  */
18119                 this.afterDragOut(target, e, id);
18120             }
18121         }
18122         this.cachedTarget = null;
18123     },
18124
18125     /**
18126      * An empty function by default, but provided so that you can perform a custom action before the dragged
18127      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18128      * @param {Roo.dd.DragDrop} target The drop target
18129      * @param {Event} e The event object
18130      * @param {String} id The id of the dragged element
18131      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18132      */
18133     beforeDragOut : function(target, e, id){
18134         return true;
18135     },
18136     
18137     // private
18138     onDragDrop : function(e, id){
18139         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18140         if(this.beforeDragDrop(target, e, id) !== false){
18141             if(target.isNotifyTarget){
18142                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18143                     this.onValidDrop(target, e, id);
18144                 }else{
18145                     this.onInvalidDrop(target, e, id);
18146                 }
18147             }else{
18148                 this.onValidDrop(target, e, id);
18149             }
18150             
18151             if(this.afterDragDrop){
18152                 /**
18153                  * An empty function by default, but provided so that you can perform a custom action
18154                  * after a valid drag drop has occurred by providing an implementation.
18155                  * @param {Roo.dd.DragDrop} target The drop target
18156                  * @param {Event} e The event object
18157                  * @param {String} id The id of the dropped element
18158                  * @method afterDragDrop
18159                  */
18160                 this.afterDragDrop(target, e, id);
18161             }
18162         }
18163         delete this.cachedTarget;
18164     },
18165
18166     /**
18167      * An empty function by default, but provided so that you can perform a custom action before the dragged
18168      * item is dropped onto the target and optionally cancel the onDragDrop.
18169      * @param {Roo.dd.DragDrop} target The drop target
18170      * @param {Event} e The event object
18171      * @param {String} id The id of the dragged element
18172      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18173      */
18174     beforeDragDrop : function(target, e, id){
18175         return true;
18176     },
18177
18178     // private
18179     onValidDrop : function(target, e, id){
18180         this.hideProxy();
18181         if(this.afterValidDrop){
18182             /**
18183              * An empty function by default, but provided so that you can perform a custom action
18184              * after a valid drop has occurred by providing an implementation.
18185              * @param {Object} target The target DD 
18186              * @param {Event} e The event object
18187              * @param {String} id The id of the dropped element
18188              * @method afterInvalidDrop
18189              */
18190             this.afterValidDrop(target, e, id);
18191         }
18192     },
18193
18194     // private
18195     getRepairXY : function(e, data){
18196         return this.el.getXY();  
18197     },
18198
18199     // private
18200     onInvalidDrop : function(target, e, id){
18201         this.beforeInvalidDrop(target, e, id);
18202         if(this.cachedTarget){
18203             if(this.cachedTarget.isNotifyTarget){
18204                 this.cachedTarget.notifyOut(this, e, this.dragData);
18205             }
18206             this.cacheTarget = null;
18207         }
18208         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18209
18210         if(this.afterInvalidDrop){
18211             /**
18212              * An empty function by default, but provided so that you can perform a custom action
18213              * after an invalid drop has occurred by providing an implementation.
18214              * @param {Event} e The event object
18215              * @param {String} id The id of the dropped element
18216              * @method afterInvalidDrop
18217              */
18218             this.afterInvalidDrop(e, id);
18219         }
18220     },
18221
18222     // private
18223     afterRepair : function(){
18224         if(Roo.enableFx){
18225             this.el.highlight(this.hlColor || "c3daf9");
18226         }
18227         this.dragging = false;
18228     },
18229
18230     /**
18231      * An empty function by default, but provided so that you can perform a custom action after an invalid
18232      * drop has occurred.
18233      * @param {Roo.dd.DragDrop} target The drop target
18234      * @param {Event} e The event object
18235      * @param {String} id The id of the dragged element
18236      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18237      */
18238     beforeInvalidDrop : function(target, e, id){
18239         return true;
18240     },
18241
18242     // private
18243     handleMouseDown : function(e){
18244         if(this.dragging) {
18245             return;
18246         }
18247         var data = this.getDragData(e);
18248         if(data && this.onBeforeDrag(data, e) !== false){
18249             this.dragData = data;
18250             this.proxy.stop();
18251             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18252         } 
18253     },
18254
18255     /**
18256      * An empty function by default, but provided so that you can perform a custom action before the initial
18257      * drag event begins and optionally cancel it.
18258      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18259      * @param {Event} e The event object
18260      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18261      */
18262     onBeforeDrag : function(data, e){
18263         return true;
18264     },
18265
18266     /**
18267      * An empty function by default, but provided so that you can perform a custom action once the initial
18268      * drag event has begun.  The drag cannot be canceled from this function.
18269      * @param {Number} x The x position of the click on the dragged object
18270      * @param {Number} y The y position of the click on the dragged object
18271      */
18272     onStartDrag : Roo.emptyFn,
18273
18274     // private - YUI override
18275     startDrag : function(x, y){
18276         this.proxy.reset();
18277         this.dragging = true;
18278         this.proxy.update("");
18279         this.onInitDrag(x, y);
18280         this.proxy.show();
18281     },
18282
18283     // private
18284     onInitDrag : function(x, y){
18285         var clone = this.el.dom.cloneNode(true);
18286         clone.id = Roo.id(); // prevent duplicate ids
18287         this.proxy.update(clone);
18288         this.onStartDrag(x, y);
18289         return true;
18290     },
18291
18292     /**
18293      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18294      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18295      */
18296     getProxy : function(){
18297         return this.proxy;  
18298     },
18299
18300     /**
18301      * Hides the drag source's {@link Roo.dd.StatusProxy}
18302      */
18303     hideProxy : function(){
18304         this.proxy.hide();  
18305         this.proxy.reset(true);
18306         this.dragging = false;
18307     },
18308
18309     // private
18310     triggerCacheRefresh : function(){
18311         Roo.dd.DDM.refreshCache(this.groups);
18312     },
18313
18314     // private - override to prevent hiding
18315     b4EndDrag: function(e) {
18316     },
18317
18318     // private - override to prevent moving
18319     endDrag : function(e){
18320         this.onEndDrag(this.dragData, e);
18321     },
18322
18323     // private
18324     onEndDrag : function(data, e){
18325     },
18326     
18327     // private - pin to cursor
18328     autoOffset : function(x, y) {
18329         this.setDelta(-12, -20);
18330     }    
18331 });/*
18332  * Based on:
18333  * Ext JS Library 1.1.1
18334  * Copyright(c) 2006-2007, Ext JS, LLC.
18335  *
18336  * Originally Released Under LGPL - original licence link has changed is not relivant.
18337  *
18338  * Fork - LGPL
18339  * <script type="text/javascript">
18340  */
18341
18342
18343 /**
18344  * @class Roo.dd.DropTarget
18345  * @extends Roo.dd.DDTarget
18346  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18347  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18348  * @constructor
18349  * @param {String/HTMLElement/Element} el The container element
18350  * @param {Object} config
18351  */
18352 Roo.dd.DropTarget = function(el, config){
18353     this.el = Roo.get(el);
18354     
18355     var listeners = false; ;
18356     if (config && config.listeners) {
18357         listeners= config.listeners;
18358         delete config.listeners;
18359     }
18360     Roo.apply(this, config);
18361     
18362     if(this.containerScroll){
18363         Roo.dd.ScrollManager.register(this.el);
18364     }
18365     this.addEvents( {
18366          /**
18367          * @scope Roo.dd.DropTarget
18368          */
18369          
18370          /**
18371          * @event enter
18372          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18373          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18374          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18375          * 
18376          * IMPORTANT : it should set this.overClass and this.dropAllowed
18377          * 
18378          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18379          * @param {Event} e The event
18380          * @param {Object} data An object containing arbitrary data supplied by the drag source
18381          */
18382         "enter" : true,
18383         
18384          /**
18385          * @event over
18386          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18387          * This method will be called on every mouse movement while the drag source is over the drop target.
18388          * This default implementation simply returns the dropAllowed config value.
18389          * 
18390          * IMPORTANT : it should set this.dropAllowed
18391          * 
18392          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18393          * @param {Event} e The event
18394          * @param {Object} data An object containing arbitrary data supplied by the drag source
18395          
18396          */
18397         "over" : true,
18398         /**
18399          * @event out
18400          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18401          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18402          * overClass (if any) from the drop element.
18403          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18404          * @param {Event} e The event
18405          * @param {Object} data An object containing arbitrary data supplied by the drag source
18406          */
18407          "out" : true,
18408          
18409         /**
18410          * @event drop
18411          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18412          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18413          * implementation that does something to process the drop event and returns true so that the drag source's
18414          * repair action does not run.
18415          * 
18416          * IMPORTANT : it should set this.success
18417          * 
18418          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18419          * @param {Event} e The event
18420          * @param {Object} data An object containing arbitrary data supplied by the drag source
18421         */
18422          "drop" : true
18423     });
18424             
18425      
18426     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18427         this.el.dom, 
18428         this.ddGroup || this.group,
18429         {
18430             isTarget: true,
18431             listeners : listeners || {} 
18432            
18433         
18434         }
18435     );
18436
18437 };
18438
18439 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18440     /**
18441      * @cfg {String} overClass
18442      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18443      */
18444      /**
18445      * @cfg {String} ddGroup
18446      * The drag drop group to handle drop events for
18447      */
18448      
18449     /**
18450      * @cfg {String} dropAllowed
18451      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18452      */
18453     dropAllowed : "x-dd-drop-ok",
18454     /**
18455      * @cfg {String} dropNotAllowed
18456      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18457      */
18458     dropNotAllowed : "x-dd-drop-nodrop",
18459     /**
18460      * @cfg {boolean} success
18461      * set this after drop listener.. 
18462      */
18463     success : false,
18464     /**
18465      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18466      * if the drop point is valid for over/enter..
18467      */
18468     valid : false,
18469     // private
18470     isTarget : true,
18471
18472     // private
18473     isNotifyTarget : true,
18474     
18475     /**
18476      * @hide
18477      */
18478     notifyEnter : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('enter', dd, e, data);
18482         if(this.overClass){
18483             this.el.addClass(this.overClass);
18484         }
18485         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18486             this.valid ? this.dropAllowed : this.dropNotAllowed
18487         );
18488     },
18489
18490     /**
18491      * @hide
18492      */
18493     notifyOver : function(dd, e, data)
18494     {
18495         this.valid = true;
18496         this.fireEvent('over', dd, e, data);
18497         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18498             this.valid ? this.dropAllowed : this.dropNotAllowed
18499         );
18500     },
18501
18502     /**
18503      * @hide
18504      */
18505     notifyOut : function(dd, e, data)
18506     {
18507         this.fireEvent('out', dd, e, data);
18508         if(this.overClass){
18509             this.el.removeClass(this.overClass);
18510         }
18511     },
18512
18513     /**
18514      * @hide
18515      */
18516     notifyDrop : function(dd, e, data)
18517     {
18518         this.success = false;
18519         this.fireEvent('drop', dd, e, data);
18520         return this.success;
18521     }
18522 });/*
18523  * Based on:
18524  * Ext JS Library 1.1.1
18525  * Copyright(c) 2006-2007, Ext JS, LLC.
18526  *
18527  * Originally Released Under LGPL - original licence link has changed is not relivant.
18528  *
18529  * Fork - LGPL
18530  * <script type="text/javascript">
18531  */
18532
18533
18534 /**
18535  * @class Roo.dd.DragZone
18536  * @extends Roo.dd.DragSource
18537  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18538  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18539  * @constructor
18540  * @param {String/HTMLElement/Element} el The container element
18541  * @param {Object} config
18542  */
18543 Roo.dd.DragZone = function(el, config){
18544     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18545     if(this.containerScroll){
18546         Roo.dd.ScrollManager.register(this.el);
18547     }
18548 };
18549
18550 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18551     /**
18552      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18553      * for auto scrolling during drag operations.
18554      */
18555     /**
18556      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18557      * method after a failed drop (defaults to "c3daf9" - light blue)
18558      */
18559
18560     /**
18561      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18562      * for a valid target to drag based on the mouse down. Override this method
18563      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18564      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18565      * @param {EventObject} e The mouse down event
18566      * @return {Object} The dragData
18567      */
18568     getDragData : function(e){
18569         return Roo.dd.Registry.getHandleFromEvent(e);
18570     },
18571     
18572     /**
18573      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18574      * this.dragData.ddel
18575      * @param {Number} x The x position of the click on the dragged object
18576      * @param {Number} y The y position of the click on the dragged object
18577      * @return {Boolean} true to continue the drag, false to cancel
18578      */
18579     onInitDrag : function(x, y){
18580         this.proxy.update(this.dragData.ddel.cloneNode(true));
18581         this.onStartDrag(x, y);
18582         return true;
18583     },
18584     
18585     /**
18586      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18587      */
18588     afterRepair : function(){
18589         if(Roo.enableFx){
18590             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18591         }
18592         this.dragging = false;
18593     },
18594
18595     /**
18596      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18597      * the XY of this.dragData.ddel
18598      * @param {EventObject} e The mouse up event
18599      * @return {Array} The xy location (e.g. [100, 200])
18600      */
18601     getRepairXY : function(e){
18602         return Roo.Element.fly(this.dragData.ddel).getXY();  
18603     }
18604 });/*
18605  * Based on:
18606  * Ext JS Library 1.1.1
18607  * Copyright(c) 2006-2007, Ext JS, LLC.
18608  *
18609  * Originally Released Under LGPL - original licence link has changed is not relivant.
18610  *
18611  * Fork - LGPL
18612  * <script type="text/javascript">
18613  */
18614 /**
18615  * @class Roo.dd.DropZone
18616  * @extends Roo.dd.DropTarget
18617  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18618  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18619  * @constructor
18620  * @param {String/HTMLElement/Element} el The container element
18621  * @param {Object} config
18622  */
18623 Roo.dd.DropZone = function(el, config){
18624     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18625 };
18626
18627 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18628     /**
18629      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18630      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18631      * provide your own custom lookup.
18632      * @param {Event} e The event
18633      * @return {Object} data The custom data
18634      */
18635     getTargetFromEvent : function(e){
18636         return Roo.dd.Registry.getTargetFromEvent(e);
18637     },
18638
18639     /**
18640      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18641      * that it has registered.  This method has no default implementation and should be overridden to provide
18642      * node-specific processing if necessary.
18643      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18644      * {@link #getTargetFromEvent} for this node)
18645      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18646      * @param {Event} e The event
18647      * @param {Object} data An object containing arbitrary data supplied by the drag source
18648      */
18649     onNodeEnter : function(n, dd, e, data){
18650         
18651     },
18652
18653     /**
18654      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18655      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18656      * overridden to provide the proper feedback.
18657      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18658      * {@link #getTargetFromEvent} for this node)
18659      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18660      * @param {Event} e The event
18661      * @param {Object} data An object containing arbitrary data supplied by the drag source
18662      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18663      * underlying {@link Roo.dd.StatusProxy} can be updated
18664      */
18665     onNodeOver : function(n, dd, e, data){
18666         return this.dropAllowed;
18667     },
18668
18669     /**
18670      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18671      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18672      * node-specific processing if necessary.
18673      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18674      * {@link #getTargetFromEvent} for this node)
18675      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18676      * @param {Event} e The event
18677      * @param {Object} data An object containing arbitrary data supplied by the drag source
18678      */
18679     onNodeOut : function(n, dd, e, data){
18680         
18681     },
18682
18683     /**
18684      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18685      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18686      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18687      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18688      * {@link #getTargetFromEvent} for this node)
18689      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18690      * @param {Event} e The event
18691      * @param {Object} data An object containing arbitrary data supplied by the drag source
18692      * @return {Boolean} True if the drop was valid, else false
18693      */
18694     onNodeDrop : function(n, dd, e, data){
18695         return false;
18696     },
18697
18698     /**
18699      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18700      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18701      * it should be overridden to provide the proper feedback if necessary.
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18706      * underlying {@link Roo.dd.StatusProxy} can be updated
18707      */
18708     onContainerOver : function(dd, e, data){
18709         return this.dropNotAllowed;
18710     },
18711
18712     /**
18713      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18714      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18715      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18716      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      * @return {Boolean} True if the drop was valid, else false
18721      */
18722     onContainerDrop : function(dd, e, data){
18723         return false;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18728      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18729      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18730      * you should override this method and provide a custom implementation.
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18735      * underlying {@link Roo.dd.StatusProxy} can be updated
18736      */
18737     notifyEnter : function(dd, e, data){
18738         return this.dropNotAllowed;
18739     },
18740
18741     /**
18742      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18743      * This method will be called on every mouse movement while the drag source is over the drop zone.
18744      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18745      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18746      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18747      * registered node, it will call {@link #onContainerOver}.
18748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18749      * @param {Event} e The event
18750      * @param {Object} data An object containing arbitrary data supplied by the drag source
18751      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18752      * underlying {@link Roo.dd.StatusProxy} can be updated
18753      */
18754     notifyOver : function(dd, e, data){
18755         var n = this.getTargetFromEvent(e);
18756         if(!n){ // not over valid drop target
18757             if(this.lastOverNode){
18758                 this.onNodeOut(this.lastOverNode, dd, e, data);
18759                 this.lastOverNode = null;
18760             }
18761             return this.onContainerOver(dd, e, data);
18762         }
18763         if(this.lastOverNode != n){
18764             if(this.lastOverNode){
18765                 this.onNodeOut(this.lastOverNode, dd, e, data);
18766             }
18767             this.onNodeEnter(n, dd, e, data);
18768             this.lastOverNode = n;
18769         }
18770         return this.onNodeOver(n, dd, e, data);
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18775      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18776      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18777      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18778      * @param {Event} e The event
18779      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18780      */
18781     notifyOut : function(dd, e, data){
18782         if(this.lastOverNode){
18783             this.onNodeOut(this.lastOverNode, dd, e, data);
18784             this.lastOverNode = null;
18785         }
18786     },
18787
18788     /**
18789      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18790      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18791      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18792      * otherwise it will call {@link #onContainerDrop}.
18793      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18794      * @param {Event} e The event
18795      * @param {Object} data An object containing arbitrary data supplied by the drag source
18796      * @return {Boolean} True if the drop was valid, else false
18797      */
18798     notifyDrop : function(dd, e, data){
18799         if(this.lastOverNode){
18800             this.onNodeOut(this.lastOverNode, dd, e, data);
18801             this.lastOverNode = null;
18802         }
18803         var n = this.getTargetFromEvent(e);
18804         return n ?
18805             this.onNodeDrop(n, dd, e, data) :
18806             this.onContainerDrop(dd, e, data);
18807     },
18808
18809     // private
18810     triggerCacheRefresh : function(){
18811         Roo.dd.DDM.refreshCache(this.groups);
18812     }  
18813 });/*
18814  * Based on:
18815  * Ext JS Library 1.1.1
18816  * Copyright(c) 2006-2007, Ext JS, LLC.
18817  *
18818  * Originally Released Under LGPL - original licence link has changed is not relivant.
18819  *
18820  * Fork - LGPL
18821  * <script type="text/javascript">
18822  */
18823
18824
18825 /**
18826  * @class Roo.data.SortTypes
18827  * @singleton
18828  * Defines the default sorting (casting?) comparison functions used when sorting data.
18829  */
18830 Roo.data.SortTypes = {
18831     /**
18832      * Default sort that does nothing
18833      * @param {Mixed} s The value being converted
18834      * @return {Mixed} The comparison value
18835      */
18836     none : function(s){
18837         return s;
18838     },
18839     
18840     /**
18841      * The regular expression used to strip tags
18842      * @type {RegExp}
18843      * @property
18844      */
18845     stripTagsRE : /<\/?[^>]+>/gi,
18846     
18847     /**
18848      * Strips all HTML tags to sort on text only
18849      * @param {Mixed} s The value being converted
18850      * @return {String} The comparison value
18851      */
18852     asText : function(s){
18853         return String(s).replace(this.stripTagsRE, "");
18854     },
18855     
18856     /**
18857      * Strips all HTML tags to sort on text only - Case insensitive
18858      * @param {Mixed} s The value being converted
18859      * @return {String} The comparison value
18860      */
18861     asUCText : function(s){
18862         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18863     },
18864     
18865     /**
18866      * Case insensitive string
18867      * @param {Mixed} s The value being converted
18868      * @return {String} The comparison value
18869      */
18870     asUCString : function(s) {
18871         return String(s).toUpperCase();
18872     },
18873     
18874     /**
18875      * Date sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Number} The comparison value
18878      */
18879     asDate : function(s) {
18880         if(!s){
18881             return 0;
18882         }
18883         if(s instanceof Date){
18884             return s.getTime();
18885         }
18886         return Date.parse(String(s));
18887     },
18888     
18889     /**
18890      * Float sorting
18891      * @param {Mixed} s The value being converted
18892      * @return {Float} The comparison value
18893      */
18894     asFloat : function(s) {
18895         var val = parseFloat(String(s).replace(/,/g, ""));
18896         if(isNaN(val)) val = 0;
18897         return val;
18898     },
18899     
18900     /**
18901      * Integer sorting
18902      * @param {Mixed} s The value being converted
18903      * @return {Number} The comparison value
18904      */
18905     asInt : function(s) {
18906         var val = parseInt(String(s).replace(/,/g, ""));
18907         if(isNaN(val)) val = 0;
18908         return val;
18909     }
18910 };/*
18911  * Based on:
18912  * Ext JS Library 1.1.1
18913  * Copyright(c) 2006-2007, Ext JS, LLC.
18914  *
18915  * Originally Released Under LGPL - original licence link has changed is not relivant.
18916  *
18917  * Fork - LGPL
18918  * <script type="text/javascript">
18919  */
18920
18921 /**
18922 * @class Roo.data.Record
18923  * Instances of this class encapsulate both record <em>definition</em> information, and record
18924  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18925  * to access Records cached in an {@link Roo.data.Store} object.<br>
18926  * <p>
18927  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18928  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18929  * objects.<br>
18930  * <p>
18931  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18932  * @constructor
18933  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18934  * {@link #create}. The parameters are the same.
18935  * @param {Array} data An associative Array of data values keyed by the field name.
18936  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18937  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18938  * not specified an integer id is generated.
18939  */
18940 Roo.data.Record = function(data, id){
18941     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18942     this.data = data;
18943 };
18944
18945 /**
18946  * Generate a constructor for a specific record layout.
18947  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18948  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18949  * Each field definition object may contain the following properties: <ul>
18950  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18951  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18952  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18953  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18954  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18955  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18956  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18957  * this may be omitted.</p></li>
18958  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18959  * <ul><li>auto (Default, implies no conversion)</li>
18960  * <li>string</li>
18961  * <li>int</li>
18962  * <li>float</li>
18963  * <li>boolean</li>
18964  * <li>date</li></ul></p></li>
18965  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18966  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18967  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18968  * by the Reader into an object that will be stored in the Record. It is passed the
18969  * following parameters:<ul>
18970  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18971  * </ul></p></li>
18972  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18973  * </ul>
18974  * <br>usage:<br><pre><code>
18975 var TopicRecord = Roo.data.Record.create(
18976     {name: 'title', mapping: 'topic_title'},
18977     {name: 'author', mapping: 'username'},
18978     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18979     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18980     {name: 'lastPoster', mapping: 'user2'},
18981     {name: 'excerpt', mapping: 'post_text'}
18982 );
18983
18984 var myNewRecord = new TopicRecord({
18985     title: 'Do my job please',
18986     author: 'noobie',
18987     totalPosts: 1,
18988     lastPost: new Date(),
18989     lastPoster: 'Animal',
18990     excerpt: 'No way dude!'
18991 });
18992 myStore.add(myNewRecord);
18993 </code></pre>
18994  * @method create
18995  * @static
18996  */
18997 Roo.data.Record.create = function(o){
18998     var f = function(){
18999         f.superclass.constructor.apply(this, arguments);
19000     };
19001     Roo.extend(f, Roo.data.Record);
19002     var p = f.prototype;
19003     p.fields = new Roo.util.MixedCollection(false, function(field){
19004         return field.name;
19005     });
19006     for(var i = 0, len = o.length; i < len; i++){
19007         p.fields.add(new Roo.data.Field(o[i]));
19008     }
19009     f.getField = function(name){
19010         return p.fields.get(name);  
19011     };
19012     return f;
19013 };
19014
19015 Roo.data.Record.AUTO_ID = 1000;
19016 Roo.data.Record.EDIT = 'edit';
19017 Roo.data.Record.REJECT = 'reject';
19018 Roo.data.Record.COMMIT = 'commit';
19019
19020 Roo.data.Record.prototype = {
19021     /**
19022      * Readonly flag - true if this record has been modified.
19023      * @type Boolean
19024      */
19025     dirty : false,
19026     editing : false,
19027     error: null,
19028     modified: null,
19029
19030     // private
19031     join : function(store){
19032         this.store = store;
19033     },
19034
19035     /**
19036      * Set the named field to the specified value.
19037      * @param {String} name The name of the field to set.
19038      * @param {Object} value The value to set the field to.
19039      */
19040     set : function(name, value){
19041         if(this.data[name] == value){
19042             return;
19043         }
19044         this.dirty = true;
19045         if(!this.modified){
19046             this.modified = {};
19047         }
19048         if(typeof this.modified[name] == 'undefined'){
19049             this.modified[name] = this.data[name];
19050         }
19051         this.data[name] = value;
19052         if(!this.editing){
19053             this.store.afterEdit(this);
19054         }       
19055     },
19056
19057     /**
19058      * Get the value of the named field.
19059      * @param {String} name The name of the field to get the value of.
19060      * @return {Object} The value of the field.
19061      */
19062     get : function(name){
19063         return this.data[name]; 
19064     },
19065
19066     // private
19067     beginEdit : function(){
19068         this.editing = true;
19069         this.modified = {}; 
19070     },
19071
19072     // private
19073     cancelEdit : function(){
19074         this.editing = false;
19075         delete this.modified;
19076     },
19077
19078     // private
19079     endEdit : function(){
19080         this.editing = false;
19081         if(this.dirty && this.store){
19082             this.store.afterEdit(this);
19083         }
19084     },
19085
19086     /**
19087      * Usually called by the {@link Roo.data.Store} which owns the Record.
19088      * Rejects all changes made to the Record since either creation, or the last commit operation.
19089      * Modified fields are reverted to their original values.
19090      * <p>
19091      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19092      * of reject operations.
19093      */
19094     reject : function(){
19095         var m = this.modified;
19096         for(var n in m){
19097             if(typeof m[n] != "function"){
19098                 this.data[n] = m[n];
19099             }
19100         }
19101         this.dirty = false;
19102         delete this.modified;
19103         this.editing = false;
19104         if(this.store){
19105             this.store.afterReject(this);
19106         }
19107     },
19108
19109     /**
19110      * Usually called by the {@link Roo.data.Store} which owns the Record.
19111      * Commits all changes made to the Record since either creation, or the last commit operation.
19112      * <p>
19113      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19114      * of commit operations.
19115      */
19116     commit : function(){
19117         this.dirty = false;
19118         delete this.modified;
19119         this.editing = false;
19120         if(this.store){
19121             this.store.afterCommit(this);
19122         }
19123     },
19124
19125     // private
19126     hasError : function(){
19127         return this.error != null;
19128     },
19129
19130     // private
19131     clearError : function(){
19132         this.error = null;
19133     },
19134
19135     /**
19136      * Creates a copy of this record.
19137      * @param {String} id (optional) A new record id if you don't want to use this record's id
19138      * @return {Record}
19139      */
19140     copy : function(newId) {
19141         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19142     }
19143 };/*
19144  * Based on:
19145  * Ext JS Library 1.1.1
19146  * Copyright(c) 2006-2007, Ext JS, LLC.
19147  *
19148  * Originally Released Under LGPL - original licence link has changed is not relivant.
19149  *
19150  * Fork - LGPL
19151  * <script type="text/javascript">
19152  */
19153
19154
19155
19156 /**
19157  * @class Roo.data.Store
19158  * @extends Roo.util.Observable
19159  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19160  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19161  * <p>
19162  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19163  * has no knowledge of the format of the data returned by the Proxy.<br>
19164  * <p>
19165  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19166  * instances from the data object. These records are cached and made available through accessor functions.
19167  * @constructor
19168  * Creates a new Store.
19169  * @param {Object} config A config object containing the objects needed for the Store to access data,
19170  * and read the data into Records.
19171  */
19172 Roo.data.Store = function(config){
19173     this.data = new Roo.util.MixedCollection(false);
19174     this.data.getKey = function(o){
19175         return o.id;
19176     };
19177     this.baseParams = {};
19178     // private
19179     this.paramNames = {
19180         "start" : "start",
19181         "limit" : "limit",
19182         "sort" : "sort",
19183         "dir" : "dir",
19184         "multisort" : "_multisort"
19185     };
19186
19187     if(config && config.data){
19188         this.inlineData = config.data;
19189         delete config.data;
19190     }
19191
19192     Roo.apply(this, config);
19193     
19194     if(this.reader){ // reader passed
19195         this.reader = Roo.factory(this.reader, Roo.data);
19196         this.reader.xmodule = this.xmodule || false;
19197         if(!this.recordType){
19198             this.recordType = this.reader.recordType;
19199         }
19200         if(this.reader.onMetaChange){
19201             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19202         }
19203     }
19204
19205     if(this.recordType){
19206         this.fields = this.recordType.prototype.fields;
19207     }
19208     this.modified = [];
19209
19210     this.addEvents({
19211         /**
19212          * @event datachanged
19213          * Fires when the data cache has changed, and a widget which is using this Store
19214          * as a Record cache should refresh its view.
19215          * @param {Store} this
19216          */
19217         datachanged : true,
19218         /**
19219          * @event metachange
19220          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19221          * @param {Store} this
19222          * @param {Object} meta The JSON metadata
19223          */
19224         metachange : true,
19225         /**
19226          * @event add
19227          * Fires when Records have been added to the Store
19228          * @param {Store} this
19229          * @param {Roo.data.Record[]} records The array of Records added
19230          * @param {Number} index The index at which the record(s) were added
19231          */
19232         add : true,
19233         /**
19234          * @event remove
19235          * Fires when a Record has been removed from the Store
19236          * @param {Store} this
19237          * @param {Roo.data.Record} record The Record that was removed
19238          * @param {Number} index The index at which the record was removed
19239          */
19240         remove : true,
19241         /**
19242          * @event update
19243          * Fires when a Record has been updated
19244          * @param {Store} this
19245          * @param {Roo.data.Record} record The Record that was updated
19246          * @param {String} operation The update operation being performed.  Value may be one of:
19247          * <pre><code>
19248  Roo.data.Record.EDIT
19249  Roo.data.Record.REJECT
19250  Roo.data.Record.COMMIT
19251          * </code></pre>
19252          */
19253         update : true,
19254         /**
19255          * @event clear
19256          * Fires when the data cache has been cleared.
19257          * @param {Store} this
19258          */
19259         clear : true,
19260         /**
19261          * @event beforeload
19262          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19263          * the load action will be canceled.
19264          * @param {Store} this
19265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19266          */
19267         beforeload : true,
19268         /**
19269          * @event load
19270          * Fires after a new set of Records has been loaded.
19271          * @param {Store} this
19272          * @param {Roo.data.Record[]} records The Records that were loaded
19273          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19274          */
19275         load : true,
19276         /**
19277          * @event loadexception
19278          * Fires if an exception occurs in the Proxy during loading.
19279          * Called with the signature of the Proxy's "loadexception" event.
19280          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19281          * 
19282          * @param {Proxy} 
19283          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19284          * @param {Object} load options 
19285          * @param {Object} jsonData from your request (normally this contains the Exception)
19286          */
19287         loadexception : true
19288     });
19289     
19290     if(this.proxy){
19291         this.proxy = Roo.factory(this.proxy, Roo.data);
19292         this.proxy.xmodule = this.xmodule || false;
19293         this.relayEvents(this.proxy,  ["loadexception"]);
19294     }
19295     this.sortToggle = {};
19296     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19297
19298     Roo.data.Store.superclass.constructor.call(this);
19299
19300     if(this.inlineData){
19301         this.loadData(this.inlineData);
19302         delete this.inlineData;
19303     }
19304 };
19305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19306      /**
19307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19308     * without a remote query - used by combo/forms at present.
19309     */
19310     
19311     /**
19312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19313     */
19314     /**
19315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19316     */
19317     /**
19318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19320     */
19321     /**
19322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19323     * on any HTTP request
19324     */
19325     /**
19326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19327     */
19328     /**
19329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19330     */
19331     multiSort: false,
19332     /**
19333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19335     */
19336     remoteSort : false,
19337
19338     /**
19339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19340      * loaded or when a record is removed. (defaults to false).
19341     */
19342     pruneModifiedRecords : false,
19343
19344     // private
19345     lastOptions : null,
19346
19347     /**
19348      * Add Records to the Store and fires the add event.
19349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19350      */
19351     add : function(records){
19352         records = [].concat(records);
19353         for(var i = 0, len = records.length; i < len; i++){
19354             records[i].join(this);
19355         }
19356         var index = this.data.length;
19357         this.data.addAll(records);
19358         this.fireEvent("add", this, records, index);
19359     },
19360
19361     /**
19362      * Remove a Record from the Store and fires the remove event.
19363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19364      */
19365     remove : function(record){
19366         var index = this.data.indexOf(record);
19367         this.data.removeAt(index);
19368         if(this.pruneModifiedRecords){
19369             this.modified.remove(record);
19370         }
19371         this.fireEvent("remove", this, record, index);
19372     },
19373
19374     /**
19375      * Remove all Records from the Store and fires the clear event.
19376      */
19377     removeAll : function(){
19378         this.data.clear();
19379         if(this.pruneModifiedRecords){
19380             this.modified = [];
19381         }
19382         this.fireEvent("clear", this);
19383     },
19384
19385     /**
19386      * Inserts Records to the Store at the given index and fires the add event.
19387      * @param {Number} index The start index at which to insert the passed Records.
19388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19389      */
19390     insert : function(index, records){
19391         records = [].concat(records);
19392         for(var i = 0, len = records.length; i < len; i++){
19393             this.data.insert(index, records[i]);
19394             records[i].join(this);
19395         }
19396         this.fireEvent("add", this, records, index);
19397     },
19398
19399     /**
19400      * Get the index within the cache of the passed Record.
19401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19402      * @return {Number} The index of the passed Record. Returns -1 if not found.
19403      */
19404     indexOf : function(record){
19405         return this.data.indexOf(record);
19406     },
19407
19408     /**
19409      * Get the index within the cache of the Record with the passed id.
19410      * @param {String} id The id of the Record to find.
19411      * @return {Number} The index of the Record. Returns -1 if not found.
19412      */
19413     indexOfId : function(id){
19414         return this.data.indexOfKey(id);
19415     },
19416
19417     /**
19418      * Get the Record with the specified id.
19419      * @param {String} id The id of the Record to find.
19420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19421      */
19422     getById : function(id){
19423         return this.data.key(id);
19424     },
19425
19426     /**
19427      * Get the Record at the specified index.
19428      * @param {Number} index The index of the Record to find.
19429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19430      */
19431     getAt : function(index){
19432         return this.data.itemAt(index);
19433     },
19434
19435     /**
19436      * Returns a range of Records between specified indices.
19437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19439      * @return {Roo.data.Record[]} An array of Records
19440      */
19441     getRange : function(start, end){
19442         return this.data.getRange(start, end);
19443     },
19444
19445     // private
19446     storeOptions : function(o){
19447         o = Roo.apply({}, o);
19448         delete o.callback;
19449         delete o.scope;
19450         this.lastOptions = o;
19451     },
19452
19453     /**
19454      * Loads the Record cache from the configured Proxy using the configured Reader.
19455      * <p>
19456      * If using remote paging, then the first load call must specify the <em>start</em>
19457      * and <em>limit</em> properties in the options.params property to establish the initial
19458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19459      * <p>
19460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19461      * and this call will return before the new data has been loaded. Perform any post-processing
19462      * in a callback function, or in a "load" event handler.</strong>
19463      * <p>
19464      * @param {Object} options An object containing properties which control loading options:<ul>
19465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19467      * passed the following arguments:<ul>
19468      * <li>r : Roo.data.Record[]</li>
19469      * <li>options: Options object from the load call</li>
19470      * <li>success: Boolean success indicator</li></ul></li>
19471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19473      * </ul>
19474      */
19475     load : function(options){
19476         options = options || {};
19477         if(this.fireEvent("beforeload", this, options) !== false){
19478             this.storeOptions(options);
19479             var p = Roo.apply(options.params || {}, this.baseParams);
19480             // if meta was not loaded from remote source.. try requesting it.
19481             if (!this.reader.metaFromRemote) {
19482                 p._requestMeta = 1;
19483             }
19484             if(this.sortInfo && this.remoteSort){
19485                 var pn = this.paramNames;
19486                 p[pn["sort"]] = this.sortInfo.field;
19487                 p[pn["dir"]] = this.sortInfo.direction;
19488             }
19489             if (this.multiSort) {
19490                 var pn = this.paramNames;
19491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19492             }
19493             
19494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19495         }
19496     },
19497
19498     /**
19499      * Reloads the Record cache from the configured Proxy using the configured Reader and
19500      * the options from the last load operation performed.
19501      * @param {Object} options (optional) An object containing properties which may override the options
19502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19503      * the most recently used options are reused).
19504      */
19505     reload : function(options){
19506         this.load(Roo.applyIf(options||{}, this.lastOptions));
19507     },
19508
19509     // private
19510     // Called as a callback by the Reader during a load operation.
19511     loadRecords : function(o, options, success){
19512         if(!o || success === false){
19513             if(success !== false){
19514                 this.fireEvent("load", this, [], options);
19515             }
19516             if(options.callback){
19517                 options.callback.call(options.scope || this, [], options, false);
19518             }
19519             return;
19520         }
19521         // if data returned failure - throw an exception.
19522         if (o.success === false) {
19523             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19524             return;
19525         }
19526         var r = o.records, t = o.totalRecords || r.length;
19527         if(!options || options.add !== true){
19528             if(this.pruneModifiedRecords){
19529                 this.modified = [];
19530             }
19531             for(var i = 0, len = r.length; i < len; i++){
19532                 r[i].join(this);
19533             }
19534             if(this.snapshot){
19535                 this.data = this.snapshot;
19536                 delete this.snapshot;
19537             }
19538             this.data.clear();
19539             this.data.addAll(r);
19540             this.totalLength = t;
19541             this.applySort();
19542             this.fireEvent("datachanged", this);
19543         }else{
19544             this.totalLength = Math.max(t, this.data.length+r.length);
19545             this.add(r);
19546         }
19547         this.fireEvent("load", this, r, options);
19548         if(options.callback){
19549             options.callback.call(options.scope || this, r, options, true);
19550         }
19551     },
19552
19553     /**
19554      * Loads data from a passed data block. A Reader which understands the format of the data
19555      * must have been configured in the constructor.
19556      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19557      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19558      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19559      */
19560     loadData : function(o, append){
19561         var r = this.reader.readRecords(o);
19562         this.loadRecords(r, {add: append}, true);
19563     },
19564
19565     /**
19566      * Gets the number of cached records.
19567      * <p>
19568      * <em>If using paging, this may not be the total size of the dataset. If the data object
19569      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19570      * the data set size</em>
19571      */
19572     getCount : function(){
19573         return this.data.length || 0;
19574     },
19575
19576     /**
19577      * Gets the total number of records in the dataset as returned by the server.
19578      * <p>
19579      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19580      * the dataset size</em>
19581      */
19582     getTotalCount : function(){
19583         return this.totalLength || 0;
19584     },
19585
19586     /**
19587      * Returns the sort state of the Store as an object with two properties:
19588      * <pre><code>
19589  field {String} The name of the field by which the Records are sorted
19590  direction {String} The sort order, "ASC" or "DESC"
19591      * </code></pre>
19592      */
19593     getSortState : function(){
19594         return this.sortInfo;
19595     },
19596
19597     // private
19598     applySort : function(){
19599         if(this.sortInfo && !this.remoteSort){
19600             var s = this.sortInfo, f = s.field;
19601             var st = this.fields.get(f).sortType;
19602             var fn = function(r1, r2){
19603                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19604                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19605             };
19606             this.data.sort(s.direction, fn);
19607             if(this.snapshot && this.snapshot != this.data){
19608                 this.snapshot.sort(s.direction, fn);
19609             }
19610         }
19611     },
19612
19613     /**
19614      * Sets the default sort column and order to be used by the next load operation.
19615      * @param {String} fieldName The name of the field to sort by.
19616      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19617      */
19618     setDefaultSort : function(field, dir){
19619         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19620     },
19621
19622     /**
19623      * Sort the Records.
19624      * If remote sorting is used, the sort is performed on the server, and the cache is
19625      * reloaded. If local sorting is used, the cache is sorted internally.
19626      * @param {String} fieldName The name of the field to sort by.
19627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19628      */
19629     sort : function(fieldName, dir){
19630         var f = this.fields.get(fieldName);
19631         if(!dir){
19632             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19633             
19634             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19635                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19636             }else{
19637                 dir = f.sortDir;
19638             }
19639         }
19640         this.sortToggle[f.name] = dir;
19641         this.sortInfo = {field: f.name, direction: dir};
19642         if(!this.remoteSort){
19643             this.applySort();
19644             this.fireEvent("datachanged", this);
19645         }else{
19646             this.load(this.lastOptions);
19647         }
19648     },
19649
19650     /**
19651      * Calls the specified function for each of the Records in the cache.
19652      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19653      * Returning <em>false</em> aborts and exits the iteration.
19654      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19655      */
19656     each : function(fn, scope){
19657         this.data.each(fn, scope);
19658     },
19659
19660     /**
19661      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19662      * (e.g., during paging).
19663      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19664      */
19665     getModifiedRecords : function(){
19666         return this.modified;
19667     },
19668
19669     // private
19670     createFilterFn : function(property, value, anyMatch){
19671         if(!value.exec){ // not a regex
19672             value = String(value);
19673             if(value.length == 0){
19674                 return false;
19675             }
19676             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19677         }
19678         return function(r){
19679             return value.test(r.data[property]);
19680         };
19681     },
19682
19683     /**
19684      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19685      * @param {String} property A field on your records
19686      * @param {Number} start The record index to start at (defaults to 0)
19687      * @param {Number} end The last record index to include (defaults to length - 1)
19688      * @return {Number} The sum
19689      */
19690     sum : function(property, start, end){
19691         var rs = this.data.items, v = 0;
19692         start = start || 0;
19693         end = (end || end === 0) ? end : rs.length-1;
19694
19695         for(var i = start; i <= end; i++){
19696             v += (rs[i].data[property] || 0);
19697         }
19698         return v;
19699     },
19700
19701     /**
19702      * Filter the records by a specified property.
19703      * @param {String} field A field on your records
19704      * @param {String/RegExp} value Either a string that the field
19705      * should start with or a RegExp to test against the field
19706      * @param {Boolean} anyMatch True to match any part not just the beginning
19707      */
19708     filter : function(property, value, anyMatch){
19709         var fn = this.createFilterFn(property, value, anyMatch);
19710         return fn ? this.filterBy(fn) : this.clearFilter();
19711     },
19712
19713     /**
19714      * Filter by a function. The specified function will be called with each
19715      * record in this data source. If the function returns true the record is included,
19716      * otherwise it is filtered.
19717      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19718      * @param {Object} scope (optional) The scope of the function (defaults to this)
19719      */
19720     filterBy : function(fn, scope){
19721         this.snapshot = this.snapshot || this.data;
19722         this.data = this.queryBy(fn, scope||this);
19723         this.fireEvent("datachanged", this);
19724     },
19725
19726     /**
19727      * Query the records by a specified property.
19728      * @param {String} field A field on your records
19729      * @param {String/RegExp} value Either a string that the field
19730      * should start with or a RegExp to test against the field
19731      * @param {Boolean} anyMatch True to match any part not just the beginning
19732      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19733      */
19734     query : function(property, value, anyMatch){
19735         var fn = this.createFilterFn(property, value, anyMatch);
19736         return fn ? this.queryBy(fn) : this.data.clone();
19737     },
19738
19739     /**
19740      * Query by a function. The specified function will be called with each
19741      * record in this data source. If the function returns true the record is included
19742      * in the results.
19743      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19744      * @param {Object} scope (optional) The scope of the function (defaults to this)
19745       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19746      **/
19747     queryBy : function(fn, scope){
19748         var data = this.snapshot || this.data;
19749         return data.filterBy(fn, scope||this);
19750     },
19751
19752     /**
19753      * Collects unique values for a particular dataIndex from this store.
19754      * @param {String} dataIndex The property to collect
19755      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19756      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19757      * @return {Array} An array of the unique values
19758      **/
19759     collect : function(dataIndex, allowNull, bypassFilter){
19760         var d = (bypassFilter === true && this.snapshot) ?
19761                 this.snapshot.items : this.data.items;
19762         var v, sv, r = [], l = {};
19763         for(var i = 0, len = d.length; i < len; i++){
19764             v = d[i].data[dataIndex];
19765             sv = String(v);
19766             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19767                 l[sv] = true;
19768                 r[r.length] = v;
19769             }
19770         }
19771         return r;
19772     },
19773
19774     /**
19775      * Revert to a view of the Record cache with no filtering applied.
19776      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19777      */
19778     clearFilter : function(suppressEvent){
19779         if(this.snapshot && this.snapshot != this.data){
19780             this.data = this.snapshot;
19781             delete this.snapshot;
19782             if(suppressEvent !== true){
19783                 this.fireEvent("datachanged", this);
19784             }
19785         }
19786     },
19787
19788     // private
19789     afterEdit : function(record){
19790         if(this.modified.indexOf(record) == -1){
19791             this.modified.push(record);
19792         }
19793         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19794     },
19795
19796     // private
19797     afterReject : function(record){
19798         this.modified.remove(record);
19799         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19800     },
19801
19802     // private
19803     afterCommit : function(record){
19804         this.modified.remove(record);
19805         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19806     },
19807
19808     /**
19809      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19810      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19811      */
19812     commitChanges : function(){
19813         var m = this.modified.slice(0);
19814         this.modified = [];
19815         for(var i = 0, len = m.length; i < len; i++){
19816             m[i].commit();
19817         }
19818     },
19819
19820     /**
19821      * Cancel outstanding changes on all changed records.
19822      */
19823     rejectChanges : function(){
19824         var m = this.modified.slice(0);
19825         this.modified = [];
19826         for(var i = 0, len = m.length; i < len; i++){
19827             m[i].reject();
19828         }
19829     },
19830
19831     onMetaChange : function(meta, rtype, o){
19832         this.recordType = rtype;
19833         this.fields = rtype.prototype.fields;
19834         delete this.snapshot;
19835         this.sortInfo = meta.sortInfo || this.sortInfo;
19836         this.modified = [];
19837         this.fireEvent('metachange', this, this.reader.meta);
19838     }
19839 });/*
19840  * Based on:
19841  * Ext JS Library 1.1.1
19842  * Copyright(c) 2006-2007, Ext JS, LLC.
19843  *
19844  * Originally Released Under LGPL - original licence link has changed is not relivant.
19845  *
19846  * Fork - LGPL
19847  * <script type="text/javascript">
19848  */
19849
19850 /**
19851  * @class Roo.data.SimpleStore
19852  * @extends Roo.data.Store
19853  * Small helper class to make creating Stores from Array data easier.
19854  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19855  * @cfg {Array} fields An array of field definition objects, or field name strings.
19856  * @cfg {Array} data The multi-dimensional array of data
19857  * @constructor
19858  * @param {Object} config
19859  */
19860 Roo.data.SimpleStore = function(config){
19861     Roo.data.SimpleStore.superclass.constructor.call(this, {
19862         isLocal : true,
19863         reader: new Roo.data.ArrayReader({
19864                 id: config.id
19865             },
19866             Roo.data.Record.create(config.fields)
19867         ),
19868         proxy : new Roo.data.MemoryProxy(config.data)
19869     });
19870     this.load();
19871 };
19872 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882
19883 /**
19884 /**
19885  * @extends Roo.data.Store
19886  * @class Roo.data.JsonStore
19887  * Small helper class to make creating Stores for JSON data easier. <br/>
19888 <pre><code>
19889 var store = new Roo.data.JsonStore({
19890     url: 'get-images.php',
19891     root: 'images',
19892     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19893 });
19894 </code></pre>
19895  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19896  * JsonReader and HttpProxy (unless inline data is provided).</b>
19897  * @cfg {Array} fields An array of field definition objects, or field name strings.
19898  * @constructor
19899  * @param {Object} config
19900  */
19901 Roo.data.JsonStore = function(c){
19902     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19903         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19904         reader: new Roo.data.JsonReader(c, c.fields)
19905     }));
19906 };
19907 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19908  * Based on:
19909  * Ext JS Library 1.1.1
19910  * Copyright(c) 2006-2007, Ext JS, LLC.
19911  *
19912  * Originally Released Under LGPL - original licence link has changed is not relivant.
19913  *
19914  * Fork - LGPL
19915  * <script type="text/javascript">
19916  */
19917
19918  
19919 Roo.data.Field = function(config){
19920     if(typeof config == "string"){
19921         config = {name: config};
19922     }
19923     Roo.apply(this, config);
19924     
19925     if(!this.type){
19926         this.type = "auto";
19927     }
19928     
19929     var st = Roo.data.SortTypes;
19930     // named sortTypes are supported, here we look them up
19931     if(typeof this.sortType == "string"){
19932         this.sortType = st[this.sortType];
19933     }
19934     
19935     // set default sortType for strings and dates
19936     if(!this.sortType){
19937         switch(this.type){
19938             case "string":
19939                 this.sortType = st.asUCString;
19940                 break;
19941             case "date":
19942                 this.sortType = st.asDate;
19943                 break;
19944             default:
19945                 this.sortType = st.none;
19946         }
19947     }
19948
19949     // define once
19950     var stripRe = /[\$,%]/g;
19951
19952     // prebuilt conversion function for this field, instead of
19953     // switching every time we're reading a value
19954     if(!this.convert){
19955         var cv, dateFormat = this.dateFormat;
19956         switch(this.type){
19957             case "":
19958             case "auto":
19959             case undefined:
19960                 cv = function(v){ return v; };
19961                 break;
19962             case "string":
19963                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19964                 break;
19965             case "int":
19966                 cv = function(v){
19967                     return v !== undefined && v !== null && v !== '' ?
19968                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19969                     };
19970                 break;
19971             case "float":
19972                 cv = function(v){
19973                     return v !== undefined && v !== null && v !== '' ?
19974                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19975                     };
19976                 break;
19977             case "bool":
19978             case "boolean":
19979                 cv = function(v){ return v === true || v === "true" || v == 1; };
19980                 break;
19981             case "date":
19982                 cv = function(v){
19983                     if(!v){
19984                         return '';
19985                     }
19986                     if(v instanceof Date){
19987                         return v;
19988                     }
19989                     if(dateFormat){
19990                         if(dateFormat == "timestamp"){
19991                             return new Date(v*1000);
19992                         }
19993                         return Date.parseDate(v, dateFormat);
19994                     }
19995                     var parsed = Date.parse(v);
19996                     return parsed ? new Date(parsed) : null;
19997                 };
19998              break;
19999             
20000         }
20001         this.convert = cv;
20002     }
20003 };
20004
20005 Roo.data.Field.prototype = {
20006     dateFormat: null,
20007     defaultValue: "",
20008     mapping: null,
20009     sortType : null,
20010     sortDir : "ASC"
20011 };/*
20012  * Based on:
20013  * Ext JS Library 1.1.1
20014  * Copyright(c) 2006-2007, Ext JS, LLC.
20015  *
20016  * Originally Released Under LGPL - original licence link has changed is not relivant.
20017  *
20018  * Fork - LGPL
20019  * <script type="text/javascript">
20020  */
20021  
20022 // Base class for reading structured data from a data source.  This class is intended to be
20023 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20024
20025 /**
20026  * @class Roo.data.DataReader
20027  * Base class for reading structured data from a data source.  This class is intended to be
20028  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20029  */
20030
20031 Roo.data.DataReader = function(meta, recordType){
20032     
20033     this.meta = meta;
20034     
20035     this.recordType = recordType instanceof Array ? 
20036         Roo.data.Record.create(recordType) : recordType;
20037 };
20038
20039 Roo.data.DataReader.prototype = {
20040      /**
20041      * Create an empty record
20042      * @param {Object} data (optional) - overlay some values
20043      * @return {Roo.data.Record} record created.
20044      */
20045     newRow :  function(d) {
20046         var da =  {};
20047         this.recordType.prototype.fields.each(function(c) {
20048             switch( c.type) {
20049                 case 'int' : da[c.name] = 0; break;
20050                 case 'date' : da[c.name] = new Date(); break;
20051                 case 'float' : da[c.name] = 0.0; break;
20052                 case 'boolean' : da[c.name] = false; break;
20053                 default : da[c.name] = ""; break;
20054             }
20055             
20056         });
20057         return new this.recordType(Roo.apply(da, d));
20058     }
20059     
20060 };/*
20061  * Based on:
20062  * Ext JS Library 1.1.1
20063  * Copyright(c) 2006-2007, Ext JS, LLC.
20064  *
20065  * Originally Released Under LGPL - original licence link has changed is not relivant.
20066  *
20067  * Fork - LGPL
20068  * <script type="text/javascript">
20069  */
20070
20071 /**
20072  * @class Roo.data.DataProxy
20073  * @extends Roo.data.Observable
20074  * This class is an abstract base class for implementations which provide retrieval of
20075  * unformatted data objects.<br>
20076  * <p>
20077  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20078  * (of the appropriate type which knows how to parse the data object) to provide a block of
20079  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20080  * <p>
20081  * Custom implementations must implement the load method as described in
20082  * {@link Roo.data.HttpProxy#load}.
20083  */
20084 Roo.data.DataProxy = function(){
20085     this.addEvents({
20086         /**
20087          * @event beforeload
20088          * Fires before a network request is made to retrieve a data object.
20089          * @param {Object} This DataProxy object.
20090          * @param {Object} params The params parameter to the load function.
20091          */
20092         beforeload : true,
20093         /**
20094          * @event load
20095          * Fires before the load method's callback is called.
20096          * @param {Object} This DataProxy object.
20097          * @param {Object} o The data object.
20098          * @param {Object} arg The callback argument object passed to the load function.
20099          */
20100         load : true,
20101         /**
20102          * @event loadexception
20103          * Fires if an Exception occurs during data retrieval.
20104          * @param {Object} This DataProxy object.
20105          * @param {Object} o The data object.
20106          * @param {Object} arg The callback argument object passed to the load function.
20107          * @param {Object} e The Exception.
20108          */
20109         loadexception : true
20110     });
20111     Roo.data.DataProxy.superclass.constructor.call(this);
20112 };
20113
20114 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20115
20116     /**
20117      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20118      */
20119 /*
20120  * Based on:
20121  * Ext JS Library 1.1.1
20122  * Copyright(c) 2006-2007, Ext JS, LLC.
20123  *
20124  * Originally Released Under LGPL - original licence link has changed is not relivant.
20125  *
20126  * Fork - LGPL
20127  * <script type="text/javascript">
20128  */
20129 /**
20130  * @class Roo.data.MemoryProxy
20131  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20132  * to the Reader when its load method is called.
20133  * @constructor
20134  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20135  */
20136 Roo.data.MemoryProxy = function(data){
20137     if (data.data) {
20138         data = data.data;
20139     }
20140     Roo.data.MemoryProxy.superclass.constructor.call(this);
20141     this.data = data;
20142 };
20143
20144 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20145     /**
20146      * Load data from the requested source (in this case an in-memory
20147      * data object passed to the constructor), read the data object into
20148      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20149      * process that block using the passed callback.
20150      * @param {Object} params This parameter is not used by the MemoryProxy class.
20151      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20152      * object into a block of Roo.data.Records.
20153      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20154      * The function must be passed <ul>
20155      * <li>The Record block object</li>
20156      * <li>The "arg" argument from the load function</li>
20157      * <li>A boolean success indicator</li>
20158      * </ul>
20159      * @param {Object} scope The scope in which to call the callback
20160      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20161      */
20162     load : function(params, reader, callback, scope, arg){
20163         params = params || {};
20164         var result;
20165         try {
20166             result = reader.readRecords(this.data);
20167         }catch(e){
20168             this.fireEvent("loadexception", this, arg, null, e);
20169             callback.call(scope, null, arg, false);
20170             return;
20171         }
20172         callback.call(scope, result, arg, true);
20173     },
20174     
20175     // private
20176     update : function(params, records){
20177         
20178     }
20179 });/*
20180  * Based on:
20181  * Ext JS Library 1.1.1
20182  * Copyright(c) 2006-2007, Ext JS, LLC.
20183  *
20184  * Originally Released Under LGPL - original licence link has changed is not relivant.
20185  *
20186  * Fork - LGPL
20187  * <script type="text/javascript">
20188  */
20189 /**
20190  * @class Roo.data.HttpProxy
20191  * @extends Roo.data.DataProxy
20192  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20193  * configured to reference a certain URL.<br><br>
20194  * <p>
20195  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20196  * from which the running page was served.<br><br>
20197  * <p>
20198  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20199  * <p>
20200  * Be aware that to enable the browser to parse an XML document, the server must set
20201  * the Content-Type header in the HTTP response to "text/xml".
20202  * @constructor
20203  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20204  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20205  * will be used to make the request.
20206  */
20207 Roo.data.HttpProxy = function(conn){
20208     Roo.data.HttpProxy.superclass.constructor.call(this);
20209     // is conn a conn config or a real conn?
20210     this.conn = conn;
20211     this.useAjax = !conn || !conn.events;
20212   
20213 };
20214
20215 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20216     // thse are take from connection...
20217     
20218     /**
20219      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20220      */
20221     /**
20222      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20223      * extra parameters to each request made by this object. (defaults to undefined)
20224      */
20225     /**
20226      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20227      *  to each request made by this object. (defaults to undefined)
20228      */
20229     /**
20230      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20231      */
20232     /**
20233      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20234      */
20235      /**
20236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20237      * @type Boolean
20238      */
20239   
20240
20241     /**
20242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20243      * @type Boolean
20244      */
20245     /**
20246      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20247      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20248      * a finer-grained basis than the DataProxy events.
20249      */
20250     getConnection : function(){
20251         return this.useAjax ? Roo.Ajax : this.conn;
20252     },
20253
20254     /**
20255      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20256      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20257      * process that block using the passed callback.
20258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20259      * for the request to the remote server.
20260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20261      * object into a block of Roo.data.Records.
20262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20263      * The function must be passed <ul>
20264      * <li>The Record block object</li>
20265      * <li>The "arg" argument from the load function</li>
20266      * <li>A boolean success indicator</li>
20267      * </ul>
20268      * @param {Object} scope The scope in which to call the callback
20269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20270      */
20271     load : function(params, reader, callback, scope, arg){
20272         if(this.fireEvent("beforeload", this, params) !== false){
20273             var  o = {
20274                 params : params || {},
20275                 request: {
20276                     callback : callback,
20277                     scope : scope,
20278                     arg : arg
20279                 },
20280                 reader: reader,
20281                 callback : this.loadResponse,
20282                 scope: this
20283             };
20284             if(this.useAjax){
20285                 Roo.applyIf(o, this.conn);
20286                 if(this.activeRequest){
20287                     Roo.Ajax.abort(this.activeRequest);
20288                 }
20289                 this.activeRequest = Roo.Ajax.request(o);
20290             }else{
20291                 this.conn.request(o);
20292             }
20293         }else{
20294             callback.call(scope||this, null, arg, false);
20295         }
20296     },
20297
20298     // private
20299     loadResponse : function(o, success, response){
20300         delete this.activeRequest;
20301         if(!success){
20302             this.fireEvent("loadexception", this, o, response);
20303             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20304             return;
20305         }
20306         var result;
20307         try {
20308             result = o.reader.read(response);
20309         }catch(e){
20310             this.fireEvent("loadexception", this, o, response, e);
20311             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20312             return;
20313         }
20314         
20315         this.fireEvent("load", this, o, o.request.arg);
20316         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20317     },
20318
20319     // private
20320     update : function(dataSet){
20321
20322     },
20323
20324     // private
20325     updateResponse : function(dataSet){
20326
20327     }
20328 });/*
20329  * Based on:
20330  * Ext JS Library 1.1.1
20331  * Copyright(c) 2006-2007, Ext JS, LLC.
20332  *
20333  * Originally Released Under LGPL - original licence link has changed is not relivant.
20334  *
20335  * Fork - LGPL
20336  * <script type="text/javascript">
20337  */
20338
20339 /**
20340  * @class Roo.data.ScriptTagProxy
20341  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20342  * other than the originating domain of the running page.<br><br>
20343  * <p>
20344  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20345  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20346  * <p>
20347  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20348  * source code that is used as the source inside a &lt;script> tag.<br><br>
20349  * <p>
20350  * In order for the browser to process the returned data, the server must wrap the data object
20351  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20352  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20353  * depending on whether the callback name was passed:
20354  * <p>
20355  * <pre><code>
20356 boolean scriptTag = false;
20357 String cb = request.getParameter("callback");
20358 if (cb != null) {
20359     scriptTag = true;
20360     response.setContentType("text/javascript");
20361 } else {
20362     response.setContentType("application/x-json");
20363 }
20364 Writer out = response.getWriter();
20365 if (scriptTag) {
20366     out.write(cb + "(");
20367 }
20368 out.print(dataBlock.toJsonString());
20369 if (scriptTag) {
20370     out.write(");");
20371 }
20372 </pre></code>
20373  *
20374  * @constructor
20375  * @param {Object} config A configuration object.
20376  */
20377 Roo.data.ScriptTagProxy = function(config){
20378     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20379     Roo.apply(this, config);
20380     this.head = document.getElementsByTagName("head")[0];
20381 };
20382
20383 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20384
20385 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20386     /**
20387      * @cfg {String} url The URL from which to request the data object.
20388      */
20389     /**
20390      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20391      */
20392     timeout : 30000,
20393     /**
20394      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20395      * the server the name of the callback function set up by the load call to process the returned data object.
20396      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20397      * javascript output which calls this named function passing the data object as its only parameter.
20398      */
20399     callbackParam : "callback",
20400     /**
20401      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20402      * name to the request.
20403      */
20404     nocache : true,
20405
20406     /**
20407      * Load data from the configured URL, read the data object into
20408      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20409      * process that block using the passed callback.
20410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20411      * for the request to the remote server.
20412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20413      * object into a block of Roo.data.Records.
20414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20415      * The function must be passed <ul>
20416      * <li>The Record block object</li>
20417      * <li>The "arg" argument from the load function</li>
20418      * <li>A boolean success indicator</li>
20419      * </ul>
20420      * @param {Object} scope The scope in which to call the callback
20421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20422      */
20423     load : function(params, reader, callback, scope, arg){
20424         if(this.fireEvent("beforeload", this, params) !== false){
20425
20426             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20427
20428             var url = this.url;
20429             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20430             if(this.nocache){
20431                 url += "&_dc=" + (new Date().getTime());
20432             }
20433             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20434             var trans = {
20435                 id : transId,
20436                 cb : "stcCallback"+transId,
20437                 scriptId : "stcScript"+transId,
20438                 params : params,
20439                 arg : arg,
20440                 url : url,
20441                 callback : callback,
20442                 scope : scope,
20443                 reader : reader
20444             };
20445             var conn = this;
20446
20447             window[trans.cb] = function(o){
20448                 conn.handleResponse(o, trans);
20449             };
20450
20451             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20452
20453             if(this.autoAbort !== false){
20454                 this.abort();
20455             }
20456
20457             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20458
20459             var script = document.createElement("script");
20460             script.setAttribute("src", url);
20461             script.setAttribute("type", "text/javascript");
20462             script.setAttribute("id", trans.scriptId);
20463             this.head.appendChild(script);
20464
20465             this.trans = trans;
20466         }else{
20467             callback.call(scope||this, null, arg, false);
20468         }
20469     },
20470
20471     // private
20472     isLoading : function(){
20473         return this.trans ? true : false;
20474     },
20475
20476     /**
20477      * Abort the current server request.
20478      */
20479     abort : function(){
20480         if(this.isLoading()){
20481             this.destroyTrans(this.trans);
20482         }
20483     },
20484
20485     // private
20486     destroyTrans : function(trans, isLoaded){
20487         this.head.removeChild(document.getElementById(trans.scriptId));
20488         clearTimeout(trans.timeoutId);
20489         if(isLoaded){
20490             window[trans.cb] = undefined;
20491             try{
20492                 delete window[trans.cb];
20493             }catch(e){}
20494         }else{
20495             // if hasn't been loaded, wait for load to remove it to prevent script error
20496             window[trans.cb] = function(){
20497                 window[trans.cb] = undefined;
20498                 try{
20499                     delete window[trans.cb];
20500                 }catch(e){}
20501             };
20502         }
20503     },
20504
20505     // private
20506     handleResponse : function(o, trans){
20507         this.trans = false;
20508         this.destroyTrans(trans, true);
20509         var result;
20510         try {
20511             result = trans.reader.readRecords(o);
20512         }catch(e){
20513             this.fireEvent("loadexception", this, o, trans.arg, e);
20514             trans.callback.call(trans.scope||window, null, trans.arg, false);
20515             return;
20516         }
20517         this.fireEvent("load", this, o, trans.arg);
20518         trans.callback.call(trans.scope||window, result, trans.arg, true);
20519     },
20520
20521     // private
20522     handleFailure : function(trans){
20523         this.trans = false;
20524         this.destroyTrans(trans, false);
20525         this.fireEvent("loadexception", this, null, trans.arg);
20526         trans.callback.call(trans.scope||window, null, trans.arg, false);
20527     }
20528 });/*
20529  * Based on:
20530  * Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  *
20533  * Originally Released Under LGPL - original licence link has changed is not relivant.
20534  *
20535  * Fork - LGPL
20536  * <script type="text/javascript">
20537  */
20538
20539 /**
20540  * @class Roo.data.JsonReader
20541  * @extends Roo.data.DataReader
20542  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20543  * based on mappings in a provided Roo.data.Record constructor.
20544  * 
20545  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20546  * in the reply previously. 
20547  * 
20548  * <p>
20549  * Example code:
20550  * <pre><code>
20551 var RecordDef = Roo.data.Record.create([
20552     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20553     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20554 ]);
20555 var myReader = new Roo.data.JsonReader({
20556     totalProperty: "results",    // The property which contains the total dataset size (optional)
20557     root: "rows",                // The property which contains an Array of row objects
20558     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20559 }, RecordDef);
20560 </code></pre>
20561  * <p>
20562  * This would consume a JSON file like this:
20563  * <pre><code>
20564 { 'results': 2, 'rows': [
20565     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20566     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20567 }
20568 </code></pre>
20569  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20570  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20571  * paged from the remote server.
20572  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20573  * @cfg {String} root name of the property which contains the Array of row objects.
20574  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20575  * @constructor
20576  * Create a new JsonReader
20577  * @param {Object} meta Metadata configuration options
20578  * @param {Object} recordType Either an Array of field definition objects,
20579  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20580  */
20581 Roo.data.JsonReader = function(meta, recordType){
20582     
20583     meta = meta || {};
20584     // set some defaults:
20585     Roo.applyIf(meta, {
20586         totalProperty: 'total',
20587         successProperty : 'success',
20588         root : 'data',
20589         id : 'id'
20590     });
20591     
20592     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20593 };
20594 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20595     
20596     /**
20597      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20598      * Used by Store query builder to append _requestMeta to params.
20599      * 
20600      */
20601     metaFromRemote : false,
20602     /**
20603      * This method is only used by a DataProxy which has retrieved data from a remote server.
20604      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20605      * @return {Object} data A data block which is used by an Roo.data.Store object as
20606      * a cache of Roo.data.Records.
20607      */
20608     read : function(response){
20609         var json = response.responseText;
20610        
20611         var o = /* eval:var:o */ eval("("+json+")");
20612         if(!o) {
20613             throw {message: "JsonReader.read: Json object not found"};
20614         }
20615         
20616         if(o.metaData){
20617             
20618             delete this.ef;
20619             this.metaFromRemote = true;
20620             this.meta = o.metaData;
20621             this.recordType = Roo.data.Record.create(o.metaData.fields);
20622             this.onMetaChange(this.meta, this.recordType, o);
20623         }
20624         return this.readRecords(o);
20625     },
20626
20627     // private function a store will implement
20628     onMetaChange : function(meta, recordType, o){
20629
20630     },
20631
20632     /**
20633          * @ignore
20634          */
20635     simpleAccess: function(obj, subsc) {
20636         return obj[subsc];
20637     },
20638
20639         /**
20640          * @ignore
20641          */
20642     getJsonAccessor: function(){
20643         var re = /[\[\.]/;
20644         return function(expr) {
20645             try {
20646                 return(re.test(expr))
20647                     ? new Function("obj", "return obj." + expr)
20648                     : function(obj){
20649                         return obj[expr];
20650                     };
20651             } catch(e){}
20652             return Roo.emptyFn;
20653         };
20654     }(),
20655
20656     /**
20657      * Create a data block containing Roo.data.Records from an XML document.
20658      * @param {Object} o An object which contains an Array of row objects in the property specified
20659      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20660      * which contains the total size of the dataset.
20661      * @return {Object} data A data block which is used by an Roo.data.Store object as
20662      * a cache of Roo.data.Records.
20663      */
20664     readRecords : function(o){
20665         /**
20666          * After any data loads, the raw JSON data is available for further custom processing.
20667          * @type Object
20668          */
20669         this.jsonData = o;
20670         var s = this.meta, Record = this.recordType,
20671             f = Record.prototype.fields, fi = f.items, fl = f.length;
20672
20673 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20674         if (!this.ef) {
20675             if(s.totalProperty) {
20676                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20677                 }
20678                 if(s.successProperty) {
20679                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20680                 }
20681                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20682                 if (s.id) {
20683                         var g = this.getJsonAccessor(s.id);
20684                         this.getId = function(rec) {
20685                                 var r = g(rec);
20686                                 return (r === undefined || r === "") ? null : r;
20687                         };
20688                 } else {
20689                         this.getId = function(){return null;};
20690                 }
20691             this.ef = [];
20692             for(var jj = 0; jj < fl; jj++){
20693                 f = fi[jj];
20694                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20695                 this.ef[jj] = this.getJsonAccessor(map);
20696             }
20697         }
20698
20699         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20700         if(s.totalProperty){
20701             var vt = parseInt(this.getTotal(o), 10);
20702             if(!isNaN(vt)){
20703                 totalRecords = vt;
20704             }
20705         }
20706         if(s.successProperty){
20707             var vs = this.getSuccess(o);
20708             if(vs === false || vs === 'false'){
20709                 success = false;
20710             }
20711         }
20712         var records = [];
20713             for(var i = 0; i < c; i++){
20714                     var n = root[i];
20715                 var values = {};
20716                 var id = this.getId(n);
20717                 for(var j = 0; j < fl; j++){
20718                     f = fi[j];
20719                 var v = this.ef[j](n);
20720                 if (!f.convert) {
20721                     Roo.log('missing convert for ' + f.name);
20722                     Roo.log(f);
20723                     continue;
20724                 }
20725                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20726                 }
20727                 var record = new Record(values, id);
20728                 record.json = n;
20729                 records[i] = record;
20730             }
20731             return {
20732                 success : success,
20733                 records : records,
20734                 totalRecords : totalRecords
20735             };
20736     }
20737 });/*
20738  * Based on:
20739  * Ext JS Library 1.1.1
20740  * Copyright(c) 2006-2007, Ext JS, LLC.
20741  *
20742  * Originally Released Under LGPL - original licence link has changed is not relivant.
20743  *
20744  * Fork - LGPL
20745  * <script type="text/javascript">
20746  */
20747
20748 /**
20749  * @class Roo.data.XmlReader
20750  * @extends Roo.data.DataReader
20751  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20752  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20753  * <p>
20754  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20755  * header in the HTTP response must be set to "text/xml".</em>
20756  * <p>
20757  * Example code:
20758  * <pre><code>
20759 var RecordDef = Roo.data.Record.create([
20760    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20761    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20762 ]);
20763 var myReader = new Roo.data.XmlReader({
20764    totalRecords: "results", // The element which contains the total dataset size (optional)
20765    record: "row",           // The repeated element which contains row information
20766    id: "id"                 // The element within the row that provides an ID for the record (optional)
20767 }, RecordDef);
20768 </code></pre>
20769  * <p>
20770  * This would consume an XML file like this:
20771  * <pre><code>
20772 &lt;?xml?>
20773 &lt;dataset>
20774  &lt;results>2&lt;/results>
20775  &lt;row>
20776    &lt;id>1&lt;/id>
20777    &lt;name>Bill&lt;/name>
20778    &lt;occupation>Gardener&lt;/occupation>
20779  &lt;/row>
20780  &lt;row>
20781    &lt;id>2&lt;/id>
20782    &lt;name>Ben&lt;/name>
20783    &lt;occupation>Horticulturalist&lt;/occupation>
20784  &lt;/row>
20785 &lt;/dataset>
20786 </code></pre>
20787  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20788  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20789  * paged from the remote server.
20790  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20791  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20792  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20793  * a record identifier value.
20794  * @constructor
20795  * Create a new XmlReader
20796  * @param {Object} meta Metadata configuration options
20797  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20798  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20799  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20800  */
20801 Roo.data.XmlReader = function(meta, recordType){
20802     meta = meta || {};
20803     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20804 };
20805 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20806     /**
20807      * This method is only used by a DataProxy which has retrieved data from a remote server.
20808          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20809          * to contain a method called 'responseXML' that returns an XML document object.
20810      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20811      * a cache of Roo.data.Records.
20812      */
20813     read : function(response){
20814         var doc = response.responseXML;
20815         if(!doc) {
20816             throw {message: "XmlReader.read: XML Document not available"};
20817         }
20818         return this.readRecords(doc);
20819     },
20820
20821     /**
20822      * Create a data block containing Roo.data.Records from an XML document.
20823          * @param {Object} doc A parsed XML document.
20824      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20825      * a cache of Roo.data.Records.
20826      */
20827     readRecords : function(doc){
20828         /**
20829          * After any data loads/reads, the raw XML Document is available for further custom processing.
20830          * @type XMLDocument
20831          */
20832         this.xmlData = doc;
20833         var root = doc.documentElement || doc;
20834         var q = Roo.DomQuery;
20835         var recordType = this.recordType, fields = recordType.prototype.fields;
20836         var sid = this.meta.id;
20837         var totalRecords = 0, success = true;
20838         if(this.meta.totalRecords){
20839             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20840         }
20841         
20842         if(this.meta.success){
20843             var sv = q.selectValue(this.meta.success, root, true);
20844             success = sv !== false && sv !== 'false';
20845         }
20846         var records = [];
20847         var ns = q.select(this.meta.record, root);
20848         for(var i = 0, len = ns.length; i < len; i++) {
20849                 var n = ns[i];
20850                 var values = {};
20851                 var id = sid ? q.selectValue(sid, n) : undefined;
20852                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20853                     var f = fields.items[j];
20854                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20855                     v = f.convert(v);
20856                     values[f.name] = v;
20857                 }
20858                 var record = new recordType(values, id);
20859                 record.node = n;
20860                 records[records.length] = record;
20861             }
20862
20863             return {
20864                 success : success,
20865                 records : records,
20866                 totalRecords : totalRecords || records.length
20867             };
20868     }
20869 });/*
20870  * Based on:
20871  * Ext JS Library 1.1.1
20872  * Copyright(c) 2006-2007, Ext JS, LLC.
20873  *
20874  * Originally Released Under LGPL - original licence link has changed is not relivant.
20875  *
20876  * Fork - LGPL
20877  * <script type="text/javascript">
20878  */
20879
20880 /**
20881  * @class Roo.data.ArrayReader
20882  * @extends Roo.data.DataReader
20883  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20884  * Each element of that Array represents a row of data fields. The
20885  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20886  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20887  * <p>
20888  * Example code:.
20889  * <pre><code>
20890 var RecordDef = Roo.data.Record.create([
20891     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20892     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20893 ]);
20894 var myReader = new Roo.data.ArrayReader({
20895     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20896 }, RecordDef);
20897 </code></pre>
20898  * <p>
20899  * This would consume an Array like this:
20900  * <pre><code>
20901 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20902   </code></pre>
20903  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20904  * @constructor
20905  * Create a new JsonReader
20906  * @param {Object} meta Metadata configuration options.
20907  * @param {Object} recordType Either an Array of field definition objects
20908  * as specified to {@link Roo.data.Record#create},
20909  * or an {@link Roo.data.Record} object
20910  * created using {@link Roo.data.Record#create}.
20911  */
20912 Roo.data.ArrayReader = function(meta, recordType){
20913     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20914 };
20915
20916 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20917     /**
20918      * Create a data block containing Roo.data.Records from an XML document.
20919      * @param {Object} o An Array of row objects which represents the dataset.
20920      * @return {Object} data A data block which is used by an Roo.data.Store object as
20921      * a cache of Roo.data.Records.
20922      */
20923     readRecords : function(o){
20924         var sid = this.meta ? this.meta.id : null;
20925         var recordType = this.recordType, fields = recordType.prototype.fields;
20926         var records = [];
20927         var root = o;
20928             for(var i = 0; i < root.length; i++){
20929                     var n = root[i];
20930                 var values = {};
20931                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20932                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20933                 var f = fields.items[j];
20934                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20935                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20936                 v = f.convert(v);
20937                 values[f.name] = v;
20938             }
20939                 var record = new recordType(values, id);
20940                 record.json = n;
20941                 records[records.length] = record;
20942             }
20943             return {
20944                 records : records,
20945                 totalRecords : records.length
20946             };
20947     }
20948 });/*
20949  * Based on:
20950  * Ext JS Library 1.1.1
20951  * Copyright(c) 2006-2007, Ext JS, LLC.
20952  *
20953  * Originally Released Under LGPL - original licence link has changed is not relivant.
20954  *
20955  * Fork - LGPL
20956  * <script type="text/javascript">
20957  */
20958
20959
20960 /**
20961  * @class Roo.data.Tree
20962  * @extends Roo.util.Observable
20963  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20964  * in the tree have most standard DOM functionality.
20965  * @constructor
20966  * @param {Node} root (optional) The root node
20967  */
20968 Roo.data.Tree = function(root){
20969    this.nodeHash = {};
20970    /**
20971     * The root node for this tree
20972     * @type Node
20973     */
20974    this.root = null;
20975    if(root){
20976        this.setRootNode(root);
20977    }
20978    this.addEvents({
20979        /**
20980         * @event append
20981         * Fires when a new child node is appended to a node in this tree.
20982         * @param {Tree} tree The owner tree
20983         * @param {Node} parent The parent node
20984         * @param {Node} node The newly appended node
20985         * @param {Number} index The index of the newly appended node
20986         */
20987        "append" : true,
20988        /**
20989         * @event remove
20990         * Fires when a child node is removed from a node in this tree.
20991         * @param {Tree} tree The owner tree
20992         * @param {Node} parent The parent node
20993         * @param {Node} node The child node removed
20994         */
20995        "remove" : true,
20996        /**
20997         * @event move
20998         * Fires when a node is moved to a new location in the tree
20999         * @param {Tree} tree The owner tree
21000         * @param {Node} node The node moved
21001         * @param {Node} oldParent The old parent of this node
21002         * @param {Node} newParent The new parent of this node
21003         * @param {Number} index The index it was moved to
21004         */
21005        "move" : true,
21006        /**
21007         * @event insert
21008         * Fires when a new child node is inserted in a node in this tree.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The child node inserted
21012         * @param {Node} refNode The child node the node was inserted before
21013         */
21014        "insert" : true,
21015        /**
21016         * @event beforeappend
21017         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node to be appended
21021         */
21022        "beforeappend" : true,
21023        /**
21024         * @event beforeremove
21025         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21026         * @param {Tree} tree The owner tree
21027         * @param {Node} parent The parent node
21028         * @param {Node} node The child node to be removed
21029         */
21030        "beforeremove" : true,
21031        /**
21032         * @event beforemove
21033         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21034         * @param {Tree} tree The owner tree
21035         * @param {Node} node The node being moved
21036         * @param {Node} oldParent The parent of the node
21037         * @param {Node} newParent The new parent the node is moving to
21038         * @param {Number} index The index it is being moved to
21039         */
21040        "beforemove" : true,
21041        /**
21042         * @event beforeinsert
21043         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21044         * @param {Tree} tree The owner tree
21045         * @param {Node} parent The parent node
21046         * @param {Node} node The child node to be inserted
21047         * @param {Node} refNode The child node the node is being inserted before
21048         */
21049        "beforeinsert" : true
21050    });
21051
21052     Roo.data.Tree.superclass.constructor.call(this);
21053 };
21054
21055 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21056     pathSeparator: "/",
21057
21058     proxyNodeEvent : function(){
21059         return this.fireEvent.apply(this, arguments);
21060     },
21061
21062     /**
21063      * Returns the root node for this tree.
21064      * @return {Node}
21065      */
21066     getRootNode : function(){
21067         return this.root;
21068     },
21069
21070     /**
21071      * Sets the root node for this tree.
21072      * @param {Node} node
21073      * @return {Node}
21074      */
21075     setRootNode : function(node){
21076         this.root = node;
21077         node.ownerTree = this;
21078         node.isRoot = true;
21079         this.registerNode(node);
21080         return node;
21081     },
21082
21083     /**
21084      * Gets a node in this tree by its id.
21085      * @param {String} id
21086      * @return {Node}
21087      */
21088     getNodeById : function(id){
21089         return this.nodeHash[id];
21090     },
21091
21092     registerNode : function(node){
21093         this.nodeHash[node.id] = node;
21094     },
21095
21096     unregisterNode : function(node){
21097         delete this.nodeHash[node.id];
21098     },
21099
21100     toString : function(){
21101         return "[Tree"+(this.id?" "+this.id:"")+"]";
21102     }
21103 });
21104
21105 /**
21106  * @class Roo.data.Node
21107  * @extends Roo.util.Observable
21108  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21109  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21110  * @constructor
21111  * @param {Object} attributes The attributes/config for the node
21112  */
21113 Roo.data.Node = function(attributes){
21114     /**
21115      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21116      * @type {Object}
21117      */
21118     this.attributes = attributes || {};
21119     this.leaf = this.attributes.leaf;
21120     /**
21121      * The node id. @type String
21122      */
21123     this.id = this.attributes.id;
21124     if(!this.id){
21125         this.id = Roo.id(null, "ynode-");
21126         this.attributes.id = this.id;
21127     }
21128     /**
21129      * All child nodes of this node. @type Array
21130      */
21131     this.childNodes = [];
21132     if(!this.childNodes.indexOf){ // indexOf is a must
21133         this.childNodes.indexOf = function(o){
21134             for(var i = 0, len = this.length; i < len; i++){
21135                 if(this[i] == o) {
21136                     return i;
21137                 }
21138             }
21139             return -1;
21140         };
21141     }
21142     /**
21143      * The parent node for this node. @type Node
21144      */
21145     this.parentNode = null;
21146     /**
21147      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21148      */
21149     this.firstChild = null;
21150     /**
21151      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21152      */
21153     this.lastChild = null;
21154     /**
21155      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21156      */
21157     this.previousSibling = null;
21158     /**
21159      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21160      */
21161     this.nextSibling = null;
21162
21163     this.addEvents({
21164        /**
21165         * @event append
21166         * Fires when a new child node is appended
21167         * @param {Tree} tree The owner tree
21168         * @param {Node} this This node
21169         * @param {Node} node The newly appended node
21170         * @param {Number} index The index of the newly appended node
21171         */
21172        "append" : true,
21173        /**
21174         * @event remove
21175         * Fires when a child node is removed
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The removed node
21179         */
21180        "remove" : true,
21181        /**
21182         * @event move
21183         * Fires when this node is moved to a new location in the tree
21184         * @param {Tree} tree The owner tree
21185         * @param {Node} this This node
21186         * @param {Node} oldParent The old parent of this node
21187         * @param {Node} newParent The new parent of this node
21188         * @param {Number} index The index it was moved to
21189         */
21190        "move" : true,
21191        /**
21192         * @event insert
21193         * Fires when a new child node is inserted.
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The child node inserted
21197         * @param {Node} refNode The child node the node was inserted before
21198         */
21199        "insert" : true,
21200        /**
21201         * @event beforeappend
21202         * Fires before a new child is appended, return false to cancel the append.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node to be appended
21206         */
21207        "beforeappend" : true,
21208        /**
21209         * @event beforeremove
21210         * Fires before a child is removed, return false to cancel the remove.
21211         * @param {Tree} tree The owner tree
21212         * @param {Node} this This node
21213         * @param {Node} node The child node to be removed
21214         */
21215        "beforeremove" : true,
21216        /**
21217         * @event beforemove
21218         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21219         * @param {Tree} tree The owner tree
21220         * @param {Node} this This node
21221         * @param {Node} oldParent The parent of this node
21222         * @param {Node} newParent The new parent this node is moving to
21223         * @param {Number} index The index it is being moved to
21224         */
21225        "beforemove" : true,
21226        /**
21227         * @event beforeinsert
21228         * Fires before a new child is inserted, return false to cancel the insert.
21229         * @param {Tree} tree The owner tree
21230         * @param {Node} this This node
21231         * @param {Node} node The child node to be inserted
21232         * @param {Node} refNode The child node the node is being inserted before
21233         */
21234        "beforeinsert" : true
21235    });
21236     this.listeners = this.attributes.listeners;
21237     Roo.data.Node.superclass.constructor.call(this);
21238 };
21239
21240 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21241     fireEvent : function(evtName){
21242         // first do standard event for this node
21243         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21244             return false;
21245         }
21246         // then bubble it up to the tree if the event wasn't cancelled
21247         var ot = this.getOwnerTree();
21248         if(ot){
21249             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21250                 return false;
21251             }
21252         }
21253         return true;
21254     },
21255
21256     /**
21257      * Returns true if this node is a leaf
21258      * @return {Boolean}
21259      */
21260     isLeaf : function(){
21261         return this.leaf === true;
21262     },
21263
21264     // private
21265     setFirstChild : function(node){
21266         this.firstChild = node;
21267     },
21268
21269     //private
21270     setLastChild : function(node){
21271         this.lastChild = node;
21272     },
21273
21274
21275     /**
21276      * Returns true if this node is the last child of its parent
21277      * @return {Boolean}
21278      */
21279     isLast : function(){
21280        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21281     },
21282
21283     /**
21284      * Returns true if this node is the first child of its parent
21285      * @return {Boolean}
21286      */
21287     isFirst : function(){
21288        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21289     },
21290
21291     hasChildNodes : function(){
21292         return !this.isLeaf() && this.childNodes.length > 0;
21293     },
21294
21295     /**
21296      * Insert node(s) as the last child node of this node.
21297      * @param {Node/Array} node The node or Array of nodes to append
21298      * @return {Node} The appended node if single append, or null if an array was passed
21299      */
21300     appendChild : function(node){
21301         var multi = false;
21302         if(node instanceof Array){
21303             multi = node;
21304         }else if(arguments.length > 1){
21305             multi = arguments;
21306         }
21307         // if passed an array or multiple args do them one by one
21308         if(multi){
21309             for(var i = 0, len = multi.length; i < len; i++) {
21310                 this.appendChild(multi[i]);
21311             }
21312         }else{
21313             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21314                 return false;
21315             }
21316             var index = this.childNodes.length;
21317             var oldParent = node.parentNode;
21318             // it's a move, make sure we move it cleanly
21319             if(oldParent){
21320                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21321                     return false;
21322                 }
21323                 oldParent.removeChild(node);
21324             }
21325             index = this.childNodes.length;
21326             if(index == 0){
21327                 this.setFirstChild(node);
21328             }
21329             this.childNodes.push(node);
21330             node.parentNode = this;
21331             var ps = this.childNodes[index-1];
21332             if(ps){
21333                 node.previousSibling = ps;
21334                 ps.nextSibling = node;
21335             }else{
21336                 node.previousSibling = null;
21337             }
21338             node.nextSibling = null;
21339             this.setLastChild(node);
21340             node.setOwnerTree(this.getOwnerTree());
21341             this.fireEvent("append", this.ownerTree, this, node, index);
21342             if(oldParent){
21343                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21344             }
21345             return node;
21346         }
21347     },
21348
21349     /**
21350      * Removes a child node from this node.
21351      * @param {Node} node The node to remove
21352      * @return {Node} The removed node
21353      */
21354     removeChild : function(node){
21355         var index = this.childNodes.indexOf(node);
21356         if(index == -1){
21357             return false;
21358         }
21359         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21360             return false;
21361         }
21362
21363         // remove it from childNodes collection
21364         this.childNodes.splice(index, 1);
21365
21366         // update siblings
21367         if(node.previousSibling){
21368             node.previousSibling.nextSibling = node.nextSibling;
21369         }
21370         if(node.nextSibling){
21371             node.nextSibling.previousSibling = node.previousSibling;
21372         }
21373
21374         // update child refs
21375         if(this.firstChild == node){
21376             this.setFirstChild(node.nextSibling);
21377         }
21378         if(this.lastChild == node){
21379             this.setLastChild(node.previousSibling);
21380         }
21381
21382         node.setOwnerTree(null);
21383         // clear any references from the node
21384         node.parentNode = null;
21385         node.previousSibling = null;
21386         node.nextSibling = null;
21387         this.fireEvent("remove", this.ownerTree, this, node);
21388         return node;
21389     },
21390
21391     /**
21392      * Inserts the first node before the second node in this nodes childNodes collection.
21393      * @param {Node} node The node to insert
21394      * @param {Node} refNode The node to insert before (if null the node is appended)
21395      * @return {Node} The inserted node
21396      */
21397     insertBefore : function(node, refNode){
21398         if(!refNode){ // like standard Dom, refNode can be null for append
21399             return this.appendChild(node);
21400         }
21401         // nothing to do
21402         if(node == refNode){
21403             return false;
21404         }
21405
21406         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21407             return false;
21408         }
21409         var index = this.childNodes.indexOf(refNode);
21410         var oldParent = node.parentNode;
21411         var refIndex = index;
21412
21413         // when moving internally, indexes will change after remove
21414         if(oldParent == this && this.childNodes.indexOf(node) < index){
21415             refIndex--;
21416         }
21417
21418         // it's a move, make sure we move it cleanly
21419         if(oldParent){
21420             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21421                 return false;
21422             }
21423             oldParent.removeChild(node);
21424         }
21425         if(refIndex == 0){
21426             this.setFirstChild(node);
21427         }
21428         this.childNodes.splice(refIndex, 0, node);
21429         node.parentNode = this;
21430         var ps = this.childNodes[refIndex-1];
21431         if(ps){
21432             node.previousSibling = ps;
21433             ps.nextSibling = node;
21434         }else{
21435             node.previousSibling = null;
21436         }
21437         node.nextSibling = refNode;
21438         refNode.previousSibling = node;
21439         node.setOwnerTree(this.getOwnerTree());
21440         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21441         if(oldParent){
21442             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21443         }
21444         return node;
21445     },
21446
21447     /**
21448      * Returns the child node at the specified index.
21449      * @param {Number} index
21450      * @return {Node}
21451      */
21452     item : function(index){
21453         return this.childNodes[index];
21454     },
21455
21456     /**
21457      * Replaces one child node in this node with another.
21458      * @param {Node} newChild The replacement node
21459      * @param {Node} oldChild The node to replace
21460      * @return {Node} The replaced node
21461      */
21462     replaceChild : function(newChild, oldChild){
21463         this.insertBefore(newChild, oldChild);
21464         this.removeChild(oldChild);
21465         return oldChild;
21466     },
21467
21468     /**
21469      * Returns the index of a child node
21470      * @param {Node} node
21471      * @return {Number} The index of the node or -1 if it was not found
21472      */
21473     indexOf : function(child){
21474         return this.childNodes.indexOf(child);
21475     },
21476
21477     /**
21478      * Returns the tree this node is in.
21479      * @return {Tree}
21480      */
21481     getOwnerTree : function(){
21482         // if it doesn't have one, look for one
21483         if(!this.ownerTree){
21484             var p = this;
21485             while(p){
21486                 if(p.ownerTree){
21487                     this.ownerTree = p.ownerTree;
21488                     break;
21489                 }
21490                 p = p.parentNode;
21491             }
21492         }
21493         return this.ownerTree;
21494     },
21495
21496     /**
21497      * Returns depth of this node (the root node has a depth of 0)
21498      * @return {Number}
21499      */
21500     getDepth : function(){
21501         var depth = 0;
21502         var p = this;
21503         while(p.parentNode){
21504             ++depth;
21505             p = p.parentNode;
21506         }
21507         return depth;
21508     },
21509
21510     // private
21511     setOwnerTree : function(tree){
21512         // if it's move, we need to update everyone
21513         if(tree != this.ownerTree){
21514             if(this.ownerTree){
21515                 this.ownerTree.unregisterNode(this);
21516             }
21517             this.ownerTree = tree;
21518             var cs = this.childNodes;
21519             for(var i = 0, len = cs.length; i < len; i++) {
21520                 cs[i].setOwnerTree(tree);
21521             }
21522             if(tree){
21523                 tree.registerNode(this);
21524             }
21525         }
21526     },
21527
21528     /**
21529      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21530      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21531      * @return {String} The path
21532      */
21533     getPath : function(attr){
21534         attr = attr || "id";
21535         var p = this.parentNode;
21536         var b = [this.attributes[attr]];
21537         while(p){
21538             b.unshift(p.attributes[attr]);
21539             p = p.parentNode;
21540         }
21541         var sep = this.getOwnerTree().pathSeparator;
21542         return sep + b.join(sep);
21543     },
21544
21545     /**
21546      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21547      * function call will be the scope provided or the current node. The arguments to the function
21548      * will be the args provided or the current node. If the function returns false at any point,
21549      * the bubble is stopped.
21550      * @param {Function} fn The function to call
21551      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21552      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21553      */
21554     bubble : function(fn, scope, args){
21555         var p = this;
21556         while(p){
21557             if(fn.call(scope || p, args || p) === false){
21558                 break;
21559             }
21560             p = p.parentNode;
21561         }
21562     },
21563
21564     /**
21565      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21566      * function call will be the scope provided or the current node. The arguments to the function
21567      * will be the args provided or the current node. If the function returns false at any point,
21568      * the cascade is stopped on that branch.
21569      * @param {Function} fn The function to call
21570      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21571      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21572      */
21573     cascade : function(fn, scope, args){
21574         if(fn.call(scope || this, args || this) !== false){
21575             var cs = this.childNodes;
21576             for(var i = 0, len = cs.length; i < len; i++) {
21577                 cs[i].cascade(fn, scope, args);
21578             }
21579         }
21580     },
21581
21582     /**
21583      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21584      * function call will be the scope provided or the current node. The arguments to the function
21585      * will be the args provided or the current node. If the function returns false at any point,
21586      * the iteration stops.
21587      * @param {Function} fn The function to call
21588      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21589      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21590      */
21591     eachChild : function(fn, scope, args){
21592         var cs = this.childNodes;
21593         for(var i = 0, len = cs.length; i < len; i++) {
21594                 if(fn.call(scope || this, args || cs[i]) === false){
21595                     break;
21596                 }
21597         }
21598     },
21599
21600     /**
21601      * Finds the first child that has the attribute with the specified value.
21602      * @param {String} attribute The attribute name
21603      * @param {Mixed} value The value to search for
21604      * @return {Node} The found child or null if none was found
21605      */
21606     findChild : function(attribute, value){
21607         var cs = this.childNodes;
21608         for(var i = 0, len = cs.length; i < len; i++) {
21609                 if(cs[i].attributes[attribute] == value){
21610                     return cs[i];
21611                 }
21612         }
21613         return null;
21614     },
21615
21616     /**
21617      * Finds the first child by a custom function. The child matches if the function passed
21618      * returns true.
21619      * @param {Function} fn
21620      * @param {Object} scope (optional)
21621      * @return {Node} The found child or null if none was found
21622      */
21623     findChildBy : function(fn, scope){
21624         var cs = this.childNodes;
21625         for(var i = 0, len = cs.length; i < len; i++) {
21626                 if(fn.call(scope||cs[i], cs[i]) === true){
21627                     return cs[i];
21628                 }
21629         }
21630         return null;
21631     },
21632
21633     /**
21634      * Sorts this nodes children using the supplied sort function
21635      * @param {Function} fn
21636      * @param {Object} scope (optional)
21637      */
21638     sort : function(fn, scope){
21639         var cs = this.childNodes;
21640         var len = cs.length;
21641         if(len > 0){
21642             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21643             cs.sort(sortFn);
21644             for(var i = 0; i < len; i++){
21645                 var n = cs[i];
21646                 n.previousSibling = cs[i-1];
21647                 n.nextSibling = cs[i+1];
21648                 if(i == 0){
21649                     this.setFirstChild(n);
21650                 }
21651                 if(i == len-1){
21652                     this.setLastChild(n);
21653                 }
21654             }
21655         }
21656     },
21657
21658     /**
21659      * Returns true if this node is an ancestor (at any point) of the passed node.
21660      * @param {Node} node
21661      * @return {Boolean}
21662      */
21663     contains : function(node){
21664         return node.isAncestor(this);
21665     },
21666
21667     /**
21668      * Returns true if the passed node is an ancestor (at any point) of this node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     isAncestor : function(node){
21673         var p = this.parentNode;
21674         while(p){
21675             if(p == node){
21676                 return true;
21677             }
21678             p = p.parentNode;
21679         }
21680         return false;
21681     },
21682
21683     toString : function(){
21684         return "[Node"+(this.id?" "+this.id:"")+"]";
21685     }
21686 });/*
21687  * Based on:
21688  * Ext JS Library 1.1.1
21689  * Copyright(c) 2006-2007, Ext JS, LLC.
21690  *
21691  * Originally Released Under LGPL - original licence link has changed is not relivant.
21692  *
21693  * Fork - LGPL
21694  * <script type="text/javascript">
21695  */
21696  
21697
21698 /**
21699  * @class Roo.ComponentMgr
21700  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21701  * @singleton
21702  */
21703 Roo.ComponentMgr = function(){
21704     var all = new Roo.util.MixedCollection();
21705
21706     return {
21707         /**
21708          * Registers a component.
21709          * @param {Roo.Component} c The component
21710          */
21711         register : function(c){
21712             all.add(c);
21713         },
21714
21715         /**
21716          * Unregisters a component.
21717          * @param {Roo.Component} c The component
21718          */
21719         unregister : function(c){
21720             all.remove(c);
21721         },
21722
21723         /**
21724          * Returns a component by id
21725          * @param {String} id The component id
21726          */
21727         get : function(id){
21728             return all.get(id);
21729         },
21730
21731         /**
21732          * Registers a function that will be called when a specified component is added to ComponentMgr
21733          * @param {String} id The component id
21734          * @param {Funtction} fn The callback function
21735          * @param {Object} scope The scope of the callback
21736          */
21737         onAvailable : function(id, fn, scope){
21738             all.on("add", function(index, o){
21739                 if(o.id == id){
21740                     fn.call(scope || o, o);
21741                     all.un("add", fn, scope);
21742                 }
21743             });
21744         }
21745     };
21746 }();/*
21747  * Based on:
21748  * Ext JS Library 1.1.1
21749  * Copyright(c) 2006-2007, Ext JS, LLC.
21750  *
21751  * Originally Released Under LGPL - original licence link has changed is not relivant.
21752  *
21753  * Fork - LGPL
21754  * <script type="text/javascript">
21755  */
21756  
21757 /**
21758  * @class Roo.Component
21759  * @extends Roo.util.Observable
21760  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21761  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21762  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21763  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21764  * All visual components (widgets) that require rendering into a layout should subclass Component.
21765  * @constructor
21766  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21767  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21768  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21769  */
21770 Roo.Component = function(config){
21771     config = config || {};
21772     if(config.tagName || config.dom || typeof config == "string"){ // element object
21773         config = {el: config, id: config.id || config};
21774     }
21775     this.initialConfig = config;
21776
21777     Roo.apply(this, config);
21778     this.addEvents({
21779         /**
21780          * @event disable
21781          * Fires after the component is disabled.
21782              * @param {Roo.Component} this
21783              */
21784         disable : true,
21785         /**
21786          * @event enable
21787          * Fires after the component is enabled.
21788              * @param {Roo.Component} this
21789              */
21790         enable : true,
21791         /**
21792          * @event beforeshow
21793          * Fires before the component is shown.  Return false to stop the show.
21794              * @param {Roo.Component} this
21795              */
21796         beforeshow : true,
21797         /**
21798          * @event show
21799          * Fires after the component is shown.
21800              * @param {Roo.Component} this
21801              */
21802         show : true,
21803         /**
21804          * @event beforehide
21805          * Fires before the component is hidden. Return false to stop the hide.
21806              * @param {Roo.Component} this
21807              */
21808         beforehide : true,
21809         /**
21810          * @event hide
21811          * Fires after the component is hidden.
21812              * @param {Roo.Component} this
21813              */
21814         hide : true,
21815         /**
21816          * @event beforerender
21817          * Fires before the component is rendered. Return false to stop the render.
21818              * @param {Roo.Component} this
21819              */
21820         beforerender : true,
21821         /**
21822          * @event render
21823          * Fires after the component is rendered.
21824              * @param {Roo.Component} this
21825              */
21826         render : true,
21827         /**
21828          * @event beforedestroy
21829          * Fires before the component is destroyed. Return false to stop the destroy.
21830              * @param {Roo.Component} this
21831              */
21832         beforedestroy : true,
21833         /**
21834          * @event destroy
21835          * Fires after the component is destroyed.
21836              * @param {Roo.Component} this
21837              */
21838         destroy : true
21839     });
21840     if(!this.id){
21841         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21842     }
21843     Roo.ComponentMgr.register(this);
21844     Roo.Component.superclass.constructor.call(this);
21845     this.initComponent();
21846     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21847         this.render(this.renderTo);
21848         delete this.renderTo;
21849     }
21850 };
21851
21852 // private
21853 Roo.Component.AUTO_ID = 1000;
21854
21855 Roo.extend(Roo.Component, Roo.util.Observable, {
21856     /**
21857      * @property {Boolean} hidden
21858      * true if this component is hidden. Read-only.
21859      */
21860     hidden : false,
21861     /**
21862      * true if this component is disabled. Read-only.
21863      */
21864     disabled : false,
21865     /**
21866      * true if this component has been rendered. Read-only.
21867      */
21868     rendered : false,
21869     
21870     /** @cfg {String} disableClass
21871      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21872      */
21873     disabledClass : "x-item-disabled",
21874         /** @cfg {Boolean} allowDomMove
21875          * Whether the component can move the Dom node when rendering (defaults to true).
21876          */
21877     allowDomMove : true,
21878     /** @cfg {String} hideMode
21879      * How this component should hidden. Supported values are
21880      * "visibility" (css visibility), "offsets" (negative offset position) and
21881      * "display" (css display) - defaults to "display".
21882      */
21883     hideMode: 'display',
21884
21885     // private
21886     ctype : "Roo.Component",
21887
21888     /** @cfg {String} actionMode 
21889      * which property holds the element that used for  hide() / show() / disable() / enable()
21890      * default is 'el' 
21891      */
21892     actionMode : "el",
21893
21894     // private
21895     getActionEl : function(){
21896         return this[this.actionMode];
21897     },
21898
21899     initComponent : Roo.emptyFn,
21900     /**
21901      * If this is a lazy rendering component, render it to its container element.
21902      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21903      */
21904     render : function(container, position){
21905         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21906             if(!container && this.el){
21907                 this.el = Roo.get(this.el);
21908                 container = this.el.dom.parentNode;
21909                 this.allowDomMove = false;
21910             }
21911             this.container = Roo.get(container);
21912             this.rendered = true;
21913             if(position !== undefined){
21914                 if(typeof position == 'number'){
21915                     position = this.container.dom.childNodes[position];
21916                 }else{
21917                     position = Roo.getDom(position);
21918                 }
21919             }
21920             this.onRender(this.container, position || null);
21921             if(this.cls){
21922                 this.el.addClass(this.cls);
21923                 delete this.cls;
21924             }
21925             if(this.style){
21926                 this.el.applyStyles(this.style);
21927                 delete this.style;
21928             }
21929             this.fireEvent("render", this);
21930             this.afterRender(this.container);
21931             if(this.hidden){
21932                 this.hide();
21933             }
21934             if(this.disabled){
21935                 this.disable();
21936             }
21937         }
21938         return this;
21939     },
21940
21941     // private
21942     // default function is not really useful
21943     onRender : function(ct, position){
21944         if(this.el){
21945             this.el = Roo.get(this.el);
21946             if(this.allowDomMove !== false){
21947                 ct.dom.insertBefore(this.el.dom, position);
21948             }
21949         }
21950     },
21951
21952     // private
21953     getAutoCreate : function(){
21954         var cfg = typeof this.autoCreate == "object" ?
21955                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21956         if(this.id && !cfg.id){
21957             cfg.id = this.id;
21958         }
21959         return cfg;
21960     },
21961
21962     // private
21963     afterRender : Roo.emptyFn,
21964
21965     /**
21966      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21967      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21968      */
21969     destroy : function(){
21970         if(this.fireEvent("beforedestroy", this) !== false){
21971             this.purgeListeners();
21972             this.beforeDestroy();
21973             if(this.rendered){
21974                 this.el.removeAllListeners();
21975                 this.el.remove();
21976                 if(this.actionMode == "container"){
21977                     this.container.remove();
21978                 }
21979             }
21980             this.onDestroy();
21981             Roo.ComponentMgr.unregister(this);
21982             this.fireEvent("destroy", this);
21983         }
21984     },
21985
21986         // private
21987     beforeDestroy : function(){
21988
21989     },
21990
21991         // private
21992         onDestroy : function(){
21993
21994     },
21995
21996     /**
21997      * Returns the underlying {@link Roo.Element}.
21998      * @return {Roo.Element} The element
21999      */
22000     getEl : function(){
22001         return this.el;
22002     },
22003
22004     /**
22005      * Returns the id of this component.
22006      * @return {String}
22007      */
22008     getId : function(){
22009         return this.id;
22010     },
22011
22012     /**
22013      * Try to focus this component.
22014      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22015      * @return {Roo.Component} this
22016      */
22017     focus : function(selectText){
22018         if(this.rendered){
22019             this.el.focus();
22020             if(selectText === true){
22021                 this.el.dom.select();
22022             }
22023         }
22024         return this;
22025     },
22026
22027     // private
22028     blur : function(){
22029         if(this.rendered){
22030             this.el.blur();
22031         }
22032         return this;
22033     },
22034
22035     /**
22036      * Disable this component.
22037      * @return {Roo.Component} this
22038      */
22039     disable : function(){
22040         if(this.rendered){
22041             this.onDisable();
22042         }
22043         this.disabled = true;
22044         this.fireEvent("disable", this);
22045         return this;
22046     },
22047
22048         // private
22049     onDisable : function(){
22050         this.getActionEl().addClass(this.disabledClass);
22051         this.el.dom.disabled = true;
22052     },
22053
22054     /**
22055      * Enable this component.
22056      * @return {Roo.Component} this
22057      */
22058     enable : function(){
22059         if(this.rendered){
22060             this.onEnable();
22061         }
22062         this.disabled = false;
22063         this.fireEvent("enable", this);
22064         return this;
22065     },
22066
22067         // private
22068     onEnable : function(){
22069         this.getActionEl().removeClass(this.disabledClass);
22070         this.el.dom.disabled = false;
22071     },
22072
22073     /**
22074      * Convenience function for setting disabled/enabled by boolean.
22075      * @param {Boolean} disabled
22076      */
22077     setDisabled : function(disabled){
22078         this[disabled ? "disable" : "enable"]();
22079     },
22080
22081     /**
22082      * Show this component.
22083      * @return {Roo.Component} this
22084      */
22085     show: function(){
22086         if(this.fireEvent("beforeshow", this) !== false){
22087             this.hidden = false;
22088             if(this.rendered){
22089                 this.onShow();
22090             }
22091             this.fireEvent("show", this);
22092         }
22093         return this;
22094     },
22095
22096     // private
22097     onShow : function(){
22098         var ae = this.getActionEl();
22099         if(this.hideMode == 'visibility'){
22100             ae.dom.style.visibility = "visible";
22101         }else if(this.hideMode == 'offsets'){
22102             ae.removeClass('x-hidden');
22103         }else{
22104             ae.dom.style.display = "";
22105         }
22106     },
22107
22108     /**
22109      * Hide this component.
22110      * @return {Roo.Component} this
22111      */
22112     hide: function(){
22113         if(this.fireEvent("beforehide", this) !== false){
22114             this.hidden = true;
22115             if(this.rendered){
22116                 this.onHide();
22117             }
22118             this.fireEvent("hide", this);
22119         }
22120         return this;
22121     },
22122
22123     // private
22124     onHide : function(){
22125         var ae = this.getActionEl();
22126         if(this.hideMode == 'visibility'){
22127             ae.dom.style.visibility = "hidden";
22128         }else if(this.hideMode == 'offsets'){
22129             ae.addClass('x-hidden');
22130         }else{
22131             ae.dom.style.display = "none";
22132         }
22133     },
22134
22135     /**
22136      * Convenience function to hide or show this component by boolean.
22137      * @param {Boolean} visible True to show, false to hide
22138      * @return {Roo.Component} this
22139      */
22140     setVisible: function(visible){
22141         if(visible) {
22142             this.show();
22143         }else{
22144             this.hide();
22145         }
22146         return this;
22147     },
22148
22149     /**
22150      * Returns true if this component is visible.
22151      */
22152     isVisible : function(){
22153         return this.getActionEl().isVisible();
22154     },
22155
22156     cloneConfig : function(overrides){
22157         overrides = overrides || {};
22158         var id = overrides.id || Roo.id();
22159         var cfg = Roo.applyIf(overrides, this.initialConfig);
22160         cfg.id = id; // prevent dup id
22161         return new this.constructor(cfg);
22162     }
22163 });/*
22164  * Based on:
22165  * Ext JS Library 1.1.1
22166  * Copyright(c) 2006-2007, Ext JS, LLC.
22167  *
22168  * Originally Released Under LGPL - original licence link has changed is not relivant.
22169  *
22170  * Fork - LGPL
22171  * <script type="text/javascript">
22172  */
22173  (function(){ 
22174 /**
22175  * @class Roo.Layer
22176  * @extends Roo.Element
22177  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22178  * automatic maintaining of shadow/shim positions.
22179  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22180  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22181  * you can pass a string with a CSS class name. False turns off the shadow.
22182  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22183  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22184  * @cfg {String} cls CSS class to add to the element
22185  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22186  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22187  * @constructor
22188  * @param {Object} config An object with config options.
22189  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22190  */
22191
22192 Roo.Layer = function(config, existingEl){
22193     config = config || {};
22194     var dh = Roo.DomHelper;
22195     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22196     if(existingEl){
22197         this.dom = Roo.getDom(existingEl);
22198     }
22199     if(!this.dom){
22200         var o = config.dh || {tag: "div", cls: "x-layer"};
22201         this.dom = dh.append(pel, o);
22202     }
22203     if(config.cls){
22204         this.addClass(config.cls);
22205     }
22206     this.constrain = config.constrain !== false;
22207     this.visibilityMode = Roo.Element.VISIBILITY;
22208     if(config.id){
22209         this.id = this.dom.id = config.id;
22210     }else{
22211         this.id = Roo.id(this.dom);
22212     }
22213     this.zindex = config.zindex || this.getZIndex();
22214     this.position("absolute", this.zindex);
22215     if(config.shadow){
22216         this.shadowOffset = config.shadowOffset || 4;
22217         this.shadow = new Roo.Shadow({
22218             offset : this.shadowOffset,
22219             mode : config.shadow
22220         });
22221     }else{
22222         this.shadowOffset = 0;
22223     }
22224     this.useShim = config.shim !== false && Roo.useShims;
22225     this.useDisplay = config.useDisplay;
22226     this.hide();
22227 };
22228
22229 var supr = Roo.Element.prototype;
22230
22231 // shims are shared among layer to keep from having 100 iframes
22232 var shims = [];
22233
22234 Roo.extend(Roo.Layer, Roo.Element, {
22235
22236     getZIndex : function(){
22237         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22238     },
22239
22240     getShim : function(){
22241         if(!this.useShim){
22242             return null;
22243         }
22244         if(this.shim){
22245             return this.shim;
22246         }
22247         var shim = shims.shift();
22248         if(!shim){
22249             shim = this.createShim();
22250             shim.enableDisplayMode('block');
22251             shim.dom.style.display = 'none';
22252             shim.dom.style.visibility = 'visible';
22253         }
22254         var pn = this.dom.parentNode;
22255         if(shim.dom.parentNode != pn){
22256             pn.insertBefore(shim.dom, this.dom);
22257         }
22258         shim.setStyle('z-index', this.getZIndex()-2);
22259         this.shim = shim;
22260         return shim;
22261     },
22262
22263     hideShim : function(){
22264         if(this.shim){
22265             this.shim.setDisplayed(false);
22266             shims.push(this.shim);
22267             delete this.shim;
22268         }
22269     },
22270
22271     disableShadow : function(){
22272         if(this.shadow){
22273             this.shadowDisabled = true;
22274             this.shadow.hide();
22275             this.lastShadowOffset = this.shadowOffset;
22276             this.shadowOffset = 0;
22277         }
22278     },
22279
22280     enableShadow : function(show){
22281         if(this.shadow){
22282             this.shadowDisabled = false;
22283             this.shadowOffset = this.lastShadowOffset;
22284             delete this.lastShadowOffset;
22285             if(show){
22286                 this.sync(true);
22287             }
22288         }
22289     },
22290
22291     // private
22292     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22293     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22294     sync : function(doShow){
22295         var sw = this.shadow;
22296         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22297             var sh = this.getShim();
22298
22299             var w = this.getWidth(),
22300                 h = this.getHeight();
22301
22302             var l = this.getLeft(true),
22303                 t = this.getTop(true);
22304
22305             if(sw && !this.shadowDisabled){
22306                 if(doShow && !sw.isVisible()){
22307                     sw.show(this);
22308                 }else{
22309                     sw.realign(l, t, w, h);
22310                 }
22311                 if(sh){
22312                     if(doShow){
22313                        sh.show();
22314                     }
22315                     // fit the shim behind the shadow, so it is shimmed too
22316                     var a = sw.adjusts, s = sh.dom.style;
22317                     s.left = (Math.min(l, l+a.l))+"px";
22318                     s.top = (Math.min(t, t+a.t))+"px";
22319                     s.width = (w+a.w)+"px";
22320                     s.height = (h+a.h)+"px";
22321                 }
22322             }else if(sh){
22323                 if(doShow){
22324                    sh.show();
22325                 }
22326                 sh.setSize(w, h);
22327                 sh.setLeftTop(l, t);
22328             }
22329             
22330         }
22331     },
22332
22333     // private
22334     destroy : function(){
22335         this.hideShim();
22336         if(this.shadow){
22337             this.shadow.hide();
22338         }
22339         this.removeAllListeners();
22340         var pn = this.dom.parentNode;
22341         if(pn){
22342             pn.removeChild(this.dom);
22343         }
22344         Roo.Element.uncache(this.id);
22345     },
22346
22347     remove : function(){
22348         this.destroy();
22349     },
22350
22351     // private
22352     beginUpdate : function(){
22353         this.updating = true;
22354     },
22355
22356     // private
22357     endUpdate : function(){
22358         this.updating = false;
22359         this.sync(true);
22360     },
22361
22362     // private
22363     hideUnders : function(negOffset){
22364         if(this.shadow){
22365             this.shadow.hide();
22366         }
22367         this.hideShim();
22368     },
22369
22370     // private
22371     constrainXY : function(){
22372         if(this.constrain){
22373             var vw = Roo.lib.Dom.getViewWidth(),
22374                 vh = Roo.lib.Dom.getViewHeight();
22375             var s = Roo.get(document).getScroll();
22376
22377             var xy = this.getXY();
22378             var x = xy[0], y = xy[1];   
22379             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22380             // only move it if it needs it
22381             var moved = false;
22382             // first validate right/bottom
22383             if((x + w) > vw+s.left){
22384                 x = vw - w - this.shadowOffset;
22385                 moved = true;
22386             }
22387             if((y + h) > vh+s.top){
22388                 y = vh - h - this.shadowOffset;
22389                 moved = true;
22390             }
22391             // then make sure top/left isn't negative
22392             if(x < s.left){
22393                 x = s.left;
22394                 moved = true;
22395             }
22396             if(y < s.top){
22397                 y = s.top;
22398                 moved = true;
22399             }
22400             if(moved){
22401                 if(this.avoidY){
22402                     var ay = this.avoidY;
22403                     if(y <= ay && (y+h) >= ay){
22404                         y = ay-h-5;   
22405                     }
22406                 }
22407                 xy = [x, y];
22408                 this.storeXY(xy);
22409                 supr.setXY.call(this, xy);
22410                 this.sync();
22411             }
22412         }
22413     },
22414
22415     isVisible : function(){
22416         return this.visible;    
22417     },
22418
22419     // private
22420     showAction : function(){
22421         this.visible = true; // track visibility to prevent getStyle calls
22422         if(this.useDisplay === true){
22423             this.setDisplayed("");
22424         }else if(this.lastXY){
22425             supr.setXY.call(this, this.lastXY);
22426         }else if(this.lastLT){
22427             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22428         }
22429     },
22430
22431     // private
22432     hideAction : function(){
22433         this.visible = false;
22434         if(this.useDisplay === true){
22435             this.setDisplayed(false);
22436         }else{
22437             this.setLeftTop(-10000,-10000);
22438         }
22439     },
22440
22441     // overridden Element method
22442     setVisible : function(v, a, d, c, e){
22443         if(v){
22444             this.showAction();
22445         }
22446         if(a && v){
22447             var cb = function(){
22448                 this.sync(true);
22449                 if(c){
22450                     c();
22451                 }
22452             }.createDelegate(this);
22453             supr.setVisible.call(this, true, true, d, cb, e);
22454         }else{
22455             if(!v){
22456                 this.hideUnders(true);
22457             }
22458             var cb = c;
22459             if(a){
22460                 cb = function(){
22461                     this.hideAction();
22462                     if(c){
22463                         c();
22464                     }
22465                 }.createDelegate(this);
22466             }
22467             supr.setVisible.call(this, v, a, d, cb, e);
22468             if(v){
22469                 this.sync(true);
22470             }else if(!a){
22471                 this.hideAction();
22472             }
22473         }
22474     },
22475
22476     storeXY : function(xy){
22477         delete this.lastLT;
22478         this.lastXY = xy;
22479     },
22480
22481     storeLeftTop : function(left, top){
22482         delete this.lastXY;
22483         this.lastLT = [left, top];
22484     },
22485
22486     // private
22487     beforeFx : function(){
22488         this.beforeAction();
22489         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22490     },
22491
22492     // private
22493     afterFx : function(){
22494         Roo.Layer.superclass.afterFx.apply(this, arguments);
22495         this.sync(this.isVisible());
22496     },
22497
22498     // private
22499     beforeAction : function(){
22500         if(!this.updating && this.shadow){
22501             this.shadow.hide();
22502         }
22503     },
22504
22505     // overridden Element method
22506     setLeft : function(left){
22507         this.storeLeftTop(left, this.getTop(true));
22508         supr.setLeft.apply(this, arguments);
22509         this.sync();
22510     },
22511
22512     setTop : function(top){
22513         this.storeLeftTop(this.getLeft(true), top);
22514         supr.setTop.apply(this, arguments);
22515         this.sync();
22516     },
22517
22518     setLeftTop : function(left, top){
22519         this.storeLeftTop(left, top);
22520         supr.setLeftTop.apply(this, arguments);
22521         this.sync();
22522     },
22523
22524     setXY : function(xy, a, d, c, e){
22525         this.fixDisplay();
22526         this.beforeAction();
22527         this.storeXY(xy);
22528         var cb = this.createCB(c);
22529         supr.setXY.call(this, xy, a, d, cb, e);
22530         if(!a){
22531             cb();
22532         }
22533     },
22534
22535     // private
22536     createCB : function(c){
22537         var el = this;
22538         return function(){
22539             el.constrainXY();
22540             el.sync(true);
22541             if(c){
22542                 c();
22543             }
22544         };
22545     },
22546
22547     // overridden Element method
22548     setX : function(x, a, d, c, e){
22549         this.setXY([x, this.getY()], a, d, c, e);
22550     },
22551
22552     // overridden Element method
22553     setY : function(y, a, d, c, e){
22554         this.setXY([this.getX(), y], a, d, c, e);
22555     },
22556
22557     // overridden Element method
22558     setSize : function(w, h, a, d, c, e){
22559         this.beforeAction();
22560         var cb = this.createCB(c);
22561         supr.setSize.call(this, w, h, a, d, cb, e);
22562         if(!a){
22563             cb();
22564         }
22565     },
22566
22567     // overridden Element method
22568     setWidth : function(w, a, d, c, e){
22569         this.beforeAction();
22570         var cb = this.createCB(c);
22571         supr.setWidth.call(this, w, a, d, cb, e);
22572         if(!a){
22573             cb();
22574         }
22575     },
22576
22577     // overridden Element method
22578     setHeight : function(h, a, d, c, e){
22579         this.beforeAction();
22580         var cb = this.createCB(c);
22581         supr.setHeight.call(this, h, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // overridden Element method
22588     setBounds : function(x, y, w, h, a, d, c, e){
22589         this.beforeAction();
22590         var cb = this.createCB(c);
22591         if(!a){
22592             this.storeXY([x, y]);
22593             supr.setXY.call(this, [x, y]);
22594             supr.setSize.call(this, w, h, a, d, cb, e);
22595             cb();
22596         }else{
22597             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22598         }
22599         return this;
22600     },
22601     
22602     /**
22603      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22604      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22605      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22606      * @param {Number} zindex The new z-index to set
22607      * @return {this} The Layer
22608      */
22609     setZIndex : function(zindex){
22610         this.zindex = zindex;
22611         this.setStyle("z-index", zindex + 2);
22612         if(this.shadow){
22613             this.shadow.setZIndex(zindex + 1);
22614         }
22615         if(this.shim){
22616             this.shim.setStyle("z-index", zindex);
22617         }
22618     }
22619 });
22620 })();/*
22621  * Based on:
22622  * Ext JS Library 1.1.1
22623  * Copyright(c) 2006-2007, Ext JS, LLC.
22624  *
22625  * Originally Released Under LGPL - original licence link has changed is not relivant.
22626  *
22627  * Fork - LGPL
22628  * <script type="text/javascript">
22629  */
22630
22631
22632 /**
22633  * @class Roo.Shadow
22634  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22635  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22636  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22637  * @constructor
22638  * Create a new Shadow
22639  * @param {Object} config The config object
22640  */
22641 Roo.Shadow = function(config){
22642     Roo.apply(this, config);
22643     if(typeof this.mode != "string"){
22644         this.mode = this.defaultMode;
22645     }
22646     var o = this.offset, a = {h: 0};
22647     var rad = Math.floor(this.offset/2);
22648     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22649         case "drop":
22650             a.w = 0;
22651             a.l = a.t = o;
22652             a.t -= 1;
22653             if(Roo.isIE){
22654                 a.l -= this.offset + rad;
22655                 a.t -= this.offset + rad;
22656                 a.w -= rad;
22657                 a.h -= rad;
22658                 a.t += 1;
22659             }
22660         break;
22661         case "sides":
22662             a.w = (o*2);
22663             a.l = -o;
22664             a.t = o-1;
22665             if(Roo.isIE){
22666                 a.l -= (this.offset - rad);
22667                 a.t -= this.offset + rad;
22668                 a.l += 1;
22669                 a.w -= (this.offset - rad)*2;
22670                 a.w -= rad + 1;
22671                 a.h -= 1;
22672             }
22673         break;
22674         case "frame":
22675             a.w = a.h = (o*2);
22676             a.l = a.t = -o;
22677             a.t += 1;
22678             a.h -= 2;
22679             if(Roo.isIE){
22680                 a.l -= (this.offset - rad);
22681                 a.t -= (this.offset - rad);
22682                 a.l += 1;
22683                 a.w -= (this.offset + rad + 1);
22684                 a.h -= (this.offset + rad);
22685                 a.h += 1;
22686             }
22687         break;
22688     };
22689
22690     this.adjusts = a;
22691 };
22692
22693 Roo.Shadow.prototype = {
22694     /**
22695      * @cfg {String} mode
22696      * The shadow display mode.  Supports the following options:<br />
22697      * sides: Shadow displays on both sides and bottom only<br />
22698      * frame: Shadow displays equally on all four sides<br />
22699      * drop: Traditional bottom-right drop shadow (default)
22700      */
22701     /**
22702      * @cfg {String} offset
22703      * The number of pixels to offset the shadow from the element (defaults to 4)
22704      */
22705     offset: 4,
22706
22707     // private
22708     defaultMode: "drop",
22709
22710     /**
22711      * Displays the shadow under the target element
22712      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22713      */
22714     show : function(target){
22715         target = Roo.get(target);
22716         if(!this.el){
22717             this.el = Roo.Shadow.Pool.pull();
22718             if(this.el.dom.nextSibling != target.dom){
22719                 this.el.insertBefore(target);
22720             }
22721         }
22722         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22723         if(Roo.isIE){
22724             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22725         }
22726         this.realign(
22727             target.getLeft(true),
22728             target.getTop(true),
22729             target.getWidth(),
22730             target.getHeight()
22731         );
22732         this.el.dom.style.display = "block";
22733     },
22734
22735     /**
22736      * Returns true if the shadow is visible, else false
22737      */
22738     isVisible : function(){
22739         return this.el ? true : false;  
22740     },
22741
22742     /**
22743      * Direct alignment when values are already available. Show must be called at least once before
22744      * calling this method to ensure it is initialized.
22745      * @param {Number} left The target element left position
22746      * @param {Number} top The target element top position
22747      * @param {Number} width The target element width
22748      * @param {Number} height The target element height
22749      */
22750     realign : function(l, t, w, h){
22751         if(!this.el){
22752             return;
22753         }
22754         var a = this.adjusts, d = this.el.dom, s = d.style;
22755         var iea = 0;
22756         s.left = (l+a.l)+"px";
22757         s.top = (t+a.t)+"px";
22758         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22759  
22760         if(s.width != sws || s.height != shs){
22761             s.width = sws;
22762             s.height = shs;
22763             if(!Roo.isIE){
22764                 var cn = d.childNodes;
22765                 var sww = Math.max(0, (sw-12))+"px";
22766                 cn[0].childNodes[1].style.width = sww;
22767                 cn[1].childNodes[1].style.width = sww;
22768                 cn[2].childNodes[1].style.width = sww;
22769                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22770             }
22771         }
22772     },
22773
22774     /**
22775      * Hides this shadow
22776      */
22777     hide : function(){
22778         if(this.el){
22779             this.el.dom.style.display = "none";
22780             Roo.Shadow.Pool.push(this.el);
22781             delete this.el;
22782         }
22783     },
22784
22785     /**
22786      * Adjust the z-index of this shadow
22787      * @param {Number} zindex The new z-index
22788      */
22789     setZIndex : function(z){
22790         this.zIndex = z;
22791         if(this.el){
22792             this.el.setStyle("z-index", z);
22793         }
22794     }
22795 };
22796
22797 // Private utility class that manages the internal Shadow cache
22798 Roo.Shadow.Pool = function(){
22799     var p = [];
22800     var markup = Roo.isIE ?
22801                  '<div class="x-ie-shadow"></div>' :
22802                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22803     return {
22804         pull : function(){
22805             var sh = p.shift();
22806             if(!sh){
22807                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22808                 sh.autoBoxAdjust = false;
22809             }
22810             return sh;
22811         },
22812
22813         push : function(sh){
22814             p.push(sh);
22815         }
22816     };
22817 }();/*
22818  * Based on:
22819  * Ext JS Library 1.1.1
22820  * Copyright(c) 2006-2007, Ext JS, LLC.
22821  *
22822  * Originally Released Under LGPL - original licence link has changed is not relivant.
22823  *
22824  * Fork - LGPL
22825  * <script type="text/javascript">
22826  */
22827
22828 /**
22829  * @class Roo.BoxComponent
22830  * @extends Roo.Component
22831  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22832  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22833  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22834  * layout containers.
22835  * @constructor
22836  * @param {Roo.Element/String/Object} config The configuration options.
22837  */
22838 Roo.BoxComponent = function(config){
22839     Roo.Component.call(this, config);
22840     this.addEvents({
22841         /**
22842          * @event resize
22843          * Fires after the component is resized.
22844              * @param {Roo.Component} this
22845              * @param {Number} adjWidth The box-adjusted width that was set
22846              * @param {Number} adjHeight The box-adjusted height that was set
22847              * @param {Number} rawWidth The width that was originally specified
22848              * @param {Number} rawHeight The height that was originally specified
22849              */
22850         resize : true,
22851         /**
22852          * @event move
22853          * Fires after the component is moved.
22854              * @param {Roo.Component} this
22855              * @param {Number} x The new x position
22856              * @param {Number} y The new y position
22857              */
22858         move : true
22859     });
22860 };
22861
22862 Roo.extend(Roo.BoxComponent, Roo.Component, {
22863     // private, set in afterRender to signify that the component has been rendered
22864     boxReady : false,
22865     // private, used to defer height settings to subclasses
22866     deferHeight: false,
22867     /** @cfg {Number} width
22868      * width (optional) size of component
22869      */
22870      /** @cfg {Number} height
22871      * height (optional) size of component
22872      */
22873      
22874     /**
22875      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22876      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22877      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22878      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22879      * @return {Roo.BoxComponent} this
22880      */
22881     setSize : function(w, h){
22882         // support for standard size objects
22883         if(typeof w == 'object'){
22884             h = w.height;
22885             w = w.width;
22886         }
22887         // not rendered
22888         if(!this.boxReady){
22889             this.width = w;
22890             this.height = h;
22891             return this;
22892         }
22893
22894         // prevent recalcs when not needed
22895         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22896             return this;
22897         }
22898         this.lastSize = {width: w, height: h};
22899
22900         var adj = this.adjustSize(w, h);
22901         var aw = adj.width, ah = adj.height;
22902         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22903             var rz = this.getResizeEl();
22904             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22905                 rz.setSize(aw, ah);
22906             }else if(!this.deferHeight && ah !== undefined){
22907                 rz.setHeight(ah);
22908             }else if(aw !== undefined){
22909                 rz.setWidth(aw);
22910             }
22911             this.onResize(aw, ah, w, h);
22912             this.fireEvent('resize', this, aw, ah, w, h);
22913         }
22914         return this;
22915     },
22916
22917     /**
22918      * Gets the current size of the component's underlying element.
22919      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22920      */
22921     getSize : function(){
22922         return this.el.getSize();
22923     },
22924
22925     /**
22926      * Gets the current XY position of the component's underlying element.
22927      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22928      * @return {Array} The XY position of the element (e.g., [100, 200])
22929      */
22930     getPosition : function(local){
22931         if(local === true){
22932             return [this.el.getLeft(true), this.el.getTop(true)];
22933         }
22934         return this.xy || this.el.getXY();
22935     },
22936
22937     /**
22938      * Gets the current box measurements of the component's underlying element.
22939      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22940      * @returns {Object} box An object in the format {x, y, width, height}
22941      */
22942     getBox : function(local){
22943         var s = this.el.getSize();
22944         if(local){
22945             s.x = this.el.getLeft(true);
22946             s.y = this.el.getTop(true);
22947         }else{
22948             var xy = this.xy || this.el.getXY();
22949             s.x = xy[0];
22950             s.y = xy[1];
22951         }
22952         return s;
22953     },
22954
22955     /**
22956      * Sets the current box measurements of the component's underlying element.
22957      * @param {Object} box An object in the format {x, y, width, height}
22958      * @returns {Roo.BoxComponent} this
22959      */
22960     updateBox : function(box){
22961         this.setSize(box.width, box.height);
22962         this.setPagePosition(box.x, box.y);
22963         return this;
22964     },
22965
22966     // protected
22967     getResizeEl : function(){
22968         return this.resizeEl || this.el;
22969     },
22970
22971     // protected
22972     getPositionEl : function(){
22973         return this.positionEl || this.el;
22974     },
22975
22976     /**
22977      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22978      * This method fires the move event.
22979      * @param {Number} left The new left
22980      * @param {Number} top The new top
22981      * @returns {Roo.BoxComponent} this
22982      */
22983     setPosition : function(x, y){
22984         this.x = x;
22985         this.y = y;
22986         if(!this.boxReady){
22987             return this;
22988         }
22989         var adj = this.adjustPosition(x, y);
22990         var ax = adj.x, ay = adj.y;
22991
22992         var el = this.getPositionEl();
22993         if(ax !== undefined || ay !== undefined){
22994             if(ax !== undefined && ay !== undefined){
22995                 el.setLeftTop(ax, ay);
22996             }else if(ax !== undefined){
22997                 el.setLeft(ax);
22998             }else if(ay !== undefined){
22999                 el.setTop(ay);
23000             }
23001             this.onPosition(ax, ay);
23002             this.fireEvent('move', this, ax, ay);
23003         }
23004         return this;
23005     },
23006
23007     /**
23008      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23009      * This method fires the move event.
23010      * @param {Number} x The new x position
23011      * @param {Number} y The new y position
23012      * @returns {Roo.BoxComponent} this
23013      */
23014     setPagePosition : function(x, y){
23015         this.pageX = x;
23016         this.pageY = y;
23017         if(!this.boxReady){
23018             return;
23019         }
23020         if(x === undefined || y === undefined){ // cannot translate undefined points
23021             return;
23022         }
23023         var p = this.el.translatePoints(x, y);
23024         this.setPosition(p.left, p.top);
23025         return this;
23026     },
23027
23028     // private
23029     onRender : function(ct, position){
23030         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23031         if(this.resizeEl){
23032             this.resizeEl = Roo.get(this.resizeEl);
23033         }
23034         if(this.positionEl){
23035             this.positionEl = Roo.get(this.positionEl);
23036         }
23037     },
23038
23039     // private
23040     afterRender : function(){
23041         Roo.BoxComponent.superclass.afterRender.call(this);
23042         this.boxReady = true;
23043         this.setSize(this.width, this.height);
23044         if(this.x || this.y){
23045             this.setPosition(this.x, this.y);
23046         }
23047         if(this.pageX || this.pageY){
23048             this.setPagePosition(this.pageX, this.pageY);
23049         }
23050     },
23051
23052     /**
23053      * Force the component's size to recalculate based on the underlying element's current height and width.
23054      * @returns {Roo.BoxComponent} this
23055      */
23056     syncSize : function(){
23057         delete this.lastSize;
23058         this.setSize(this.el.getWidth(), this.el.getHeight());
23059         return this;
23060     },
23061
23062     /**
23063      * Called after the component is resized, this method is empty by default but can be implemented by any
23064      * subclass that needs to perform custom logic after a resize occurs.
23065      * @param {Number} adjWidth The box-adjusted width that was set
23066      * @param {Number} adjHeight The box-adjusted height that was set
23067      * @param {Number} rawWidth The width that was originally specified
23068      * @param {Number} rawHeight The height that was originally specified
23069      */
23070     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23071
23072     },
23073
23074     /**
23075      * Called after the component is moved, this method is empty by default but can be implemented by any
23076      * subclass that needs to perform custom logic after a move occurs.
23077      * @param {Number} x The new x position
23078      * @param {Number} y The new y position
23079      */
23080     onPosition : function(x, y){
23081
23082     },
23083
23084     // private
23085     adjustSize : function(w, h){
23086         if(this.autoWidth){
23087             w = 'auto';
23088         }
23089         if(this.autoHeight){
23090             h = 'auto';
23091         }
23092         return {width : w, height: h};
23093     },
23094
23095     // private
23096     adjustPosition : function(x, y){
23097         return {x : x, y: y};
23098     }
23099 });/*
23100  * Based on:
23101  * Ext JS Library 1.1.1
23102  * Copyright(c) 2006-2007, Ext JS, LLC.
23103  *
23104  * Originally Released Under LGPL - original licence link has changed is not relivant.
23105  *
23106  * Fork - LGPL
23107  * <script type="text/javascript">
23108  */
23109
23110
23111 /**
23112  * @class Roo.SplitBar
23113  * @extends Roo.util.Observable
23114  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23115  * <br><br>
23116  * Usage:
23117  * <pre><code>
23118 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23119                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23120 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23121 split.minSize = 100;
23122 split.maxSize = 600;
23123 split.animate = true;
23124 split.on('moved', splitterMoved);
23125 </code></pre>
23126  * @constructor
23127  * Create a new SplitBar
23128  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23129  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23130  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23131  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23132                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23133                         position of the SplitBar).
23134  */
23135 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23136     
23137     /** @private */
23138     this.el = Roo.get(dragElement, true);
23139     this.el.dom.unselectable = "on";
23140     /** @private */
23141     this.resizingEl = Roo.get(resizingElement, true);
23142
23143     /**
23144      * @private
23145      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23146      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23147      * @type Number
23148      */
23149     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23150     
23151     /**
23152      * The minimum size of the resizing element. (Defaults to 0)
23153      * @type Number
23154      */
23155     this.minSize = 0;
23156     
23157     /**
23158      * The maximum size of the resizing element. (Defaults to 2000)
23159      * @type Number
23160      */
23161     this.maxSize = 2000;
23162     
23163     /**
23164      * Whether to animate the transition to the new size
23165      * @type Boolean
23166      */
23167     this.animate = false;
23168     
23169     /**
23170      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23171      * @type Boolean
23172      */
23173     this.useShim = false;
23174     
23175     /** @private */
23176     this.shim = null;
23177     
23178     if(!existingProxy){
23179         /** @private */
23180         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23181     }else{
23182         this.proxy = Roo.get(existingProxy).dom;
23183     }
23184     /** @private */
23185     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23186     
23187     /** @private */
23188     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23189     
23190     /** @private */
23191     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23192     
23193     /** @private */
23194     this.dragSpecs = {};
23195     
23196     /**
23197      * @private The adapter to use to positon and resize elements
23198      */
23199     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23200     this.adapter.init(this);
23201     
23202     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23203         /** @private */
23204         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23205         this.el.addClass("x-splitbar-h");
23206     }else{
23207         /** @private */
23208         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23209         this.el.addClass("x-splitbar-v");
23210     }
23211     
23212     this.addEvents({
23213         /**
23214          * @event resize
23215          * Fires when the splitter is moved (alias for {@link #event-moved})
23216          * @param {Roo.SplitBar} this
23217          * @param {Number} newSize the new width or height
23218          */
23219         "resize" : true,
23220         /**
23221          * @event moved
23222          * Fires when the splitter is moved
23223          * @param {Roo.SplitBar} this
23224          * @param {Number} newSize the new width or height
23225          */
23226         "moved" : true,
23227         /**
23228          * @event beforeresize
23229          * Fires before the splitter is dragged
23230          * @param {Roo.SplitBar} this
23231          */
23232         "beforeresize" : true,
23233
23234         "beforeapply" : true
23235     });
23236
23237     Roo.util.Observable.call(this);
23238 };
23239
23240 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23241     onStartProxyDrag : function(x, y){
23242         this.fireEvent("beforeresize", this);
23243         if(!this.overlay){
23244             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23245             o.unselectable();
23246             o.enableDisplayMode("block");
23247             // all splitbars share the same overlay
23248             Roo.SplitBar.prototype.overlay = o;
23249         }
23250         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23251         this.overlay.show();
23252         Roo.get(this.proxy).setDisplayed("block");
23253         var size = this.adapter.getElementSize(this);
23254         this.activeMinSize = this.getMinimumSize();;
23255         this.activeMaxSize = this.getMaximumSize();;
23256         var c1 = size - this.activeMinSize;
23257         var c2 = Math.max(this.activeMaxSize - size, 0);
23258         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23259             this.dd.resetConstraints();
23260             this.dd.setXConstraint(
23261                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23262                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23263             );
23264             this.dd.setYConstraint(0, 0);
23265         }else{
23266             this.dd.resetConstraints();
23267             this.dd.setXConstraint(0, 0);
23268             this.dd.setYConstraint(
23269                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23270                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23271             );
23272          }
23273         this.dragSpecs.startSize = size;
23274         this.dragSpecs.startPoint = [x, y];
23275         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23276     },
23277     
23278     /** 
23279      * @private Called after the drag operation by the DDProxy
23280      */
23281     onEndProxyDrag : function(e){
23282         Roo.get(this.proxy).setDisplayed(false);
23283         var endPoint = Roo.lib.Event.getXY(e);
23284         if(this.overlay){
23285             this.overlay.hide();
23286         }
23287         var newSize;
23288         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23289             newSize = this.dragSpecs.startSize + 
23290                 (this.placement == Roo.SplitBar.LEFT ?
23291                     endPoint[0] - this.dragSpecs.startPoint[0] :
23292                     this.dragSpecs.startPoint[0] - endPoint[0]
23293                 );
23294         }else{
23295             newSize = this.dragSpecs.startSize + 
23296                 (this.placement == Roo.SplitBar.TOP ?
23297                     endPoint[1] - this.dragSpecs.startPoint[1] :
23298                     this.dragSpecs.startPoint[1] - endPoint[1]
23299                 );
23300         }
23301         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23302         if(newSize != this.dragSpecs.startSize){
23303             if(this.fireEvent('beforeapply', this, newSize) !== false){
23304                 this.adapter.setElementSize(this, newSize);
23305                 this.fireEvent("moved", this, newSize);
23306                 this.fireEvent("resize", this, newSize);
23307             }
23308         }
23309     },
23310     
23311     /**
23312      * Get the adapter this SplitBar uses
23313      * @return The adapter object
23314      */
23315     getAdapter : function(){
23316         return this.adapter;
23317     },
23318     
23319     /**
23320      * Set the adapter this SplitBar uses
23321      * @param {Object} adapter A SplitBar adapter object
23322      */
23323     setAdapter : function(adapter){
23324         this.adapter = adapter;
23325         this.adapter.init(this);
23326     },
23327     
23328     /**
23329      * Gets the minimum size for the resizing element
23330      * @return {Number} The minimum size
23331      */
23332     getMinimumSize : function(){
23333         return this.minSize;
23334     },
23335     
23336     /**
23337      * Sets the minimum size for the resizing element
23338      * @param {Number} minSize The minimum size
23339      */
23340     setMinimumSize : function(minSize){
23341         this.minSize = minSize;
23342     },
23343     
23344     /**
23345      * Gets the maximum size for the resizing element
23346      * @return {Number} The maximum size
23347      */
23348     getMaximumSize : function(){
23349         return this.maxSize;
23350     },
23351     
23352     /**
23353      * Sets the maximum size for the resizing element
23354      * @param {Number} maxSize The maximum size
23355      */
23356     setMaximumSize : function(maxSize){
23357         this.maxSize = maxSize;
23358     },
23359     
23360     /**
23361      * Sets the initialize size for the resizing element
23362      * @param {Number} size The initial size
23363      */
23364     setCurrentSize : function(size){
23365         var oldAnimate = this.animate;
23366         this.animate = false;
23367         this.adapter.setElementSize(this, size);
23368         this.animate = oldAnimate;
23369     },
23370     
23371     /**
23372      * Destroy this splitbar. 
23373      * @param {Boolean} removeEl True to remove the element
23374      */
23375     destroy : function(removeEl){
23376         if(this.shim){
23377             this.shim.remove();
23378         }
23379         this.dd.unreg();
23380         this.proxy.parentNode.removeChild(this.proxy);
23381         if(removeEl){
23382             this.el.remove();
23383         }
23384     }
23385 });
23386
23387 /**
23388  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23389  */
23390 Roo.SplitBar.createProxy = function(dir){
23391     var proxy = new Roo.Element(document.createElement("div"));
23392     proxy.unselectable();
23393     var cls = 'x-splitbar-proxy';
23394     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23395     document.body.appendChild(proxy.dom);
23396     return proxy.dom;
23397 };
23398
23399 /** 
23400  * @class Roo.SplitBar.BasicLayoutAdapter
23401  * Default Adapter. It assumes the splitter and resizing element are not positioned
23402  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23403  */
23404 Roo.SplitBar.BasicLayoutAdapter = function(){
23405 };
23406
23407 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23408     // do nothing for now
23409     init : function(s){
23410     
23411     },
23412     /**
23413      * Called before drag operations to get the current size of the resizing element. 
23414      * @param {Roo.SplitBar} s The SplitBar using this adapter
23415      */
23416      getElementSize : function(s){
23417         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23418             return s.resizingEl.getWidth();
23419         }else{
23420             return s.resizingEl.getHeight();
23421         }
23422     },
23423     
23424     /**
23425      * Called after drag operations to set the size of the resizing element.
23426      * @param {Roo.SplitBar} s The SplitBar using this adapter
23427      * @param {Number} newSize The new size to set
23428      * @param {Function} onComplete A function to be invoked when resizing is complete
23429      */
23430     setElementSize : function(s, newSize, onComplete){
23431         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23432             if(!s.animate){
23433                 s.resizingEl.setWidth(newSize);
23434                 if(onComplete){
23435                     onComplete(s, newSize);
23436                 }
23437             }else{
23438                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23439             }
23440         }else{
23441             
23442             if(!s.animate){
23443                 s.resizingEl.setHeight(newSize);
23444                 if(onComplete){
23445                     onComplete(s, newSize);
23446                 }
23447             }else{
23448                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23449             }
23450         }
23451     }
23452 };
23453
23454 /** 
23455  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23456  * @extends Roo.SplitBar.BasicLayoutAdapter
23457  * Adapter that  moves the splitter element to align with the resized sizing element. 
23458  * Used with an absolute positioned SplitBar.
23459  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23460  * document.body, make sure you assign an id to the body element.
23461  */
23462 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23463     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23464     this.container = Roo.get(container);
23465 };
23466
23467 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23468     init : function(s){
23469         this.basic.init(s);
23470     },
23471     
23472     getElementSize : function(s){
23473         return this.basic.getElementSize(s);
23474     },
23475     
23476     setElementSize : function(s, newSize, onComplete){
23477         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23478     },
23479     
23480     moveSplitter : function(s){
23481         var yes = Roo.SplitBar;
23482         switch(s.placement){
23483             case yes.LEFT:
23484                 s.el.setX(s.resizingEl.getRight());
23485                 break;
23486             case yes.RIGHT:
23487                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23488                 break;
23489             case yes.TOP:
23490                 s.el.setY(s.resizingEl.getBottom());
23491                 break;
23492             case yes.BOTTOM:
23493                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23494                 break;
23495         }
23496     }
23497 };
23498
23499 /**
23500  * Orientation constant - Create a vertical SplitBar
23501  * @static
23502  * @type Number
23503  */
23504 Roo.SplitBar.VERTICAL = 1;
23505
23506 /**
23507  * Orientation constant - Create a horizontal SplitBar
23508  * @static
23509  * @type Number
23510  */
23511 Roo.SplitBar.HORIZONTAL = 2;
23512
23513 /**
23514  * Placement constant - The resizing element is to the left of the splitter element
23515  * @static
23516  * @type Number
23517  */
23518 Roo.SplitBar.LEFT = 1;
23519
23520 /**
23521  * Placement constant - The resizing element is to the right of the splitter element
23522  * @static
23523  * @type Number
23524  */
23525 Roo.SplitBar.RIGHT = 2;
23526
23527 /**
23528  * Placement constant - The resizing element is positioned above the splitter element
23529  * @static
23530  * @type Number
23531  */
23532 Roo.SplitBar.TOP = 3;
23533
23534 /**
23535  * Placement constant - The resizing element is positioned under splitter element
23536  * @static
23537  * @type Number
23538  */
23539 Roo.SplitBar.BOTTOM = 4;
23540 /*
23541  * Based on:
23542  * Ext JS Library 1.1.1
23543  * Copyright(c) 2006-2007, Ext JS, LLC.
23544  *
23545  * Originally Released Under LGPL - original licence link has changed is not relivant.
23546  *
23547  * Fork - LGPL
23548  * <script type="text/javascript">
23549  */
23550
23551 /**
23552  * @class Roo.View
23553  * @extends Roo.util.Observable
23554  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23555  * This class also supports single and multi selection modes. <br>
23556  * Create a data model bound view:
23557  <pre><code>
23558  var store = new Roo.data.Store(...);
23559
23560  var view = new Roo.View({
23561     el : "my-element",
23562     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23563  
23564     singleSelect: true,
23565     selectedClass: "ydataview-selected",
23566     store: store
23567  });
23568
23569  // listen for node click?
23570  view.on("click", function(vw, index, node, e){
23571  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23572  });
23573
23574  // load XML data
23575  dataModel.load("foobar.xml");
23576  </code></pre>
23577  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23578  * <br><br>
23579  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23580  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23581  * 
23582  * Note: old style constructor is still suported (container, template, config)
23583  * 
23584  * @constructor
23585  * Create a new View
23586  * @param {Object} config The config object
23587  * 
23588  */
23589 Roo.View = function(config, depreciated_tpl, depreciated_config){
23590     
23591     if (typeof(depreciated_tpl) == 'undefined') {
23592         // new way.. - universal constructor.
23593         Roo.apply(this, config);
23594         this.el  = Roo.get(this.el);
23595     } else {
23596         // old format..
23597         this.el  = Roo.get(config);
23598         this.tpl = depreciated_tpl;
23599         Roo.apply(this, depreciated_config);
23600     }
23601      
23602     
23603     if(typeof(this.tpl) == "string"){
23604         this.tpl = new Roo.Template(this.tpl);
23605     } else {
23606         // support xtype ctors..
23607         this.tpl = new Roo.factory(this.tpl, Roo);
23608     }
23609     
23610     
23611     this.tpl.compile();
23612    
23613
23614      
23615     /** @private */
23616     this.addEvents({
23617     /**
23618      * @event beforeclick
23619      * Fires before a click is processed. Returns false to cancel the default action.
23620      * @param {Roo.View} this
23621      * @param {Number} index The index of the target node
23622      * @param {HTMLElement} node The target node
23623      * @param {Roo.EventObject} e The raw event object
23624      */
23625         "beforeclick" : true,
23626     /**
23627      * @event click
23628      * Fires when a template node is clicked.
23629      * @param {Roo.View} this
23630      * @param {Number} index The index of the target node
23631      * @param {HTMLElement} node The target node
23632      * @param {Roo.EventObject} e The raw event object
23633      */
23634         "click" : true,
23635     /**
23636      * @event dblclick
23637      * Fires when a template node is double clicked.
23638      * @param {Roo.View} this
23639      * @param {Number} index The index of the target node
23640      * @param {HTMLElement} node The target node
23641      * @param {Roo.EventObject} e The raw event object
23642      */
23643         "dblclick" : true,
23644     /**
23645      * @event contextmenu
23646      * Fires when a template node is right clicked.
23647      * @param {Roo.View} this
23648      * @param {Number} index The index of the target node
23649      * @param {HTMLElement} node The target node
23650      * @param {Roo.EventObject} e The raw event object
23651      */
23652         "contextmenu" : true,
23653     /**
23654      * @event selectionchange
23655      * Fires when the selected nodes change.
23656      * @param {Roo.View} this
23657      * @param {Array} selections Array of the selected nodes
23658      */
23659         "selectionchange" : true,
23660
23661     /**
23662      * @event beforeselect
23663      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23664      * @param {Roo.View} this
23665      * @param {HTMLElement} node The node to be selected
23666      * @param {Array} selections Array of currently selected nodes
23667      */
23668         "beforeselect" : true
23669     });
23670
23671     this.el.on({
23672         "click": this.onClick,
23673         "dblclick": this.onDblClick,
23674         "contextmenu": this.onContextMenu,
23675         scope:this
23676     });
23677
23678     this.selections = [];
23679     this.nodes = [];
23680     this.cmp = new Roo.CompositeElementLite([]);
23681     if(this.store){
23682         this.store = Roo.factory(this.store, Roo.data);
23683         this.setStore(this.store, true);
23684     }
23685     Roo.View.superclass.constructor.call(this);
23686 };
23687
23688 Roo.extend(Roo.View, Roo.util.Observable, {
23689     
23690      /**
23691      * @cfg {Roo.data.Store} store Data store to load data from.
23692      */
23693     store : false,
23694     
23695     /**
23696      * @cfg {String|Roo.Element} el The container element.
23697      */
23698     el : '',
23699     
23700     /**
23701      * @cfg {String|Roo.Template} tpl The template used by this View 
23702      */
23703     tpl : false,
23704     
23705     /**
23706      * @cfg {String} selectedClass The css class to add to selected nodes
23707      */
23708     selectedClass : "x-view-selected",
23709      /**
23710      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23711      */
23712     emptyText : "",
23713     /**
23714      * @cfg {Boolean} multiSelect Allow multiple selection
23715      */
23716     
23717     multiSelect : false,
23718     /**
23719      * @cfg {Boolean} singleSelect Allow single selection
23720      */
23721     singleSelect:  false,
23722     
23723     /**
23724      * Returns the element this view is bound to.
23725      * @return {Roo.Element}
23726      */
23727     getEl : function(){
23728         return this.el;
23729     },
23730
23731     /**
23732      * Refreshes the view.
23733      */
23734     refresh : function(){
23735         var t = this.tpl;
23736         this.clearSelections();
23737         this.el.update("");
23738         var html = [];
23739         var records = this.store.getRange();
23740         if(records.length < 1){
23741             this.el.update(this.emptyText);
23742             return;
23743         }
23744         for(var i = 0, len = records.length; i < len; i++){
23745             var data = this.prepareData(records[i].data, i, records[i]);
23746             html[html.length] = t.apply(data);
23747         }
23748         this.el.update(html.join(""));
23749         this.nodes = this.el.dom.childNodes;
23750         this.updateIndexes(0);
23751     },
23752
23753     /**
23754      * Function to override to reformat the data that is sent to
23755      * the template for each node.
23756      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23757      * a JSON object for an UpdateManager bound view).
23758      */
23759     prepareData : function(data){
23760         return data;
23761     },
23762
23763     onUpdate : function(ds, record){
23764         this.clearSelections();
23765         var index = this.store.indexOf(record);
23766         var n = this.nodes[index];
23767         this.tpl.insertBefore(n, this.prepareData(record.data));
23768         n.parentNode.removeChild(n);
23769         this.updateIndexes(index, index);
23770     },
23771
23772     onAdd : function(ds, records, index){
23773         this.clearSelections();
23774         if(this.nodes.length == 0){
23775             this.refresh();
23776             return;
23777         }
23778         var n = this.nodes[index];
23779         for(var i = 0, len = records.length; i < len; i++){
23780             var d = this.prepareData(records[i].data);
23781             if(n){
23782                 this.tpl.insertBefore(n, d);
23783             }else{
23784                 this.tpl.append(this.el, d);
23785             }
23786         }
23787         this.updateIndexes(index);
23788     },
23789
23790     onRemove : function(ds, record, index){
23791         this.clearSelections();
23792         this.el.dom.removeChild(this.nodes[index]);
23793         this.updateIndexes(index);
23794     },
23795
23796     /**
23797      * Refresh an individual node.
23798      * @param {Number} index
23799      */
23800     refreshNode : function(index){
23801         this.onUpdate(this.store, this.store.getAt(index));
23802     },
23803
23804     updateIndexes : function(startIndex, endIndex){
23805         var ns = this.nodes;
23806         startIndex = startIndex || 0;
23807         endIndex = endIndex || ns.length - 1;
23808         for(var i = startIndex; i <= endIndex; i++){
23809             ns[i].nodeIndex = i;
23810         }
23811     },
23812
23813     /**
23814      * Changes the data store this view uses and refresh the view.
23815      * @param {Store} store
23816      */
23817     setStore : function(store, initial){
23818         if(!initial && this.store){
23819             this.store.un("datachanged", this.refresh);
23820             this.store.un("add", this.onAdd);
23821             this.store.un("remove", this.onRemove);
23822             this.store.un("update", this.onUpdate);
23823             this.store.un("clear", this.refresh);
23824         }
23825         if(store){
23826           
23827             store.on("datachanged", this.refresh, this);
23828             store.on("add", this.onAdd, this);
23829             store.on("remove", this.onRemove, this);
23830             store.on("update", this.onUpdate, this);
23831             store.on("clear", this.refresh, this);
23832         }
23833         
23834         if(store){
23835             this.refresh();
23836         }
23837     },
23838
23839     /**
23840      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23841      * @param {HTMLElement} node
23842      * @return {HTMLElement} The template node
23843      */
23844     findItemFromChild : function(node){
23845         var el = this.el.dom;
23846         if(!node || node.parentNode == el){
23847                     return node;
23848             }
23849             var p = node.parentNode;
23850             while(p && p != el){
23851             if(p.parentNode == el){
23852                 return p;
23853             }
23854             p = p.parentNode;
23855         }
23856             return null;
23857     },
23858
23859     /** @ignore */
23860     onClick : function(e){
23861         var item = this.findItemFromChild(e.getTarget());
23862         if(item){
23863             var index = this.indexOf(item);
23864             if(this.onItemClick(item, index, e) !== false){
23865                 this.fireEvent("click", this, index, item, e);
23866             }
23867         }else{
23868             this.clearSelections();
23869         }
23870     },
23871
23872     /** @ignore */
23873     onContextMenu : function(e){
23874         var item = this.findItemFromChild(e.getTarget());
23875         if(item){
23876             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23877         }
23878     },
23879
23880     /** @ignore */
23881     onDblClick : function(e){
23882         var item = this.findItemFromChild(e.getTarget());
23883         if(item){
23884             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23885         }
23886     },
23887
23888     onItemClick : function(item, index, e){
23889         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23890             return false;
23891         }
23892         if(this.multiSelect || this.singleSelect){
23893             if(this.multiSelect && e.shiftKey && this.lastSelection){
23894                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23895             }else{
23896                 this.select(item, this.multiSelect && e.ctrlKey);
23897                 this.lastSelection = item;
23898             }
23899             e.preventDefault();
23900         }
23901         return true;
23902     },
23903
23904     /**
23905      * Get the number of selected nodes.
23906      * @return {Number}
23907      */
23908     getSelectionCount : function(){
23909         return this.selections.length;
23910     },
23911
23912     /**
23913      * Get the currently selected nodes.
23914      * @return {Array} An array of HTMLElements
23915      */
23916     getSelectedNodes : function(){
23917         return this.selections;
23918     },
23919
23920     /**
23921      * Get the indexes of the selected nodes.
23922      * @return {Array}
23923      */
23924     getSelectedIndexes : function(){
23925         var indexes = [], s = this.selections;
23926         for(var i = 0, len = s.length; i < len; i++){
23927             indexes.push(s[i].nodeIndex);
23928         }
23929         return indexes;
23930     },
23931
23932     /**
23933      * Clear all selections
23934      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23935      */
23936     clearSelections : function(suppressEvent){
23937         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23938             this.cmp.elements = this.selections;
23939             this.cmp.removeClass(this.selectedClass);
23940             this.selections = [];
23941             if(!suppressEvent){
23942                 this.fireEvent("selectionchange", this, this.selections);
23943             }
23944         }
23945     },
23946
23947     /**
23948      * Returns true if the passed node is selected
23949      * @param {HTMLElement/Number} node The node or node index
23950      * @return {Boolean}
23951      */
23952     isSelected : function(node){
23953         var s = this.selections;
23954         if(s.length < 1){
23955             return false;
23956         }
23957         node = this.getNode(node);
23958         return s.indexOf(node) !== -1;
23959     },
23960
23961     /**
23962      * Selects nodes.
23963      * @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
23964      * @param {Boolean} keepExisting (optional) true to keep existing selections
23965      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23966      */
23967     select : function(nodeInfo, keepExisting, suppressEvent){
23968         if(nodeInfo instanceof Array){
23969             if(!keepExisting){
23970                 this.clearSelections(true);
23971             }
23972             for(var i = 0, len = nodeInfo.length; i < len; i++){
23973                 this.select(nodeInfo[i], true, true);
23974             }
23975         } else{
23976             var node = this.getNode(nodeInfo);
23977             if(node && !this.isSelected(node)){
23978                 if(!keepExisting){
23979                     this.clearSelections(true);
23980                 }
23981                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23982                     Roo.fly(node).addClass(this.selectedClass);
23983                     this.selections.push(node);
23984                     if(!suppressEvent){
23985                         this.fireEvent("selectionchange", this, this.selections);
23986                     }
23987                 }
23988             }
23989         }
23990     },
23991
23992     /**
23993      * Gets a template node.
23994      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23995      * @return {HTMLElement} The node or null if it wasn't found
23996      */
23997     getNode : function(nodeInfo){
23998         if(typeof nodeInfo == "string"){
23999             return document.getElementById(nodeInfo);
24000         }else if(typeof nodeInfo == "number"){
24001             return this.nodes[nodeInfo];
24002         }
24003         return nodeInfo;
24004     },
24005
24006     /**
24007      * Gets a range template nodes.
24008      * @param {Number} startIndex
24009      * @param {Number} endIndex
24010      * @return {Array} An array of nodes
24011      */
24012     getNodes : function(start, end){
24013         var ns = this.nodes;
24014         start = start || 0;
24015         end = typeof end == "undefined" ? ns.length - 1 : end;
24016         var nodes = [];
24017         if(start <= end){
24018             for(var i = start; i <= end; i++){
24019                 nodes.push(ns[i]);
24020             }
24021         } else{
24022             for(var i = start; i >= end; i--){
24023                 nodes.push(ns[i]);
24024             }
24025         }
24026         return nodes;
24027     },
24028
24029     /**
24030      * Finds the index of the passed node
24031      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24032      * @return {Number} The index of the node or -1
24033      */
24034     indexOf : function(node){
24035         node = this.getNode(node);
24036         if(typeof node.nodeIndex == "number"){
24037             return node.nodeIndex;
24038         }
24039         var ns = this.nodes;
24040         for(var i = 0, len = ns.length; i < len; i++){
24041             if(ns[i] == node){
24042                 return i;
24043             }
24044         }
24045         return -1;
24046     }
24047 });
24048 /*
24049  * Based on:
24050  * Ext JS Library 1.1.1
24051  * Copyright(c) 2006-2007, Ext JS, LLC.
24052  *
24053  * Originally Released Under LGPL - original licence link has changed is not relivant.
24054  *
24055  * Fork - LGPL
24056  * <script type="text/javascript">
24057  */
24058
24059 /**
24060  * @class Roo.JsonView
24061  * @extends Roo.View
24062  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24063 <pre><code>
24064 var view = new Roo.JsonView({
24065     container: "my-element",
24066     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24067     multiSelect: true, 
24068     jsonRoot: "data" 
24069 });
24070
24071 // listen for node click?
24072 view.on("click", function(vw, index, node, e){
24073     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24074 });
24075
24076 // direct load of JSON data
24077 view.load("foobar.php");
24078
24079 // Example from my blog list
24080 var tpl = new Roo.Template(
24081     '&lt;div class="entry"&gt;' +
24082     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24083     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24084     "&lt;/div&gt;&lt;hr /&gt;"
24085 );
24086
24087 var moreView = new Roo.JsonView({
24088     container :  "entry-list", 
24089     template : tpl,
24090     jsonRoot: "posts"
24091 });
24092 moreView.on("beforerender", this.sortEntries, this);
24093 moreView.load({
24094     url: "/blog/get-posts.php",
24095     params: "allposts=true",
24096     text: "Loading Blog Entries..."
24097 });
24098 </code></pre>
24099
24100 * Note: old code is supported with arguments : (container, template, config)
24101
24102
24103  * @constructor
24104  * Create a new JsonView
24105  * 
24106  * @param {Object} config The config object
24107  * 
24108  */
24109 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24110     
24111     
24112     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24113
24114     var um = this.el.getUpdateManager();
24115     um.setRenderer(this);
24116     um.on("update", this.onLoad, this);
24117     um.on("failure", this.onLoadException, this);
24118
24119     /**
24120      * @event beforerender
24121      * Fires before rendering of the downloaded JSON data.
24122      * @param {Roo.JsonView} this
24123      * @param {Object} data The JSON data loaded
24124      */
24125     /**
24126      * @event load
24127      * Fires when data is loaded.
24128      * @param {Roo.JsonView} this
24129      * @param {Object} data The JSON data loaded
24130      * @param {Object} response The raw Connect response object
24131      */
24132     /**
24133      * @event loadexception
24134      * Fires when loading fails.
24135      * @param {Roo.JsonView} this
24136      * @param {Object} response The raw Connect response object
24137      */
24138     this.addEvents({
24139         'beforerender' : true,
24140         'load' : true,
24141         'loadexception' : true
24142     });
24143 };
24144 Roo.extend(Roo.JsonView, Roo.View, {
24145     /**
24146      * @type {String} The root property in the loaded JSON object that contains the data
24147      */
24148     jsonRoot : "",
24149
24150     /**
24151      * Refreshes the view.
24152      */
24153     refresh : function(){
24154         this.clearSelections();
24155         this.el.update("");
24156         var html = [];
24157         var o = this.jsonData;
24158         if(o && o.length > 0){
24159             for(var i = 0, len = o.length; i < len; i++){
24160                 var data = this.prepareData(o[i], i, o);
24161                 html[html.length] = this.tpl.apply(data);
24162             }
24163         }else{
24164             html.push(this.emptyText);
24165         }
24166         this.el.update(html.join(""));
24167         this.nodes = this.el.dom.childNodes;
24168         this.updateIndexes(0);
24169     },
24170
24171     /**
24172      * 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.
24173      * @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:
24174      <pre><code>
24175      view.load({
24176          url: "your-url.php",
24177          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24178          callback: yourFunction,
24179          scope: yourObject, //(optional scope)
24180          discardUrl: false,
24181          nocache: false,
24182          text: "Loading...",
24183          timeout: 30,
24184          scripts: false
24185      });
24186      </code></pre>
24187      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24188      * 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.
24189      * @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}
24190      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24191      * @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.
24192      */
24193     load : function(){
24194         var um = this.el.getUpdateManager();
24195         um.update.apply(um, arguments);
24196     },
24197
24198     render : function(el, response){
24199         this.clearSelections();
24200         this.el.update("");
24201         var o;
24202         try{
24203             o = Roo.util.JSON.decode(response.responseText);
24204             if(this.jsonRoot){
24205                 
24206                 o = o[this.jsonRoot];
24207             }
24208         } catch(e){
24209         }
24210         /**
24211          * The current JSON data or null
24212          */
24213         this.jsonData = o;
24214         this.beforeRender();
24215         this.refresh();
24216     },
24217
24218 /**
24219  * Get the number of records in the current JSON dataset
24220  * @return {Number}
24221  */
24222     getCount : function(){
24223         return this.jsonData ? this.jsonData.length : 0;
24224     },
24225
24226 /**
24227  * Returns the JSON object for the specified node(s)
24228  * @param {HTMLElement/Array} node The node or an array of nodes
24229  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24230  * you get the JSON object for the node
24231  */
24232     getNodeData : function(node){
24233         if(node instanceof Array){
24234             var data = [];
24235             for(var i = 0, len = node.length; i < len; i++){
24236                 data.push(this.getNodeData(node[i]));
24237             }
24238             return data;
24239         }
24240         return this.jsonData[this.indexOf(node)] || null;
24241     },
24242
24243     beforeRender : function(){
24244         this.snapshot = this.jsonData;
24245         if(this.sortInfo){
24246             this.sort.apply(this, this.sortInfo);
24247         }
24248         this.fireEvent("beforerender", this, this.jsonData);
24249     },
24250
24251     onLoad : function(el, o){
24252         this.fireEvent("load", this, this.jsonData, o);
24253     },
24254
24255     onLoadException : function(el, o){
24256         this.fireEvent("loadexception", this, o);
24257     },
24258
24259 /**
24260  * Filter the data by a specific property.
24261  * @param {String} property A property on your JSON objects
24262  * @param {String/RegExp} value Either string that the property values
24263  * should start with, or a RegExp to test against the property
24264  */
24265     filter : function(property, value){
24266         if(this.jsonData){
24267             var data = [];
24268             var ss = this.snapshot;
24269             if(typeof value == "string"){
24270                 var vlen = value.length;
24271                 if(vlen == 0){
24272                     this.clearFilter();
24273                     return;
24274                 }
24275                 value = value.toLowerCase();
24276                 for(var i = 0, len = ss.length; i < len; i++){
24277                     var o = ss[i];
24278                     if(o[property].substr(0, vlen).toLowerCase() == value){
24279                         data.push(o);
24280                     }
24281                 }
24282             } else if(value.exec){ // regex?
24283                 for(var i = 0, len = ss.length; i < len; i++){
24284                     var o = ss[i];
24285                     if(value.test(o[property])){
24286                         data.push(o);
24287                     }
24288                 }
24289             } else{
24290                 return;
24291             }
24292             this.jsonData = data;
24293             this.refresh();
24294         }
24295     },
24296
24297 /**
24298  * Filter by a function. The passed function will be called with each
24299  * object in the current dataset. If the function returns true the value is kept,
24300  * otherwise it is filtered.
24301  * @param {Function} fn
24302  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24303  */
24304     filterBy : function(fn, scope){
24305         if(this.jsonData){
24306             var data = [];
24307             var ss = this.snapshot;
24308             for(var i = 0, len = ss.length; i < len; i++){
24309                 var o = ss[i];
24310                 if(fn.call(scope || this, o)){
24311                     data.push(o);
24312                 }
24313             }
24314             this.jsonData = data;
24315             this.refresh();
24316         }
24317     },
24318
24319 /**
24320  * Clears the current filter.
24321  */
24322     clearFilter : function(){
24323         if(this.snapshot && this.jsonData != this.snapshot){
24324             this.jsonData = this.snapshot;
24325             this.refresh();
24326         }
24327     },
24328
24329
24330 /**
24331  * Sorts the data for this view and refreshes it.
24332  * @param {String} property A property on your JSON objects to sort on
24333  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24334  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24335  */
24336     sort : function(property, dir, sortType){
24337         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24338         if(this.jsonData){
24339             var p = property;
24340             var dsc = dir && dir.toLowerCase() == "desc";
24341             var f = function(o1, o2){
24342                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24343                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24344                 ;
24345                 if(v1 < v2){
24346                     return dsc ? +1 : -1;
24347                 } else if(v1 > v2){
24348                     return dsc ? -1 : +1;
24349                 } else{
24350                     return 0;
24351                 }
24352             };
24353             this.jsonData.sort(f);
24354             this.refresh();
24355             if(this.jsonData != this.snapshot){
24356                 this.snapshot.sort(f);
24357             }
24358         }
24359     }
24360 });/*
24361  * Based on:
24362  * Ext JS Library 1.1.1
24363  * Copyright(c) 2006-2007, Ext JS, LLC.
24364  *
24365  * Originally Released Under LGPL - original licence link has changed is not relivant.
24366  *
24367  * Fork - LGPL
24368  * <script type="text/javascript">
24369  */
24370  
24371
24372 /**
24373  * @class Roo.ColorPalette
24374  * @extends Roo.Component
24375  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24376  * Here's an example of typical usage:
24377  * <pre><code>
24378 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24379 cp.render('my-div');
24380
24381 cp.on('select', function(palette, selColor){
24382     // do something with selColor
24383 });
24384 </code></pre>
24385  * @constructor
24386  * Create a new ColorPalette
24387  * @param {Object} config The config object
24388  */
24389 Roo.ColorPalette = function(config){
24390     Roo.ColorPalette.superclass.constructor.call(this, config);
24391     this.addEvents({
24392         /**
24393              * @event select
24394              * Fires when a color is selected
24395              * @param {ColorPalette} this
24396              * @param {String} color The 6-digit color hex code (without the # symbol)
24397              */
24398         select: true
24399     });
24400
24401     if(this.handler){
24402         this.on("select", this.handler, this.scope, true);
24403     }
24404 };
24405 Roo.extend(Roo.ColorPalette, Roo.Component, {
24406     /**
24407      * @cfg {String} itemCls
24408      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24409      */
24410     itemCls : "x-color-palette",
24411     /**
24412      * @cfg {String} value
24413      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24414      * the hex codes are case-sensitive.
24415      */
24416     value : null,
24417     clickEvent:'click',
24418     // private
24419     ctype: "Roo.ColorPalette",
24420
24421     /**
24422      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24423      */
24424     allowReselect : false,
24425
24426     /**
24427      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24428      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24429      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24430      * of colors with the width setting until the box is symmetrical.</p>
24431      * <p>You can override individual colors if needed:</p>
24432      * <pre><code>
24433 var cp = new Roo.ColorPalette();
24434 cp.colors[0] = "FF0000";  // change the first box to red
24435 </code></pre>
24436
24437 Or you can provide a custom array of your own for complete control:
24438 <pre><code>
24439 var cp = new Roo.ColorPalette();
24440 cp.colors = ["000000", "993300", "333300"];
24441 </code></pre>
24442      * @type Array
24443      */
24444     colors : [
24445         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24446         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24447         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24448         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24449         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24450     ],
24451
24452     // private
24453     onRender : function(container, position){
24454         var t = new Roo.MasterTemplate(
24455             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24456         );
24457         var c = this.colors;
24458         for(var i = 0, len = c.length; i < len; i++){
24459             t.add([c[i]]);
24460         }
24461         var el = document.createElement("div");
24462         el.className = this.itemCls;
24463         t.overwrite(el);
24464         container.dom.insertBefore(el, position);
24465         this.el = Roo.get(el);
24466         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24467         if(this.clickEvent != 'click'){
24468             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24469         }
24470     },
24471
24472     // private
24473     afterRender : function(){
24474         Roo.ColorPalette.superclass.afterRender.call(this);
24475         if(this.value){
24476             var s = this.value;
24477             this.value = null;
24478             this.select(s);
24479         }
24480     },
24481
24482     // private
24483     handleClick : function(e, t){
24484         e.preventDefault();
24485         if(!this.disabled){
24486             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24487             this.select(c.toUpperCase());
24488         }
24489     },
24490
24491     /**
24492      * Selects the specified color in the palette (fires the select event)
24493      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24494      */
24495     select : function(color){
24496         color = color.replace("#", "");
24497         if(color != this.value || this.allowReselect){
24498             var el = this.el;
24499             if(this.value){
24500                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24501             }
24502             el.child("a.color-"+color).addClass("x-color-palette-sel");
24503             this.value = color;
24504             this.fireEvent("select", this, color);
24505         }
24506     }
24507 });/*
24508  * Based on:
24509  * Ext JS Library 1.1.1
24510  * Copyright(c) 2006-2007, Ext JS, LLC.
24511  *
24512  * Originally Released Under LGPL - original licence link has changed is not relivant.
24513  *
24514  * Fork - LGPL
24515  * <script type="text/javascript">
24516  */
24517  
24518 /**
24519  * @class Roo.DatePicker
24520  * @extends Roo.Component
24521  * Simple date picker class.
24522  * @constructor
24523  * Create a new DatePicker
24524  * @param {Object} config The config object
24525  */
24526 Roo.DatePicker = function(config){
24527     Roo.DatePicker.superclass.constructor.call(this, config);
24528
24529     this.value = config && config.value ?
24530                  config.value.clearTime() : new Date().clearTime();
24531
24532     this.addEvents({
24533         /**
24534              * @event select
24535              * Fires when a date is selected
24536              * @param {DatePicker} this
24537              * @param {Date} date The selected date
24538              */
24539         select: true
24540     });
24541
24542     if(this.handler){
24543         this.on("select", this.handler,  this.scope || this);
24544     }
24545     // build the disabledDatesRE
24546     if(!this.disabledDatesRE && this.disabledDates){
24547         var dd = this.disabledDates;
24548         var re = "(?:";
24549         for(var i = 0; i < dd.length; i++){
24550             re += dd[i];
24551             if(i != dd.length-1) re += "|";
24552         }
24553         this.disabledDatesRE = new RegExp(re + ")");
24554     }
24555 };
24556
24557 Roo.extend(Roo.DatePicker, Roo.Component, {
24558     /**
24559      * @cfg {String} todayText
24560      * The text to display on the button that selects the current date (defaults to "Today")
24561      */
24562     todayText : "Today",
24563     /**
24564      * @cfg {String} okText
24565      * The text to display on the ok button
24566      */
24567     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24568     /**
24569      * @cfg {String} cancelText
24570      * The text to display on the cancel button
24571      */
24572     cancelText : "Cancel",
24573     /**
24574      * @cfg {String} todayTip
24575      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24576      */
24577     todayTip : "{0} (Spacebar)",
24578     /**
24579      * @cfg {Date} minDate
24580      * Minimum allowable date (JavaScript date object, defaults to null)
24581      */
24582     minDate : null,
24583     /**
24584      * @cfg {Date} maxDate
24585      * Maximum allowable date (JavaScript date object, defaults to null)
24586      */
24587     maxDate : null,
24588     /**
24589      * @cfg {String} minText
24590      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24591      */
24592     minText : "This date is before the minimum date",
24593     /**
24594      * @cfg {String} maxText
24595      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24596      */
24597     maxText : "This date is after the maximum date",
24598     /**
24599      * @cfg {String} format
24600      * The default date format string which can be overriden for localization support.  The format must be
24601      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24602      */
24603     format : "m/d/y",
24604     /**
24605      * @cfg {Array} disabledDays
24606      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24607      */
24608     disabledDays : null,
24609     /**
24610      * @cfg {String} disabledDaysText
24611      * The tooltip to display when the date falls on a disabled day (defaults to "")
24612      */
24613     disabledDaysText : "",
24614     /**
24615      * @cfg {RegExp} disabledDatesRE
24616      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24617      */
24618     disabledDatesRE : null,
24619     /**
24620      * @cfg {String} disabledDatesText
24621      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24622      */
24623     disabledDatesText : "",
24624     /**
24625      * @cfg {Boolean} constrainToViewport
24626      * True to constrain the date picker to the viewport (defaults to true)
24627      */
24628     constrainToViewport : true,
24629     /**
24630      * @cfg {Array} monthNames
24631      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24632      */
24633     monthNames : Date.monthNames,
24634     /**
24635      * @cfg {Array} dayNames
24636      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24637      */
24638     dayNames : Date.dayNames,
24639     /**
24640      * @cfg {String} nextText
24641      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24642      */
24643     nextText: 'Next Month (Control+Right)',
24644     /**
24645      * @cfg {String} prevText
24646      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24647      */
24648     prevText: 'Previous Month (Control+Left)',
24649     /**
24650      * @cfg {String} monthYearText
24651      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24652      */
24653     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24654     /**
24655      * @cfg {Number} startDay
24656      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24657      */
24658     startDay : 0,
24659     /**
24660      * @cfg {Bool} showClear
24661      * Show a clear button (usefull for date form elements that can be blank.)
24662      */
24663     
24664     showClear: false,
24665     
24666     /**
24667      * Sets the value of the date field
24668      * @param {Date} value The date to set
24669      */
24670     setValue : function(value){
24671         var old = this.value;
24672         this.value = value.clearTime(true);
24673         if(this.el){
24674             this.update(this.value);
24675         }
24676     },
24677
24678     /**
24679      * Gets the current selected value of the date field
24680      * @return {Date} The selected date
24681      */
24682     getValue : function(){
24683         return this.value;
24684     },
24685
24686     // private
24687     focus : function(){
24688         if(this.el){
24689             this.update(this.activeDate);
24690         }
24691     },
24692
24693     // private
24694     onRender : function(container, position){
24695         var m = [
24696              '<table cellspacing="0">',
24697                 '<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>',
24698                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24699         var dn = this.dayNames;
24700         for(var i = 0; i < 7; i++){
24701             var d = this.startDay+i;
24702             if(d > 6){
24703                 d = d-7;
24704             }
24705             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24706         }
24707         m[m.length] = "</tr></thead><tbody><tr>";
24708         for(var i = 0; i < 42; i++) {
24709             if(i % 7 == 0 && i != 0){
24710                 m[m.length] = "</tr><tr>";
24711             }
24712             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24713         }
24714         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24715             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24716
24717         var el = document.createElement("div");
24718         el.className = "x-date-picker";
24719         el.innerHTML = m.join("");
24720
24721         container.dom.insertBefore(el, position);
24722
24723         this.el = Roo.get(el);
24724         this.eventEl = Roo.get(el.firstChild);
24725
24726         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24727             handler: this.showPrevMonth,
24728             scope: this,
24729             preventDefault:true,
24730             stopDefault:true
24731         });
24732
24733         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24734             handler: this.showNextMonth,
24735             scope: this,
24736             preventDefault:true,
24737             stopDefault:true
24738         });
24739
24740         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24741
24742         this.monthPicker = this.el.down('div.x-date-mp');
24743         this.monthPicker.enableDisplayMode('block');
24744         
24745         var kn = new Roo.KeyNav(this.eventEl, {
24746             "left" : function(e){
24747                 e.ctrlKey ?
24748                     this.showPrevMonth() :
24749                     this.update(this.activeDate.add("d", -1));
24750             },
24751
24752             "right" : function(e){
24753                 e.ctrlKey ?
24754                     this.showNextMonth() :
24755                     this.update(this.activeDate.add("d", 1));
24756             },
24757
24758             "up" : function(e){
24759                 e.ctrlKey ?
24760                     this.showNextYear() :
24761                     this.update(this.activeDate.add("d", -7));
24762             },
24763
24764             "down" : function(e){
24765                 e.ctrlKey ?
24766                     this.showPrevYear() :
24767                     this.update(this.activeDate.add("d", 7));
24768             },
24769
24770             "pageUp" : function(e){
24771                 this.showNextMonth();
24772             },
24773
24774             "pageDown" : function(e){
24775                 this.showPrevMonth();
24776             },
24777
24778             "enter" : function(e){
24779                 e.stopPropagation();
24780                 return true;
24781             },
24782
24783             scope : this
24784         });
24785
24786         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24787
24788         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24789
24790         this.el.unselectable();
24791         
24792         this.cells = this.el.select("table.x-date-inner tbody td");
24793         this.textNodes = this.el.query("table.x-date-inner tbody span");
24794
24795         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24796             text: "&#160;",
24797             tooltip: this.monthYearText
24798         });
24799
24800         this.mbtn.on('click', this.showMonthPicker, this);
24801         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24802
24803
24804         var today = (new Date()).dateFormat(this.format);
24805         
24806         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24807         if (this.showClear) {
24808             baseTb.add( new Roo.Toolbar.Fill());
24809         }
24810         baseTb.add({
24811             text: String.format(this.todayText, today),
24812             tooltip: String.format(this.todayTip, today),
24813             handler: this.selectToday,
24814             scope: this
24815         });
24816         
24817         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24818             
24819         //});
24820         if (this.showClear) {
24821             
24822             baseTb.add( new Roo.Toolbar.Fill());
24823             baseTb.add({
24824                 text: '&#160;',
24825                 cls: 'x-btn-icon x-btn-clear',
24826                 handler: function() {
24827                     //this.value = '';
24828                     this.fireEvent("select", this, '');
24829                 },
24830                 scope: this
24831             });
24832         }
24833         
24834         
24835         if(Roo.isIE){
24836             this.el.repaint();
24837         }
24838         this.update(this.value);
24839     },
24840
24841     createMonthPicker : function(){
24842         if(!this.monthPicker.dom.firstChild){
24843             var buf = ['<table border="0" cellspacing="0">'];
24844             for(var i = 0; i < 6; i++){
24845                 buf.push(
24846                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24847                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24848                     i == 0 ?
24849                     '<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>' :
24850                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24851                 );
24852             }
24853             buf.push(
24854                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24855                     this.okText,
24856                     '</button><button type="button" class="x-date-mp-cancel">',
24857                     this.cancelText,
24858                     '</button></td></tr>',
24859                 '</table>'
24860             );
24861             this.monthPicker.update(buf.join(''));
24862             this.monthPicker.on('click', this.onMonthClick, this);
24863             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24864
24865             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24866             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24867
24868             this.mpMonths.each(function(m, a, i){
24869                 i += 1;
24870                 if((i%2) == 0){
24871                     m.dom.xmonth = 5 + Math.round(i * .5);
24872                 }else{
24873                     m.dom.xmonth = Math.round((i-1) * .5);
24874                 }
24875             });
24876         }
24877     },
24878
24879     showMonthPicker : function(){
24880         this.createMonthPicker();
24881         var size = this.el.getSize();
24882         this.monthPicker.setSize(size);
24883         this.monthPicker.child('table').setSize(size);
24884
24885         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24886         this.updateMPMonth(this.mpSelMonth);
24887         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24888         this.updateMPYear(this.mpSelYear);
24889
24890         this.monthPicker.slideIn('t', {duration:.2});
24891     },
24892
24893     updateMPYear : function(y){
24894         this.mpyear = y;
24895         var ys = this.mpYears.elements;
24896         for(var i = 1; i <= 10; i++){
24897             var td = ys[i-1], y2;
24898             if((i%2) == 0){
24899                 y2 = y + Math.round(i * .5);
24900                 td.firstChild.innerHTML = y2;
24901                 td.xyear = y2;
24902             }else{
24903                 y2 = y - (5-Math.round(i * .5));
24904                 td.firstChild.innerHTML = y2;
24905                 td.xyear = y2;
24906             }
24907             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24908         }
24909     },
24910
24911     updateMPMonth : function(sm){
24912         this.mpMonths.each(function(m, a, i){
24913             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24914         });
24915     },
24916
24917     selectMPMonth: function(m){
24918         
24919     },
24920
24921     onMonthClick : function(e, t){
24922         e.stopEvent();
24923         var el = new Roo.Element(t), pn;
24924         if(el.is('button.x-date-mp-cancel')){
24925             this.hideMonthPicker();
24926         }
24927         else if(el.is('button.x-date-mp-ok')){
24928             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24929             this.hideMonthPicker();
24930         }
24931         else if(pn = el.up('td.x-date-mp-month', 2)){
24932             this.mpMonths.removeClass('x-date-mp-sel');
24933             pn.addClass('x-date-mp-sel');
24934             this.mpSelMonth = pn.dom.xmonth;
24935         }
24936         else if(pn = el.up('td.x-date-mp-year', 2)){
24937             this.mpYears.removeClass('x-date-mp-sel');
24938             pn.addClass('x-date-mp-sel');
24939             this.mpSelYear = pn.dom.xyear;
24940         }
24941         else if(el.is('a.x-date-mp-prev')){
24942             this.updateMPYear(this.mpyear-10);
24943         }
24944         else if(el.is('a.x-date-mp-next')){
24945             this.updateMPYear(this.mpyear+10);
24946         }
24947     },
24948
24949     onMonthDblClick : function(e, t){
24950         e.stopEvent();
24951         var el = new Roo.Element(t), pn;
24952         if(pn = el.up('td.x-date-mp-month', 2)){
24953             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24954             this.hideMonthPicker();
24955         }
24956         else if(pn = el.up('td.x-date-mp-year', 2)){
24957             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24958             this.hideMonthPicker();
24959         }
24960     },
24961
24962     hideMonthPicker : function(disableAnim){
24963         if(this.monthPicker){
24964             if(disableAnim === true){
24965                 this.monthPicker.hide();
24966             }else{
24967                 this.monthPicker.slideOut('t', {duration:.2});
24968             }
24969         }
24970     },
24971
24972     // private
24973     showPrevMonth : function(e){
24974         this.update(this.activeDate.add("mo", -1));
24975     },
24976
24977     // private
24978     showNextMonth : function(e){
24979         this.update(this.activeDate.add("mo", 1));
24980     },
24981
24982     // private
24983     showPrevYear : function(){
24984         this.update(this.activeDate.add("y", -1));
24985     },
24986
24987     // private
24988     showNextYear : function(){
24989         this.update(this.activeDate.add("y", 1));
24990     },
24991
24992     // private
24993     handleMouseWheel : function(e){
24994         var delta = e.getWheelDelta();
24995         if(delta > 0){
24996             this.showPrevMonth();
24997             e.stopEvent();
24998         } else if(delta < 0){
24999             this.showNextMonth();
25000             e.stopEvent();
25001         }
25002     },
25003
25004     // private
25005     handleDateClick : function(e, t){
25006         e.stopEvent();
25007         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25008             this.setValue(new Date(t.dateValue));
25009             this.fireEvent("select", this, this.value);
25010         }
25011     },
25012
25013     // private
25014     selectToday : function(){
25015         this.setValue(new Date().clearTime());
25016         this.fireEvent("select", this, this.value);
25017     },
25018
25019     // private
25020     update : function(date){
25021         var vd = this.activeDate;
25022         this.activeDate = date;
25023         if(vd && this.el){
25024             var t = date.getTime();
25025             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25026                 this.cells.removeClass("x-date-selected");
25027                 this.cells.each(function(c){
25028                    if(c.dom.firstChild.dateValue == t){
25029                        c.addClass("x-date-selected");
25030                        setTimeout(function(){
25031                             try{c.dom.firstChild.focus();}catch(e){}
25032                        }, 50);
25033                        return false;
25034                    }
25035                 });
25036                 return;
25037             }
25038         }
25039         var days = date.getDaysInMonth();
25040         var firstOfMonth = date.getFirstDateOfMonth();
25041         var startingPos = firstOfMonth.getDay()-this.startDay;
25042
25043         if(startingPos <= this.startDay){
25044             startingPos += 7;
25045         }
25046
25047         var pm = date.add("mo", -1);
25048         var prevStart = pm.getDaysInMonth()-startingPos;
25049
25050         var cells = this.cells.elements;
25051         var textEls = this.textNodes;
25052         days += startingPos;
25053
25054         // convert everything to numbers so it's fast
25055         var day = 86400000;
25056         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25057         var today = new Date().clearTime().getTime();
25058         var sel = date.clearTime().getTime();
25059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25061         var ddMatch = this.disabledDatesRE;
25062         var ddText = this.disabledDatesText;
25063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25064         var ddaysText = this.disabledDaysText;
25065         var format = this.format;
25066
25067         var setCellClass = function(cal, cell){
25068             cell.title = "";
25069             var t = d.getTime();
25070             cell.firstChild.dateValue = t;
25071             if(t == today){
25072                 cell.className += " x-date-today";
25073                 cell.title = cal.todayText;
25074             }
25075             if(t == sel){
25076                 cell.className += " x-date-selected";
25077                 setTimeout(function(){
25078                     try{cell.firstChild.focus();}catch(e){}
25079                 }, 50);
25080             }
25081             // disabling
25082             if(t < min) {
25083                 cell.className = " x-date-disabled";
25084                 cell.title = cal.minText;
25085                 return;
25086             }
25087             if(t > max) {
25088                 cell.className = " x-date-disabled";
25089                 cell.title = cal.maxText;
25090                 return;
25091             }
25092             if(ddays){
25093                 if(ddays.indexOf(d.getDay()) != -1){
25094                     cell.title = ddaysText;
25095                     cell.className = " x-date-disabled";
25096                 }
25097             }
25098             if(ddMatch && format){
25099                 var fvalue = d.dateFormat(format);
25100                 if(ddMatch.test(fvalue)){
25101                     cell.title = ddText.replace("%0", fvalue);
25102                     cell.className = " x-date-disabled";
25103                 }
25104             }
25105         };
25106
25107         var i = 0;
25108         for(; i < startingPos; i++) {
25109             textEls[i].innerHTML = (++prevStart);
25110             d.setDate(d.getDate()+1);
25111             cells[i].className = "x-date-prevday";
25112             setCellClass(this, cells[i]);
25113         }
25114         for(; i < days; i++){
25115             intDay = i - startingPos + 1;
25116             textEls[i].innerHTML = (intDay);
25117             d.setDate(d.getDate()+1);
25118             cells[i].className = "x-date-active";
25119             setCellClass(this, cells[i]);
25120         }
25121         var extraDays = 0;
25122         for(; i < 42; i++) {
25123              textEls[i].innerHTML = (++extraDays);
25124              d.setDate(d.getDate()+1);
25125              cells[i].className = "x-date-nextday";
25126              setCellClass(this, cells[i]);
25127         }
25128
25129         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25130
25131         if(!this.internalRender){
25132             var main = this.el.dom.firstChild;
25133             var w = main.offsetWidth;
25134             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25135             Roo.fly(main).setWidth(w);
25136             this.internalRender = true;
25137             // opera does not respect the auto grow header center column
25138             // then, after it gets a width opera refuses to recalculate
25139             // without a second pass
25140             if(Roo.isOpera && !this.secondPass){
25141                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25142                 this.secondPass = true;
25143                 this.update.defer(10, this, [date]);
25144             }
25145         }
25146     }
25147 });        /*
25148  * Based on:
25149  * Ext JS Library 1.1.1
25150  * Copyright(c) 2006-2007, Ext JS, LLC.
25151  *
25152  * Originally Released Under LGPL - original licence link has changed is not relivant.
25153  *
25154  * Fork - LGPL
25155  * <script type="text/javascript">
25156  */
25157 /**
25158  * @class Roo.TabPanel
25159  * @extends Roo.util.Observable
25160  * A lightweight tab container.
25161  * <br><br>
25162  * Usage:
25163  * <pre><code>
25164 // basic tabs 1, built from existing content
25165 var tabs = new Roo.TabPanel("tabs1");
25166 tabs.addTab("script", "View Script");
25167 tabs.addTab("markup", "View Markup");
25168 tabs.activate("script");
25169
25170 // more advanced tabs, built from javascript
25171 var jtabs = new Roo.TabPanel("jtabs");
25172 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25173
25174 // set up the UpdateManager
25175 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25176 var updater = tab2.getUpdateManager();
25177 updater.setDefaultUrl("ajax1.htm");
25178 tab2.on('activate', updater.refresh, updater, true);
25179
25180 // Use setUrl for Ajax loading
25181 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25182 tab3.setUrl("ajax2.htm", null, true);
25183
25184 // Disabled tab
25185 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25186 tab4.disable();
25187
25188 jtabs.activate("jtabs-1");
25189  * </code></pre>
25190  * @constructor
25191  * Create a new TabPanel.
25192  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25193  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25194  */
25195 Roo.TabPanel = function(container, config){
25196     /**
25197     * The container element for this TabPanel.
25198     * @type Roo.Element
25199     */
25200     this.el = Roo.get(container, true);
25201     if(config){
25202         if(typeof config == "boolean"){
25203             this.tabPosition = config ? "bottom" : "top";
25204         }else{
25205             Roo.apply(this, config);
25206         }
25207     }
25208     if(this.tabPosition == "bottom"){
25209         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25210         this.el.addClass("x-tabs-bottom");
25211     }
25212     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25213     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25214     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25215     if(Roo.isIE){
25216         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25217     }
25218     if(this.tabPosition != "bottom"){
25219         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25220          * @type Roo.Element
25221          */
25222         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25223         this.el.addClass("x-tabs-top");
25224     }
25225     this.items = [];
25226
25227     this.bodyEl.setStyle("position", "relative");
25228
25229     this.active = null;
25230     this.activateDelegate = this.activate.createDelegate(this);
25231
25232     this.addEvents({
25233         /**
25234          * @event tabchange
25235          * Fires when the active tab changes
25236          * @param {Roo.TabPanel} this
25237          * @param {Roo.TabPanelItem} activePanel The new active tab
25238          */
25239         "tabchange": true,
25240         /**
25241          * @event beforetabchange
25242          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25243          * @param {Roo.TabPanel} this
25244          * @param {Object} e Set cancel to true on this object to cancel the tab change
25245          * @param {Roo.TabPanelItem} tab The tab being changed to
25246          */
25247         "beforetabchange" : true
25248     });
25249
25250     Roo.EventManager.onWindowResize(this.onResize, this);
25251     this.cpad = this.el.getPadding("lr");
25252     this.hiddenCount = 0;
25253
25254
25255     // toolbar on the tabbar support...
25256     if (this.toolbar) {
25257         var tcfg = this.toolbar;
25258         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25259         this.toolbar = new Roo.Toolbar(tcfg);
25260         if (Roo.isSafari) {
25261             var tbl = tcfg.container.child('table', true);
25262             tbl.setAttribute('width', '100%');
25263         }
25264         
25265     }
25266    
25267
25268
25269     Roo.TabPanel.superclass.constructor.call(this);
25270 };
25271
25272 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25273     /*
25274      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25275      */
25276     tabPosition : "top",
25277     /*
25278      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25279      */
25280     currentTabWidth : 0,
25281     /*
25282      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25283      */
25284     minTabWidth : 40,
25285     /*
25286      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25287      */
25288     maxTabWidth : 250,
25289     /*
25290      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25291      */
25292     preferredTabWidth : 175,
25293     /*
25294      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25295      */
25296     resizeTabs : false,
25297     /*
25298      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25299      */
25300     monitorResize : true,
25301     /*
25302      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25303      */
25304     toolbar : false,
25305
25306     /**
25307      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25308      * @param {String} id The id of the div to use <b>or create</b>
25309      * @param {String} text The text for the tab
25310      * @param {String} content (optional) Content to put in the TabPanelItem body
25311      * @param {Boolean} closable (optional) True to create a close icon on the tab
25312      * @return {Roo.TabPanelItem} The created TabPanelItem
25313      */
25314     addTab : function(id, text, content, closable){
25315         var item = new Roo.TabPanelItem(this, id, text, closable);
25316         this.addTabItem(item);
25317         if(content){
25318             item.setContent(content);
25319         }
25320         return item;
25321     },
25322
25323     /**
25324      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25325      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25326      * @return {Roo.TabPanelItem}
25327      */
25328     getTab : function(id){
25329         return this.items[id];
25330     },
25331
25332     /**
25333      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25334      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25335      */
25336     hideTab : function(id){
25337         var t = this.items[id];
25338         if(!t.isHidden()){
25339            t.setHidden(true);
25340            this.hiddenCount++;
25341            this.autoSizeTabs();
25342         }
25343     },
25344
25345     /**
25346      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25347      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25348      */
25349     unhideTab : function(id){
25350         var t = this.items[id];
25351         if(t.isHidden()){
25352            t.setHidden(false);
25353            this.hiddenCount--;
25354            this.autoSizeTabs();
25355         }
25356     },
25357
25358     /**
25359      * Adds an existing {@link Roo.TabPanelItem}.
25360      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25361      */
25362     addTabItem : function(item){
25363         this.items[item.id] = item;
25364         this.items.push(item);
25365         if(this.resizeTabs){
25366            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25367            this.autoSizeTabs();
25368         }else{
25369             item.autoSize();
25370         }
25371     },
25372
25373     /**
25374      * Removes a {@link Roo.TabPanelItem}.
25375      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25376      */
25377     removeTab : function(id){
25378         var items = this.items;
25379         var tab = items[id];
25380         if(!tab) { return; }
25381         var index = items.indexOf(tab);
25382         if(this.active == tab && items.length > 1){
25383             var newTab = this.getNextAvailable(index);
25384             if(newTab) {
25385                 newTab.activate();
25386             }
25387         }
25388         this.stripEl.dom.removeChild(tab.pnode.dom);
25389         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25390             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25391         }
25392         items.splice(index, 1);
25393         delete this.items[tab.id];
25394         tab.fireEvent("close", tab);
25395         tab.purgeListeners();
25396         this.autoSizeTabs();
25397     },
25398
25399     getNextAvailable : function(start){
25400         var items = this.items;
25401         var index = start;
25402         // look for a next tab that will slide over to
25403         // replace the one being removed
25404         while(index < items.length){
25405             var item = items[++index];
25406             if(item && !item.isHidden()){
25407                 return item;
25408             }
25409         }
25410         // if one isn't found select the previous tab (on the left)
25411         index = start;
25412         while(index >= 0){
25413             var item = items[--index];
25414             if(item && !item.isHidden()){
25415                 return item;
25416             }
25417         }
25418         return null;
25419     },
25420
25421     /**
25422      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25423      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25424      */
25425     disableTab : function(id){
25426         var tab = this.items[id];
25427         if(tab && this.active != tab){
25428             tab.disable();
25429         }
25430     },
25431
25432     /**
25433      * Enables a {@link Roo.TabPanelItem} that is disabled.
25434      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25435      */
25436     enableTab : function(id){
25437         var tab = this.items[id];
25438         tab.enable();
25439     },
25440
25441     /**
25442      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25443      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25444      * @return {Roo.TabPanelItem} The TabPanelItem.
25445      */
25446     activate : function(id){
25447         var tab = this.items[id];
25448         if(!tab){
25449             return null;
25450         }
25451         if(tab == this.active || tab.disabled){
25452             return tab;
25453         }
25454         var e = {};
25455         this.fireEvent("beforetabchange", this, e, tab);
25456         if(e.cancel !== true && !tab.disabled){
25457             if(this.active){
25458                 this.active.hide();
25459             }
25460             this.active = this.items[id];
25461             this.active.show();
25462             this.fireEvent("tabchange", this, this.active);
25463         }
25464         return tab;
25465     },
25466
25467     /**
25468      * Gets the active {@link Roo.TabPanelItem}.
25469      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25470      */
25471     getActiveTab : function(){
25472         return this.active;
25473     },
25474
25475     /**
25476      * Updates the tab body element to fit the height of the container element
25477      * for overflow scrolling
25478      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25479      */
25480     syncHeight : function(targetHeight){
25481         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25482         var bm = this.bodyEl.getMargins();
25483         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25484         this.bodyEl.setHeight(newHeight);
25485         return newHeight;
25486     },
25487
25488     onResize : function(){
25489         if(this.monitorResize){
25490             this.autoSizeTabs();
25491         }
25492     },
25493
25494     /**
25495      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25496      */
25497     beginUpdate : function(){
25498         this.updating = true;
25499     },
25500
25501     /**
25502      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25503      */
25504     endUpdate : function(){
25505         this.updating = false;
25506         this.autoSizeTabs();
25507     },
25508
25509     /**
25510      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25511      */
25512     autoSizeTabs : function(){
25513         var count = this.items.length;
25514         var vcount = count - this.hiddenCount;
25515         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25516         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25517         var availWidth = Math.floor(w / vcount);
25518         var b = this.stripBody;
25519         if(b.getWidth() > w){
25520             var tabs = this.items;
25521             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25522             if(availWidth < this.minTabWidth){
25523                 /*if(!this.sleft){    // incomplete scrolling code
25524                     this.createScrollButtons();
25525                 }
25526                 this.showScroll();
25527                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25528             }
25529         }else{
25530             if(this.currentTabWidth < this.preferredTabWidth){
25531                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25532             }
25533         }
25534     },
25535
25536     /**
25537      * Returns the number of tabs in this TabPanel.
25538      * @return {Number}
25539      */
25540      getCount : function(){
25541          return this.items.length;
25542      },
25543
25544     /**
25545      * Resizes all the tabs to the passed width
25546      * @param {Number} The new width
25547      */
25548     setTabWidth : function(width){
25549         this.currentTabWidth = width;
25550         for(var i = 0, len = this.items.length; i < len; i++) {
25551                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25552         }
25553     },
25554
25555     /**
25556      * Destroys this TabPanel
25557      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25558      */
25559     destroy : function(removeEl){
25560         Roo.EventManager.removeResizeListener(this.onResize, this);
25561         for(var i = 0, len = this.items.length; i < len; i++){
25562             this.items[i].purgeListeners();
25563         }
25564         if(removeEl === true){
25565             this.el.update("");
25566             this.el.remove();
25567         }
25568     }
25569 });
25570
25571 /**
25572  * @class Roo.TabPanelItem
25573  * @extends Roo.util.Observable
25574  * Represents an individual item (tab plus body) in a TabPanel.
25575  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25576  * @param {String} id The id of this TabPanelItem
25577  * @param {String} text The text for the tab of this TabPanelItem
25578  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25579  */
25580 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25581     /**
25582      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25583      * @type Roo.TabPanel
25584      */
25585     this.tabPanel = tabPanel;
25586     /**
25587      * The id for this TabPanelItem
25588      * @type String
25589      */
25590     this.id = id;
25591     /** @private */
25592     this.disabled = false;
25593     /** @private */
25594     this.text = text;
25595     /** @private */
25596     this.loaded = false;
25597     this.closable = closable;
25598
25599     /**
25600      * The body element for this TabPanelItem.
25601      * @type Roo.Element
25602      */
25603     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25604     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25605     this.bodyEl.setStyle("display", "block");
25606     this.bodyEl.setStyle("zoom", "1");
25607     this.hideAction();
25608
25609     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25610     /** @private */
25611     this.el = Roo.get(els.el, true);
25612     this.inner = Roo.get(els.inner, true);
25613     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25614     this.pnode = Roo.get(els.el.parentNode, true);
25615     this.el.on("mousedown", this.onTabMouseDown, this);
25616     this.el.on("click", this.onTabClick, this);
25617     /** @private */
25618     if(closable){
25619         var c = Roo.get(els.close, true);
25620         c.dom.title = this.closeText;
25621         c.addClassOnOver("close-over");
25622         c.on("click", this.closeClick, this);
25623      }
25624
25625     this.addEvents({
25626          /**
25627          * @event activate
25628          * Fires when this tab becomes the active tab.
25629          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25630          * @param {Roo.TabPanelItem} this
25631          */
25632         "activate": true,
25633         /**
25634          * @event beforeclose
25635          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25636          * @param {Roo.TabPanelItem} this
25637          * @param {Object} e Set cancel to true on this object to cancel the close.
25638          */
25639         "beforeclose": true,
25640         /**
25641          * @event close
25642          * Fires when this tab is closed.
25643          * @param {Roo.TabPanelItem} this
25644          */
25645          "close": true,
25646         /**
25647          * @event deactivate
25648          * Fires when this tab is no longer the active tab.
25649          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25650          * @param {Roo.TabPanelItem} this
25651          */
25652          "deactivate" : true
25653     });
25654     this.hidden = false;
25655
25656     Roo.TabPanelItem.superclass.constructor.call(this);
25657 };
25658
25659 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25660     purgeListeners : function(){
25661        Roo.util.Observable.prototype.purgeListeners.call(this);
25662        this.el.removeAllListeners();
25663     },
25664     /**
25665      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25666      */
25667     show : function(){
25668         this.pnode.addClass("on");
25669         this.showAction();
25670         if(Roo.isOpera){
25671             this.tabPanel.stripWrap.repaint();
25672         }
25673         this.fireEvent("activate", this.tabPanel, this);
25674     },
25675
25676     /**
25677      * Returns true if this tab is the active tab.
25678      * @return {Boolean}
25679      */
25680     isActive : function(){
25681         return this.tabPanel.getActiveTab() == this;
25682     },
25683
25684     /**
25685      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25686      */
25687     hide : function(){
25688         this.pnode.removeClass("on");
25689         this.hideAction();
25690         this.fireEvent("deactivate", this.tabPanel, this);
25691     },
25692
25693     hideAction : function(){
25694         this.bodyEl.hide();
25695         this.bodyEl.setStyle("position", "absolute");
25696         this.bodyEl.setLeft("-20000px");
25697         this.bodyEl.setTop("-20000px");
25698     },
25699
25700     showAction : function(){
25701         this.bodyEl.setStyle("position", "relative");
25702         this.bodyEl.setTop("");
25703         this.bodyEl.setLeft("");
25704         this.bodyEl.show();
25705     },
25706
25707     /**
25708      * Set the tooltip for the tab.
25709      * @param {String} tooltip The tab's tooltip
25710      */
25711     setTooltip : function(text){
25712         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25713             this.textEl.dom.qtip = text;
25714             this.textEl.dom.removeAttribute('title');
25715         }else{
25716             this.textEl.dom.title = text;
25717         }
25718     },
25719
25720     onTabClick : function(e){
25721         e.preventDefault();
25722         this.tabPanel.activate(this.id);
25723     },
25724
25725     onTabMouseDown : function(e){
25726         e.preventDefault();
25727         this.tabPanel.activate(this.id);
25728     },
25729
25730     getWidth : function(){
25731         return this.inner.getWidth();
25732     },
25733
25734     setWidth : function(width){
25735         var iwidth = width - this.pnode.getPadding("lr");
25736         this.inner.setWidth(iwidth);
25737         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25738         this.pnode.setWidth(width);
25739     },
25740
25741     /**
25742      * Show or hide the tab
25743      * @param {Boolean} hidden True to hide or false to show.
25744      */
25745     setHidden : function(hidden){
25746         this.hidden = hidden;
25747         this.pnode.setStyle("display", hidden ? "none" : "");
25748     },
25749
25750     /**
25751      * Returns true if this tab is "hidden"
25752      * @return {Boolean}
25753      */
25754     isHidden : function(){
25755         return this.hidden;
25756     },
25757
25758     /**
25759      * Returns the text for this tab
25760      * @return {String}
25761      */
25762     getText : function(){
25763         return this.text;
25764     },
25765
25766     autoSize : function(){
25767         //this.el.beginMeasure();
25768         this.textEl.setWidth(1);
25769         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25770         //this.el.endMeasure();
25771     },
25772
25773     /**
25774      * Sets the text for the tab (Note: this also sets the tooltip text)
25775      * @param {String} text The tab's text and tooltip
25776      */
25777     setText : function(text){
25778         this.text = text;
25779         this.textEl.update(text);
25780         this.setTooltip(text);
25781         if(!this.tabPanel.resizeTabs){
25782             this.autoSize();
25783         }
25784     },
25785     /**
25786      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25787      */
25788     activate : function(){
25789         this.tabPanel.activate(this.id);
25790     },
25791
25792     /**
25793      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25794      */
25795     disable : function(){
25796         if(this.tabPanel.active != this){
25797             this.disabled = true;
25798             this.pnode.addClass("disabled");
25799         }
25800     },
25801
25802     /**
25803      * Enables this TabPanelItem if it was previously disabled.
25804      */
25805     enable : function(){
25806         this.disabled = false;
25807         this.pnode.removeClass("disabled");
25808     },
25809
25810     /**
25811      * Sets the content for this TabPanelItem.
25812      * @param {String} content The content
25813      * @param {Boolean} loadScripts true to look for and load scripts
25814      */
25815     setContent : function(content, loadScripts){
25816         this.bodyEl.update(content, loadScripts);
25817     },
25818
25819     /**
25820      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25821      * @return {Roo.UpdateManager} The UpdateManager
25822      */
25823     getUpdateManager : function(){
25824         return this.bodyEl.getUpdateManager();
25825     },
25826
25827     /**
25828      * Set a URL to be used to load the content for this TabPanelItem.
25829      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25830      * @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)
25831      * @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)
25832      * @return {Roo.UpdateManager} The UpdateManager
25833      */
25834     setUrl : function(url, params, loadOnce){
25835         if(this.refreshDelegate){
25836             this.un('activate', this.refreshDelegate);
25837         }
25838         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25839         this.on("activate", this.refreshDelegate);
25840         return this.bodyEl.getUpdateManager();
25841     },
25842
25843     /** @private */
25844     _handleRefresh : function(url, params, loadOnce){
25845         if(!loadOnce || !this.loaded){
25846             var updater = this.bodyEl.getUpdateManager();
25847             updater.update(url, params, this._setLoaded.createDelegate(this));
25848         }
25849     },
25850
25851     /**
25852      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25853      *   Will fail silently if the setUrl method has not been called.
25854      *   This does not activate the panel, just updates its content.
25855      */
25856     refresh : function(){
25857         if(this.refreshDelegate){
25858            this.loaded = false;
25859            this.refreshDelegate();
25860         }
25861     },
25862
25863     /** @private */
25864     _setLoaded : function(){
25865         this.loaded = true;
25866     },
25867
25868     /** @private */
25869     closeClick : function(e){
25870         var o = {};
25871         e.stopEvent();
25872         this.fireEvent("beforeclose", this, o);
25873         if(o.cancel !== true){
25874             this.tabPanel.removeTab(this.id);
25875         }
25876     },
25877     /**
25878      * The text displayed in the tooltip for the close icon.
25879      * @type String
25880      */
25881     closeText : "Close this tab"
25882 });
25883
25884 /** @private */
25885 Roo.TabPanel.prototype.createStrip = function(container){
25886     var strip = document.createElement("div");
25887     strip.className = "x-tabs-wrap";
25888     container.appendChild(strip);
25889     return strip;
25890 };
25891 /** @private */
25892 Roo.TabPanel.prototype.createStripList = function(strip){
25893     // div wrapper for retard IE
25894     // returns the "tr" element.
25895     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25896         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25897         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25898     return strip.firstChild.firstChild.firstChild.firstChild;
25899 };
25900 /** @private */
25901 Roo.TabPanel.prototype.createBody = function(container){
25902     var body = document.createElement("div");
25903     Roo.id(body, "tab-body");
25904     Roo.fly(body).addClass("x-tabs-body");
25905     container.appendChild(body);
25906     return body;
25907 };
25908 /** @private */
25909 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25910     var body = Roo.getDom(id);
25911     if(!body){
25912         body = document.createElement("div");
25913         body.id = id;
25914     }
25915     Roo.fly(body).addClass("x-tabs-item-body");
25916     bodyEl.insertBefore(body, bodyEl.firstChild);
25917     return body;
25918 };
25919 /** @private */
25920 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25921     var td = document.createElement("td");
25922     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
25923     //stripEl.appendChild(td);
25924     if(closable){
25925         td.className = "x-tabs-closable";
25926         if(!this.closeTpl){
25927             this.closeTpl = new Roo.Template(
25928                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25929                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25930                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25931             );
25932         }
25933         var el = this.closeTpl.overwrite(td, {"text": text});
25934         var close = el.getElementsByTagName("div")[0];
25935         var inner = el.getElementsByTagName("em")[0];
25936         return {"el": el, "close": close, "inner": inner};
25937     } else {
25938         if(!this.tabTpl){
25939             this.tabTpl = new Roo.Template(
25940                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25941                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25942             );
25943         }
25944         var el = this.tabTpl.overwrite(td, {"text": text});
25945         var inner = el.getElementsByTagName("em")[0];
25946         return {"el": el, "inner": inner};
25947     }
25948 };/*
25949  * Based on:
25950  * Ext JS Library 1.1.1
25951  * Copyright(c) 2006-2007, Ext JS, LLC.
25952  *
25953  * Originally Released Under LGPL - original licence link has changed is not relivant.
25954  *
25955  * Fork - LGPL
25956  * <script type="text/javascript">
25957  */
25958
25959 /**
25960  * @class Roo.Button
25961  * @extends Roo.util.Observable
25962  * Simple Button class
25963  * @cfg {String} text The button text
25964  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25965  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25966  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25967  * @cfg {Object} scope The scope of the handler
25968  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25969  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25970  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25971  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25972  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25973  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25974    applies if enableToggle = true)
25975  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25976  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25977   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25978  * @constructor
25979  * Create a new button
25980  * @param {Object} config The config object
25981  */
25982 Roo.Button = function(renderTo, config)
25983 {
25984     if (!config) {
25985         config = renderTo;
25986         renderTo = config.renderTo || false;
25987     }
25988     
25989     Roo.apply(this, config);
25990     this.addEvents({
25991         /**
25992              * @event click
25993              * Fires when this button is clicked
25994              * @param {Button} this
25995              * @param {EventObject} e The click event
25996              */
25997             "click" : true,
25998         /**
25999              * @event toggle
26000              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26001              * @param {Button} this
26002              * @param {Boolean} pressed
26003              */
26004             "toggle" : true,
26005         /**
26006              * @event mouseover
26007              * Fires when the mouse hovers over the button
26008              * @param {Button} this
26009              * @param {Event} e The event object
26010              */
26011         'mouseover' : true,
26012         /**
26013              * @event mouseout
26014              * Fires when the mouse exits the button
26015              * @param {Button} this
26016              * @param {Event} e The event object
26017              */
26018         'mouseout': true,
26019          /**
26020              * @event render
26021              * Fires when the button is rendered
26022              * @param {Button} this
26023              */
26024         'render': true
26025     });
26026     if(this.menu){
26027         this.menu = Roo.menu.MenuMgr.get(this.menu);
26028     }
26029     // register listeners first!!  - so render can be captured..
26030     Roo.util.Observable.call(this);
26031     if(renderTo){
26032         this.render(renderTo);
26033     }
26034     
26035   
26036 };
26037
26038 Roo.extend(Roo.Button, Roo.util.Observable, {
26039     /**
26040      * 
26041      */
26042     
26043     /**
26044      * Read-only. True if this button is hidden
26045      * @type Boolean
26046      */
26047     hidden : false,
26048     /**
26049      * Read-only. True if this button is disabled
26050      * @type Boolean
26051      */
26052     disabled : false,
26053     /**
26054      * Read-only. True if this button is pressed (only if enableToggle = true)
26055      * @type Boolean
26056      */
26057     pressed : false,
26058
26059     /**
26060      * @cfg {Number} tabIndex 
26061      * The DOM tabIndex for this button (defaults to undefined)
26062      */
26063     tabIndex : undefined,
26064
26065     /**
26066      * @cfg {Boolean} enableToggle
26067      * True to enable pressed/not pressed toggling (defaults to false)
26068      */
26069     enableToggle: false,
26070     /**
26071      * @cfg {Mixed} menu
26072      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26073      */
26074     menu : undefined,
26075     /**
26076      * @cfg {String} menuAlign
26077      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26078      */
26079     menuAlign : "tl-bl?",
26080
26081     /**
26082      * @cfg {String} iconCls
26083      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26084      */
26085     iconCls : undefined,
26086     /**
26087      * @cfg {String} type
26088      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26089      */
26090     type : 'button',
26091
26092     // private
26093     menuClassTarget: 'tr',
26094
26095     /**
26096      * @cfg {String} clickEvent
26097      * The type of event to map to the button's event handler (defaults to 'click')
26098      */
26099     clickEvent : 'click',
26100
26101     /**
26102      * @cfg {Boolean} handleMouseEvents
26103      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26104      */
26105     handleMouseEvents : true,
26106
26107     /**
26108      * @cfg {String} tooltipType
26109      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26110      */
26111     tooltipType : 'qtip',
26112
26113     /**
26114      * @cfg {String} cls
26115      * A CSS class to apply to the button's main element.
26116      */
26117     
26118     /**
26119      * @cfg {Roo.Template} template (Optional)
26120      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26121      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26122      * require code modifications if required elements (e.g. a button) aren't present.
26123      */
26124
26125     // private
26126     render : function(renderTo){
26127         var btn;
26128         if(this.hideParent){
26129             this.parentEl = Roo.get(renderTo);
26130         }
26131         if(!this.dhconfig){
26132             if(!this.template){
26133                 if(!Roo.Button.buttonTemplate){
26134                     // hideous table template
26135                     Roo.Button.buttonTemplate = new Roo.Template(
26136                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26137                         '<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>',
26138                         "</tr></tbody></table>");
26139                 }
26140                 this.template = Roo.Button.buttonTemplate;
26141             }
26142             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26143             var btnEl = btn.child("button:first");
26144             btnEl.on('focus', this.onFocus, this);
26145             btnEl.on('blur', this.onBlur, this);
26146             if(this.cls){
26147                 btn.addClass(this.cls);
26148             }
26149             if(this.icon){
26150                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26151             }
26152             if(this.iconCls){
26153                 btnEl.addClass(this.iconCls);
26154                 if(!this.cls){
26155                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26156                 }
26157             }
26158             if(this.tabIndex !== undefined){
26159                 btnEl.dom.tabIndex = this.tabIndex;
26160             }
26161             if(this.tooltip){
26162                 if(typeof this.tooltip == 'object'){
26163                     Roo.QuickTips.tips(Roo.apply({
26164                           target: btnEl.id
26165                     }, this.tooltip));
26166                 } else {
26167                     btnEl.dom[this.tooltipType] = this.tooltip;
26168                 }
26169             }
26170         }else{
26171             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26172         }
26173         this.el = btn;
26174         if(this.id){
26175             this.el.dom.id = this.el.id = this.id;
26176         }
26177         if(this.menu){
26178             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26179             this.menu.on("show", this.onMenuShow, this);
26180             this.menu.on("hide", this.onMenuHide, this);
26181         }
26182         btn.addClass("x-btn");
26183         if(Roo.isIE && !Roo.isIE7){
26184             this.autoWidth.defer(1, this);
26185         }else{
26186             this.autoWidth();
26187         }
26188         if(this.handleMouseEvents){
26189             btn.on("mouseover", this.onMouseOver, this);
26190             btn.on("mouseout", this.onMouseOut, this);
26191             btn.on("mousedown", this.onMouseDown, this);
26192         }
26193         btn.on(this.clickEvent, this.onClick, this);
26194         //btn.on("mouseup", this.onMouseUp, this);
26195         if(this.hidden){
26196             this.hide();
26197         }
26198         if(this.disabled){
26199             this.disable();
26200         }
26201         Roo.ButtonToggleMgr.register(this);
26202         if(this.pressed){
26203             this.el.addClass("x-btn-pressed");
26204         }
26205         if(this.repeat){
26206             var repeater = new Roo.util.ClickRepeater(btn,
26207                 typeof this.repeat == "object" ? this.repeat : {}
26208             );
26209             repeater.on("click", this.onClick,  this);
26210         }
26211         
26212         this.fireEvent('render', this);
26213         
26214     },
26215     /**
26216      * Returns the button's underlying element
26217      * @return {Roo.Element} The element
26218      */
26219     getEl : function(){
26220         return this.el;  
26221     },
26222     
26223     /**
26224      * Destroys this Button and removes any listeners.
26225      */
26226     destroy : function(){
26227         Roo.ButtonToggleMgr.unregister(this);
26228         this.el.removeAllListeners();
26229         this.purgeListeners();
26230         this.el.remove();
26231     },
26232
26233     // private
26234     autoWidth : function(){
26235         if(this.el){
26236             this.el.setWidth("auto");
26237             if(Roo.isIE7 && Roo.isStrict){
26238                 var ib = this.el.child('button');
26239                 if(ib && ib.getWidth() > 20){
26240                     ib.clip();
26241                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26242                 }
26243             }
26244             if(this.minWidth){
26245                 if(this.hidden){
26246                     this.el.beginMeasure();
26247                 }
26248                 if(this.el.getWidth() < this.minWidth){
26249                     this.el.setWidth(this.minWidth);
26250                 }
26251                 if(this.hidden){
26252                     this.el.endMeasure();
26253                 }
26254             }
26255         }
26256     },
26257
26258     /**
26259      * Assigns this button's click handler
26260      * @param {Function} handler The function to call when the button is clicked
26261      * @param {Object} scope (optional) Scope for the function passed in
26262      */
26263     setHandler : function(handler, scope){
26264         this.handler = handler;
26265         this.scope = scope;  
26266     },
26267     
26268     /**
26269      * Sets this button's text
26270      * @param {String} text The button text
26271      */
26272     setText : function(text){
26273         this.text = text;
26274         if(this.el){
26275             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26276         }
26277         this.autoWidth();
26278     },
26279     
26280     /**
26281      * Gets the text for this button
26282      * @return {String} The button text
26283      */
26284     getText : function(){
26285         return this.text;  
26286     },
26287     
26288     /**
26289      * Show this button
26290      */
26291     show: function(){
26292         this.hidden = false;
26293         if(this.el){
26294             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26295         }
26296     },
26297     
26298     /**
26299      * Hide this button
26300      */
26301     hide: function(){
26302         this.hidden = true;
26303         if(this.el){
26304             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26305         }
26306     },
26307     
26308     /**
26309      * Convenience function for boolean show/hide
26310      * @param {Boolean} visible True to show, false to hide
26311      */
26312     setVisible: function(visible){
26313         if(visible) {
26314             this.show();
26315         }else{
26316             this.hide();
26317         }
26318     },
26319     
26320     /**
26321      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26322      * @param {Boolean} state (optional) Force a particular state
26323      */
26324     toggle : function(state){
26325         state = state === undefined ? !this.pressed : state;
26326         if(state != this.pressed){
26327             if(state){
26328                 this.el.addClass("x-btn-pressed");
26329                 this.pressed = true;
26330                 this.fireEvent("toggle", this, true);
26331             }else{
26332                 this.el.removeClass("x-btn-pressed");
26333                 this.pressed = false;
26334                 this.fireEvent("toggle", this, false);
26335             }
26336             if(this.toggleHandler){
26337                 this.toggleHandler.call(this.scope || this, this, state);
26338             }
26339         }
26340     },
26341     
26342     /**
26343      * Focus the button
26344      */
26345     focus : function(){
26346         this.el.child('button:first').focus();
26347     },
26348     
26349     /**
26350      * Disable this button
26351      */
26352     disable : function(){
26353         if(this.el){
26354             this.el.addClass("x-btn-disabled");
26355         }
26356         this.disabled = true;
26357     },
26358     
26359     /**
26360      * Enable this button
26361      */
26362     enable : function(){
26363         if(this.el){
26364             this.el.removeClass("x-btn-disabled");
26365         }
26366         this.disabled = false;
26367     },
26368
26369     /**
26370      * Convenience function for boolean enable/disable
26371      * @param {Boolean} enabled True to enable, false to disable
26372      */
26373     setDisabled : function(v){
26374         this[v !== true ? "enable" : "disable"]();
26375     },
26376
26377     // private
26378     onClick : function(e){
26379         if(e){
26380             e.preventDefault();
26381         }
26382         if(e.button != 0){
26383             return;
26384         }
26385         if(!this.disabled){
26386             if(this.enableToggle){
26387                 this.toggle();
26388             }
26389             if(this.menu && !this.menu.isVisible()){
26390                 this.menu.show(this.el, this.menuAlign);
26391             }
26392             this.fireEvent("click", this, e);
26393             if(this.handler){
26394                 this.el.removeClass("x-btn-over");
26395                 this.handler.call(this.scope || this, this, e);
26396             }
26397         }
26398     },
26399     // private
26400     onMouseOver : function(e){
26401         if(!this.disabled){
26402             this.el.addClass("x-btn-over");
26403             this.fireEvent('mouseover', this, e);
26404         }
26405     },
26406     // private
26407     onMouseOut : function(e){
26408         if(!e.within(this.el,  true)){
26409             this.el.removeClass("x-btn-over");
26410             this.fireEvent('mouseout', this, e);
26411         }
26412     },
26413     // private
26414     onFocus : function(e){
26415         if(!this.disabled){
26416             this.el.addClass("x-btn-focus");
26417         }
26418     },
26419     // private
26420     onBlur : function(e){
26421         this.el.removeClass("x-btn-focus");
26422     },
26423     // private
26424     onMouseDown : function(e){
26425         if(!this.disabled && e.button == 0){
26426             this.el.addClass("x-btn-click");
26427             Roo.get(document).on('mouseup', this.onMouseUp, this);
26428         }
26429     },
26430     // private
26431     onMouseUp : function(e){
26432         if(e.button == 0){
26433             this.el.removeClass("x-btn-click");
26434             Roo.get(document).un('mouseup', this.onMouseUp, this);
26435         }
26436     },
26437     // private
26438     onMenuShow : function(e){
26439         this.el.addClass("x-btn-menu-active");
26440     },
26441     // private
26442     onMenuHide : function(e){
26443         this.el.removeClass("x-btn-menu-active");
26444     }   
26445 });
26446
26447 // Private utility class used by Button
26448 Roo.ButtonToggleMgr = function(){
26449    var groups = {};
26450    
26451    function toggleGroup(btn, state){
26452        if(state){
26453            var g = groups[btn.toggleGroup];
26454            for(var i = 0, l = g.length; i < l; i++){
26455                if(g[i] != btn){
26456                    g[i].toggle(false);
26457                }
26458            }
26459        }
26460    }
26461    
26462    return {
26463        register : function(btn){
26464            if(!btn.toggleGroup){
26465                return;
26466            }
26467            var g = groups[btn.toggleGroup];
26468            if(!g){
26469                g = groups[btn.toggleGroup] = [];
26470            }
26471            g.push(btn);
26472            btn.on("toggle", toggleGroup);
26473        },
26474        
26475        unregister : function(btn){
26476            if(!btn.toggleGroup){
26477                return;
26478            }
26479            var g = groups[btn.toggleGroup];
26480            if(g){
26481                g.remove(btn);
26482                btn.un("toggle", toggleGroup);
26483            }
26484        }
26485    };
26486 }();/*
26487  * Based on:
26488  * Ext JS Library 1.1.1
26489  * Copyright(c) 2006-2007, Ext JS, LLC.
26490  *
26491  * Originally Released Under LGPL - original licence link has changed is not relivant.
26492  *
26493  * Fork - LGPL
26494  * <script type="text/javascript">
26495  */
26496  
26497 /**
26498  * @class Roo.SplitButton
26499  * @extends Roo.Button
26500  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26501  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26502  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26503  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26504  * @cfg {String} arrowTooltip The title attribute of the arrow
26505  * @constructor
26506  * Create a new menu button
26507  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26508  * @param {Object} config The config object
26509  */
26510 Roo.SplitButton = function(renderTo, config){
26511     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26512     /**
26513      * @event arrowclick
26514      * Fires when this button's arrow is clicked
26515      * @param {SplitButton} this
26516      * @param {EventObject} e The click event
26517      */
26518     this.addEvents({"arrowclick":true});
26519 };
26520
26521 Roo.extend(Roo.SplitButton, Roo.Button, {
26522     render : function(renderTo){
26523         // this is one sweet looking template!
26524         var tpl = new Roo.Template(
26525             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26526             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26527             '<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>',
26528             "</tbody></table></td><td>",
26529             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26530             '<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>',
26531             "</tbody></table></td></tr></table>"
26532         );
26533         var btn = tpl.append(renderTo, [this.text, this.type], true);
26534         var btnEl = btn.child("button");
26535         if(this.cls){
26536             btn.addClass(this.cls);
26537         }
26538         if(this.icon){
26539             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26540         }
26541         if(this.iconCls){
26542             btnEl.addClass(this.iconCls);
26543             if(!this.cls){
26544                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26545             }
26546         }
26547         this.el = btn;
26548         if(this.handleMouseEvents){
26549             btn.on("mouseover", this.onMouseOver, this);
26550             btn.on("mouseout", this.onMouseOut, this);
26551             btn.on("mousedown", this.onMouseDown, this);
26552             btn.on("mouseup", this.onMouseUp, this);
26553         }
26554         btn.on(this.clickEvent, this.onClick, this);
26555         if(this.tooltip){
26556             if(typeof this.tooltip == 'object'){
26557                 Roo.QuickTips.tips(Roo.apply({
26558                       target: btnEl.id
26559                 }, this.tooltip));
26560             } else {
26561                 btnEl.dom[this.tooltipType] = this.tooltip;
26562             }
26563         }
26564         if(this.arrowTooltip){
26565             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26566         }
26567         if(this.hidden){
26568             this.hide();
26569         }
26570         if(this.disabled){
26571             this.disable();
26572         }
26573         if(this.pressed){
26574             this.el.addClass("x-btn-pressed");
26575         }
26576         if(Roo.isIE && !Roo.isIE7){
26577             this.autoWidth.defer(1, this);
26578         }else{
26579             this.autoWidth();
26580         }
26581         if(this.menu){
26582             this.menu.on("show", this.onMenuShow, this);
26583             this.menu.on("hide", this.onMenuHide, this);
26584         }
26585         this.fireEvent('render', this);
26586     },
26587
26588     // private
26589     autoWidth : function(){
26590         if(this.el){
26591             var tbl = this.el.child("table:first");
26592             var tbl2 = this.el.child("table:last");
26593             this.el.setWidth("auto");
26594             tbl.setWidth("auto");
26595             if(Roo.isIE7 && Roo.isStrict){
26596                 var ib = this.el.child('button:first');
26597                 if(ib && ib.getWidth() > 20){
26598                     ib.clip();
26599                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26600                 }
26601             }
26602             if(this.minWidth){
26603                 if(this.hidden){
26604                     this.el.beginMeasure();
26605                 }
26606                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26607                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26608                 }
26609                 if(this.hidden){
26610                     this.el.endMeasure();
26611                 }
26612             }
26613             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26614         } 
26615     },
26616     /**
26617      * Sets this button's click handler
26618      * @param {Function} handler The function to call when the button is clicked
26619      * @param {Object} scope (optional) Scope for the function passed above
26620      */
26621     setHandler : function(handler, scope){
26622         this.handler = handler;
26623         this.scope = scope;  
26624     },
26625     
26626     /**
26627      * Sets this button's arrow click handler
26628      * @param {Function} handler The function to call when the arrow is clicked
26629      * @param {Object} scope (optional) Scope for the function passed above
26630      */
26631     setArrowHandler : function(handler, scope){
26632         this.arrowHandler = handler;
26633         this.scope = scope;  
26634     },
26635     
26636     /**
26637      * Focus the button
26638      */
26639     focus : function(){
26640         if(this.el){
26641             this.el.child("button:first").focus();
26642         }
26643     },
26644
26645     // private
26646     onClick : function(e){
26647         e.preventDefault();
26648         if(!this.disabled){
26649             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26650                 if(this.menu && !this.menu.isVisible()){
26651                     this.menu.show(this.el, this.menuAlign);
26652                 }
26653                 this.fireEvent("arrowclick", this, e);
26654                 if(this.arrowHandler){
26655                     this.arrowHandler.call(this.scope || this, this, e);
26656                 }
26657             }else{
26658                 this.fireEvent("click", this, e);
26659                 if(this.handler){
26660                     this.handler.call(this.scope || this, this, e);
26661                 }
26662             }
26663         }
26664     },
26665     // private
26666     onMouseDown : function(e){
26667         if(!this.disabled){
26668             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26669         }
26670     },
26671     // private
26672     onMouseUp : function(e){
26673         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26674     }   
26675 });
26676
26677
26678 // backwards compat
26679 Roo.MenuButton = Roo.SplitButton;/*
26680  * Based on:
26681  * Ext JS Library 1.1.1
26682  * Copyright(c) 2006-2007, Ext JS, LLC.
26683  *
26684  * Originally Released Under LGPL - original licence link has changed is not relivant.
26685  *
26686  * Fork - LGPL
26687  * <script type="text/javascript">
26688  */
26689
26690 /**
26691  * @class Roo.Toolbar
26692  * Basic Toolbar class.
26693  * @constructor
26694  * Creates a new Toolbar
26695  * @param {Object} config The config object
26696  */ 
26697 Roo.Toolbar = function(container, buttons, config)
26698 {
26699     /// old consturctor format still supported..
26700     if(container instanceof Array){ // omit the container for later rendering
26701         buttons = container;
26702         config = buttons;
26703         container = null;
26704     }
26705     if (typeof(container) == 'object' && container.xtype) {
26706         config = container;
26707         container = config.container;
26708         buttons = config.buttons; // not really - use items!!
26709     }
26710     var xitems = [];
26711     if (config && config.items) {
26712         xitems = config.items;
26713         delete config.items;
26714     }
26715     Roo.apply(this, config);
26716     this.buttons = buttons;
26717     
26718     if(container){
26719         this.render(container);
26720     }
26721     Roo.each(xitems, function(b) {
26722         this.add(b);
26723     }, this);
26724     
26725 };
26726
26727 Roo.Toolbar.prototype = {
26728     /**
26729      * @cfg {Roo.data.Store} items
26730      * array of button configs or elements to add
26731      */
26732     
26733     /**
26734      * @cfg {String/HTMLElement/Element} container
26735      * The id or element that will contain the toolbar
26736      */
26737     // private
26738     render : function(ct){
26739         this.el = Roo.get(ct);
26740         if(this.cls){
26741             this.el.addClass(this.cls);
26742         }
26743         // using a table allows for vertical alignment
26744         // 100% width is needed by Safari...
26745         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26746         this.tr = this.el.child("tr", true);
26747         var autoId = 0;
26748         this.items = new Roo.util.MixedCollection(false, function(o){
26749             return o.id || ("item" + (++autoId));
26750         });
26751         if(this.buttons){
26752             this.add.apply(this, this.buttons);
26753             delete this.buttons;
26754         }
26755     },
26756
26757     /**
26758      * Adds element(s) to the toolbar -- this function takes a variable number of 
26759      * arguments of mixed type and adds them to the toolbar.
26760      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26761      * <ul>
26762      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26763      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26764      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26765      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26766      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26767      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26768      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26769      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26770      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26771      * </ul>
26772      * @param {Mixed} arg2
26773      * @param {Mixed} etc.
26774      */
26775     add : function(){
26776         var a = arguments, l = a.length;
26777         for(var i = 0; i < l; i++){
26778             this._add(a[i]);
26779         }
26780     },
26781     // private..
26782     _add : function(el) {
26783         
26784         if (el.xtype) {
26785             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26786         }
26787         
26788         if (el.applyTo){ // some kind of form field
26789             return this.addField(el);
26790         } 
26791         if (el.render){ // some kind of Toolbar.Item
26792             return this.addItem(el);
26793         }
26794         if (typeof el == "string"){ // string
26795             if(el == "separator" || el == "-"){
26796                 return this.addSeparator();
26797             }
26798             if (el == " "){
26799                 return this.addSpacer();
26800             }
26801             if(el == "->"){
26802                 return this.addFill();
26803             }
26804             return this.addText(el);
26805             
26806         }
26807         if(el.tagName){ // element
26808             return this.addElement(el);
26809         }
26810         if(typeof el == "object"){ // must be button config?
26811             return this.addButton(el);
26812         }
26813         // and now what?!?!
26814         return false;
26815         
26816     },
26817     
26818     /**
26819      * Add an Xtype element
26820      * @param {Object} xtype Xtype Object
26821      * @return {Object} created Object
26822      */
26823     addxtype : function(e){
26824         return this.add(e);  
26825     },
26826     
26827     /**
26828      * Returns the Element for this toolbar.
26829      * @return {Roo.Element}
26830      */
26831     getEl : function(){
26832         return this.el;  
26833     },
26834     
26835     /**
26836      * Adds a separator
26837      * @return {Roo.Toolbar.Item} The separator item
26838      */
26839     addSeparator : function(){
26840         return this.addItem(new Roo.Toolbar.Separator());
26841     },
26842
26843     /**
26844      * Adds a spacer element
26845      * @return {Roo.Toolbar.Spacer} The spacer item
26846      */
26847     addSpacer : function(){
26848         return this.addItem(new Roo.Toolbar.Spacer());
26849     },
26850
26851     /**
26852      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26853      * @return {Roo.Toolbar.Fill} The fill item
26854      */
26855     addFill : function(){
26856         return this.addItem(new Roo.Toolbar.Fill());
26857     },
26858
26859     /**
26860      * Adds any standard HTML element to the toolbar
26861      * @param {String/HTMLElement/Element} el The element or id of the element to add
26862      * @return {Roo.Toolbar.Item} The element's item
26863      */
26864     addElement : function(el){
26865         return this.addItem(new Roo.Toolbar.Item(el));
26866     },
26867     /**
26868      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26869      * @type Roo.util.MixedCollection  
26870      */
26871     items : false,
26872      
26873     /**
26874      * Adds any Toolbar.Item or subclass
26875      * @param {Roo.Toolbar.Item} item
26876      * @return {Roo.Toolbar.Item} The item
26877      */
26878     addItem : function(item){
26879         var td = this.nextBlock();
26880         item.render(td);
26881         this.items.add(item);
26882         return item;
26883     },
26884     
26885     /**
26886      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26887      * @param {Object/Array} config A button config or array of configs
26888      * @return {Roo.Toolbar.Button/Array}
26889      */
26890     addButton : function(config){
26891         if(config instanceof Array){
26892             var buttons = [];
26893             for(var i = 0, len = config.length; i < len; i++) {
26894                 buttons.push(this.addButton(config[i]));
26895             }
26896             return buttons;
26897         }
26898         var b = config;
26899         if(!(config instanceof Roo.Toolbar.Button)){
26900             b = config.split ?
26901                 new Roo.Toolbar.SplitButton(config) :
26902                 new Roo.Toolbar.Button(config);
26903         }
26904         var td = this.nextBlock();
26905         b.render(td);
26906         this.items.add(b);
26907         return b;
26908     },
26909     
26910     /**
26911      * Adds text to the toolbar
26912      * @param {String} text The text to add
26913      * @return {Roo.Toolbar.Item} The element's item
26914      */
26915     addText : function(text){
26916         return this.addItem(new Roo.Toolbar.TextItem(text));
26917     },
26918     
26919     /**
26920      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26921      * @param {Number} index The index where the item is to be inserted
26922      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26923      * @return {Roo.Toolbar.Button/Item}
26924      */
26925     insertButton : function(index, item){
26926         if(item instanceof Array){
26927             var buttons = [];
26928             for(var i = 0, len = item.length; i < len; i++) {
26929                buttons.push(this.insertButton(index + i, item[i]));
26930             }
26931             return buttons;
26932         }
26933         if (!(item instanceof Roo.Toolbar.Button)){
26934            item = new Roo.Toolbar.Button(item);
26935         }
26936         var td = document.createElement("td");
26937         this.tr.insertBefore(td, this.tr.childNodes[index]);
26938         item.render(td);
26939         this.items.insert(index, item);
26940         return item;
26941     },
26942     
26943     /**
26944      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26945      * @param {Object} config
26946      * @return {Roo.Toolbar.Item} The element's item
26947      */
26948     addDom : function(config, returnEl){
26949         var td = this.nextBlock();
26950         Roo.DomHelper.overwrite(td, config);
26951         var ti = new Roo.Toolbar.Item(td.firstChild);
26952         ti.render(td);
26953         this.items.add(ti);
26954         return ti;
26955     },
26956
26957     /**
26958      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26959      * @type Roo.util.MixedCollection  
26960      */
26961     fields : false,
26962     
26963     /**
26964      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26965      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26966      * @param {Roo.form.Field} field
26967      * @return {Roo.ToolbarItem}
26968      */
26969      
26970       
26971     addField : function(field) {
26972         if (!this.fields) {
26973             var autoId = 0;
26974             this.fields = new Roo.util.MixedCollection(false, function(o){
26975                 return o.id || ("item" + (++autoId));
26976             });
26977
26978         }
26979         
26980         var td = this.nextBlock();
26981         field.render(td);
26982         var ti = new Roo.Toolbar.Item(td.firstChild);
26983         ti.render(td);
26984         this.items.add(ti);
26985         this.fields.add(field);
26986         return ti;
26987     },
26988     /**
26989      * Hide the toolbar
26990      * @method hide
26991      */
26992      
26993       
26994     hide : function()
26995     {
26996         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26997         this.el.child('div').hide();
26998     },
26999     /**
27000      * Show the toolbar
27001      * @method show
27002      */
27003     show : function()
27004     {
27005         this.el.child('div').show();
27006     },
27007       
27008     // private
27009     nextBlock : function(){
27010         var td = document.createElement("td");
27011         this.tr.appendChild(td);
27012         return td;
27013     },
27014
27015     // private
27016     destroy : function(){
27017         if(this.items){ // rendered?
27018             Roo.destroy.apply(Roo, this.items.items);
27019         }
27020         if(this.fields){ // rendered?
27021             Roo.destroy.apply(Roo, this.fields.items);
27022         }
27023         Roo.Element.uncache(this.el, this.tr);
27024     }
27025 };
27026
27027 /**
27028  * @class Roo.Toolbar.Item
27029  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27030  * @constructor
27031  * Creates a new Item
27032  * @param {HTMLElement} el 
27033  */
27034 Roo.Toolbar.Item = function(el){
27035     this.el = Roo.getDom(el);
27036     this.id = Roo.id(this.el);
27037     this.hidden = false;
27038 };
27039
27040 Roo.Toolbar.Item.prototype = {
27041     
27042     /**
27043      * Get this item's HTML Element
27044      * @return {HTMLElement}
27045      */
27046     getEl : function(){
27047        return this.el;  
27048     },
27049
27050     // private
27051     render : function(td){
27052         this.td = td;
27053         td.appendChild(this.el);
27054     },
27055     
27056     /**
27057      * Removes and destroys this item.
27058      */
27059     destroy : function(){
27060         this.td.parentNode.removeChild(this.td);
27061     },
27062     
27063     /**
27064      * Shows this item.
27065      */
27066     show: function(){
27067         this.hidden = false;
27068         this.td.style.display = "";
27069     },
27070     
27071     /**
27072      * Hides this item.
27073      */
27074     hide: function(){
27075         this.hidden = true;
27076         this.td.style.display = "none";
27077     },
27078     
27079     /**
27080      * Convenience function for boolean show/hide.
27081      * @param {Boolean} visible true to show/false to hide
27082      */
27083     setVisible: function(visible){
27084         if(visible) {
27085             this.show();
27086         }else{
27087             this.hide();
27088         }
27089     },
27090     
27091     /**
27092      * Try to focus this item.
27093      */
27094     focus : function(){
27095         Roo.fly(this.el).focus();
27096     },
27097     
27098     /**
27099      * Disables this item.
27100      */
27101     disable : function(){
27102         Roo.fly(this.td).addClass("x-item-disabled");
27103         this.disabled = true;
27104         this.el.disabled = true;
27105     },
27106     
27107     /**
27108      * Enables this item.
27109      */
27110     enable : function(){
27111         Roo.fly(this.td).removeClass("x-item-disabled");
27112         this.disabled = false;
27113         this.el.disabled = false;
27114     }
27115 };
27116
27117
27118 /**
27119  * @class Roo.Toolbar.Separator
27120  * @extends Roo.Toolbar.Item
27121  * A simple toolbar separator class
27122  * @constructor
27123  * Creates a new Separator
27124  */
27125 Roo.Toolbar.Separator = function(){
27126     var s = document.createElement("span");
27127     s.className = "ytb-sep";
27128     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27129 };
27130 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27131     enable:Roo.emptyFn,
27132     disable:Roo.emptyFn,
27133     focus:Roo.emptyFn
27134 });
27135
27136 /**
27137  * @class Roo.Toolbar.Spacer
27138  * @extends Roo.Toolbar.Item
27139  * A simple element that adds extra horizontal space to a toolbar.
27140  * @constructor
27141  * Creates a new Spacer
27142  */
27143 Roo.Toolbar.Spacer = function(){
27144     var s = document.createElement("div");
27145     s.className = "ytb-spacer";
27146     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27147 };
27148 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27149     enable:Roo.emptyFn,
27150     disable:Roo.emptyFn,
27151     focus:Roo.emptyFn
27152 });
27153
27154 /**
27155  * @class Roo.Toolbar.Fill
27156  * @extends Roo.Toolbar.Spacer
27157  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27158  * @constructor
27159  * Creates a new Spacer
27160  */
27161 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27162     // private
27163     render : function(td){
27164         td.style.width = '100%';
27165         Roo.Toolbar.Fill.superclass.render.call(this, td);
27166     }
27167 });
27168
27169 /**
27170  * @class Roo.Toolbar.TextItem
27171  * @extends Roo.Toolbar.Item
27172  * A simple class that renders text directly into a toolbar.
27173  * @constructor
27174  * Creates a new TextItem
27175  * @param {String} text
27176  */
27177 Roo.Toolbar.TextItem = function(text){
27178     if (typeof(text) == 'object') {
27179         text = text.text;
27180     }
27181     var s = document.createElement("span");
27182     s.className = "ytb-text";
27183     s.innerHTML = text;
27184     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27185 };
27186 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27187     enable:Roo.emptyFn,
27188     disable:Roo.emptyFn,
27189     focus:Roo.emptyFn
27190 });
27191
27192 /**
27193  * @class Roo.Toolbar.Button
27194  * @extends Roo.Button
27195  * A button that renders into a toolbar.
27196  * @constructor
27197  * Creates a new Button
27198  * @param {Object} config A standard {@link Roo.Button} config object
27199  */
27200 Roo.Toolbar.Button = function(config){
27201     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27202 };
27203 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27204     render : function(td){
27205         this.td = td;
27206         Roo.Toolbar.Button.superclass.render.call(this, td);
27207     },
27208     
27209     /**
27210      * Removes and destroys this button
27211      */
27212     destroy : function(){
27213         Roo.Toolbar.Button.superclass.destroy.call(this);
27214         this.td.parentNode.removeChild(this.td);
27215     },
27216     
27217     /**
27218      * Shows this button
27219      */
27220     show: function(){
27221         this.hidden = false;
27222         this.td.style.display = "";
27223     },
27224     
27225     /**
27226      * Hides this button
27227      */
27228     hide: function(){
27229         this.hidden = true;
27230         this.td.style.display = "none";
27231     },
27232
27233     /**
27234      * Disables this item
27235      */
27236     disable : function(){
27237         Roo.fly(this.td).addClass("x-item-disabled");
27238         this.disabled = true;
27239     },
27240
27241     /**
27242      * Enables this item
27243      */
27244     enable : function(){
27245         Roo.fly(this.td).removeClass("x-item-disabled");
27246         this.disabled = false;
27247     }
27248 });
27249 // backwards compat
27250 Roo.ToolbarButton = Roo.Toolbar.Button;
27251
27252 /**
27253  * @class Roo.Toolbar.SplitButton
27254  * @extends Roo.SplitButton
27255  * A menu button that renders into a toolbar.
27256  * @constructor
27257  * Creates a new SplitButton
27258  * @param {Object} config A standard {@link Roo.SplitButton} config object
27259  */
27260 Roo.Toolbar.SplitButton = function(config){
27261     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27262 };
27263 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27264     render : function(td){
27265         this.td = td;
27266         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27267     },
27268     
27269     /**
27270      * Removes and destroys this button
27271      */
27272     destroy : function(){
27273         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27274         this.td.parentNode.removeChild(this.td);
27275     },
27276     
27277     /**
27278      * Shows this button
27279      */
27280     show: function(){
27281         this.hidden = false;
27282         this.td.style.display = "";
27283     },
27284     
27285     /**
27286      * Hides this button
27287      */
27288     hide: function(){
27289         this.hidden = true;
27290         this.td.style.display = "none";
27291     }
27292 });
27293
27294 // backwards compat
27295 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27296  * Based on:
27297  * Ext JS Library 1.1.1
27298  * Copyright(c) 2006-2007, Ext JS, LLC.
27299  *
27300  * Originally Released Under LGPL - original licence link has changed is not relivant.
27301  *
27302  * Fork - LGPL
27303  * <script type="text/javascript">
27304  */
27305  
27306 /**
27307  * @class Roo.PagingToolbar
27308  * @extends Roo.Toolbar
27309  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27310  * @constructor
27311  * Create a new PagingToolbar
27312  * @param {Object} config The config object
27313  */
27314 Roo.PagingToolbar = function(el, ds, config)
27315 {
27316     // old args format still supported... - xtype is prefered..
27317     if (typeof(el) == 'object' && el.xtype) {
27318         // created from xtype...
27319         config = el;
27320         ds = el.dataSource;
27321         el = config.container;
27322     }
27323     var items = [];
27324     if (config.items) {
27325         items = config.items;
27326         config.items = [];
27327     }
27328     
27329     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27330     this.ds = ds;
27331     this.cursor = 0;
27332     this.renderButtons(this.el);
27333     this.bind(ds);
27334     
27335     // supprot items array.
27336    
27337     Roo.each(items, function(e) {
27338         this.add(Roo.factory(e));
27339     },this);
27340     
27341 };
27342
27343 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27344     /**
27345      * @cfg {Roo.data.Store} dataSource
27346      * The underlying data store providing the paged data
27347      */
27348     /**
27349      * @cfg {String/HTMLElement/Element} container
27350      * container The id or element that will contain the toolbar
27351      */
27352     /**
27353      * @cfg {Boolean} displayInfo
27354      * True to display the displayMsg (defaults to false)
27355      */
27356     /**
27357      * @cfg {Number} pageSize
27358      * The number of records to display per page (defaults to 20)
27359      */
27360     pageSize: 20,
27361     /**
27362      * @cfg {String} displayMsg
27363      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27364      */
27365     displayMsg : 'Displaying {0} - {1} of {2}',
27366     /**
27367      * @cfg {String} emptyMsg
27368      * The message to display when no records are found (defaults to "No data to display")
27369      */
27370     emptyMsg : 'No data to display',
27371     /**
27372      * Customizable piece of the default paging text (defaults to "Page")
27373      * @type String
27374      */
27375     beforePageText : "Page",
27376     /**
27377      * Customizable piece of the default paging text (defaults to "of %0")
27378      * @type String
27379      */
27380     afterPageText : "of {0}",
27381     /**
27382      * Customizable piece of the default paging text (defaults to "First Page")
27383      * @type String
27384      */
27385     firstText : "First Page",
27386     /**
27387      * Customizable piece of the default paging text (defaults to "Previous Page")
27388      * @type String
27389      */
27390     prevText : "Previous Page",
27391     /**
27392      * Customizable piece of the default paging text (defaults to "Next Page")
27393      * @type String
27394      */
27395     nextText : "Next Page",
27396     /**
27397      * Customizable piece of the default paging text (defaults to "Last Page")
27398      * @type String
27399      */
27400     lastText : "Last Page",
27401     /**
27402      * Customizable piece of the default paging text (defaults to "Refresh")
27403      * @type String
27404      */
27405     refreshText : "Refresh",
27406
27407     // private
27408     renderButtons : function(el){
27409         Roo.PagingToolbar.superclass.render.call(this, el);
27410         this.first = this.addButton({
27411             tooltip: this.firstText,
27412             cls: "x-btn-icon x-grid-page-first",
27413             disabled: true,
27414             handler: this.onClick.createDelegate(this, ["first"])
27415         });
27416         this.prev = this.addButton({
27417             tooltip: this.prevText,
27418             cls: "x-btn-icon x-grid-page-prev",
27419             disabled: true,
27420             handler: this.onClick.createDelegate(this, ["prev"])
27421         });
27422         //this.addSeparator();
27423         this.add(this.beforePageText);
27424         this.field = Roo.get(this.addDom({
27425            tag: "input",
27426            type: "text",
27427            size: "3",
27428            value: "1",
27429            cls: "x-grid-page-number"
27430         }).el);
27431         this.field.on("keydown", this.onPagingKeydown, this);
27432         this.field.on("focus", function(){this.dom.select();});
27433         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27434         this.field.setHeight(18);
27435         //this.addSeparator();
27436         this.next = this.addButton({
27437             tooltip: this.nextText,
27438             cls: "x-btn-icon x-grid-page-next",
27439             disabled: true,
27440             handler: this.onClick.createDelegate(this, ["next"])
27441         });
27442         this.last = this.addButton({
27443             tooltip: this.lastText,
27444             cls: "x-btn-icon x-grid-page-last",
27445             disabled: true,
27446             handler: this.onClick.createDelegate(this, ["last"])
27447         });
27448         //this.addSeparator();
27449         this.loading = this.addButton({
27450             tooltip: this.refreshText,
27451             cls: "x-btn-icon x-grid-loading",
27452             handler: this.onClick.createDelegate(this, ["refresh"])
27453         });
27454
27455         if(this.displayInfo){
27456             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27457         }
27458     },
27459
27460     // private
27461     updateInfo : function(){
27462         if(this.displayEl){
27463             var count = this.ds.getCount();
27464             var msg = count == 0 ?
27465                 this.emptyMsg :
27466                 String.format(
27467                     this.displayMsg,
27468                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27469                 );
27470             this.displayEl.update(msg);
27471         }
27472     },
27473
27474     // private
27475     onLoad : function(ds, r, o){
27476        this.cursor = o.params ? o.params.start : 0;
27477        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27478
27479        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27480        this.field.dom.value = ap;
27481        this.first.setDisabled(ap == 1);
27482        this.prev.setDisabled(ap == 1);
27483        this.next.setDisabled(ap == ps);
27484        this.last.setDisabled(ap == ps);
27485        this.loading.enable();
27486        this.updateInfo();
27487     },
27488
27489     // private
27490     getPageData : function(){
27491         var total = this.ds.getTotalCount();
27492         return {
27493             total : total,
27494             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27495             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27496         };
27497     },
27498
27499     // private
27500     onLoadError : function(){
27501         this.loading.enable();
27502     },
27503
27504     // private
27505     onPagingKeydown : function(e){
27506         var k = e.getKey();
27507         var d = this.getPageData();
27508         if(k == e.RETURN){
27509             var v = this.field.dom.value, pageNum;
27510             if(!v || isNaN(pageNum = parseInt(v, 10))){
27511                 this.field.dom.value = d.activePage;
27512                 return;
27513             }
27514             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27515             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27516             e.stopEvent();
27517         }
27518         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))
27519         {
27520           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27521           this.field.dom.value = pageNum;
27522           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27523           e.stopEvent();
27524         }
27525         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27526         {
27527           var v = this.field.dom.value, pageNum; 
27528           var increment = (e.shiftKey) ? 10 : 1;
27529           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27530             increment *= -1;
27531           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27532             this.field.dom.value = d.activePage;
27533             return;
27534           }
27535           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27536           {
27537             this.field.dom.value = parseInt(v, 10) + increment;
27538             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27539             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27540           }
27541           e.stopEvent();
27542         }
27543     },
27544
27545     // private
27546     beforeLoad : function(){
27547         if(this.loading){
27548             this.loading.disable();
27549         }
27550     },
27551
27552     // private
27553     onClick : function(which){
27554         var ds = this.ds;
27555         switch(which){
27556             case "first":
27557                 ds.load({params:{start: 0, limit: this.pageSize}});
27558             break;
27559             case "prev":
27560                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27561             break;
27562             case "next":
27563                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27564             break;
27565             case "last":
27566                 var total = ds.getTotalCount();
27567                 var extra = total % this.pageSize;
27568                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27569                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27570             break;
27571             case "refresh":
27572                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27573             break;
27574         }
27575     },
27576
27577     /**
27578      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27579      * @param {Roo.data.Store} store The data store to unbind
27580      */
27581     unbind : function(ds){
27582         ds.un("beforeload", this.beforeLoad, this);
27583         ds.un("load", this.onLoad, this);
27584         ds.un("loadexception", this.onLoadError, this);
27585         ds.un("remove", this.updateInfo, this);
27586         ds.un("add", this.updateInfo, this);
27587         this.ds = undefined;
27588     },
27589
27590     /**
27591      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27592      * @param {Roo.data.Store} store The data store to bind
27593      */
27594     bind : function(ds){
27595         ds.on("beforeload", this.beforeLoad, this);
27596         ds.on("load", this.onLoad, this);
27597         ds.on("loadexception", this.onLoadError, this);
27598         ds.on("remove", this.updateInfo, this);
27599         ds.on("add", this.updateInfo, this);
27600         this.ds = ds;
27601     }
27602 });/*
27603  * Based on:
27604  * Ext JS Library 1.1.1
27605  * Copyright(c) 2006-2007, Ext JS, LLC.
27606  *
27607  * Originally Released Under LGPL - original licence link has changed is not relivant.
27608  *
27609  * Fork - LGPL
27610  * <script type="text/javascript">
27611  */
27612
27613 /**
27614  * @class Roo.Resizable
27615  * @extends Roo.util.Observable
27616  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27617  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27618  * 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
27619  * the element will be wrapped for you automatically.</p>
27620  * <p>Here is the list of valid resize handles:</p>
27621  * <pre>
27622 Value   Description
27623 ------  -------------------
27624  'n'     north
27625  's'     south
27626  'e'     east
27627  'w'     west
27628  'nw'    northwest
27629  'sw'    southwest
27630  'se'    southeast
27631  'ne'    northeast
27632  'hd'    horizontal drag
27633  'all'   all
27634 </pre>
27635  * <p>Here's an example showing the creation of a typical Resizable:</p>
27636  * <pre><code>
27637 var resizer = new Roo.Resizable("element-id", {
27638     handles: 'all',
27639     minWidth: 200,
27640     minHeight: 100,
27641     maxWidth: 500,
27642     maxHeight: 400,
27643     pinned: true
27644 });
27645 resizer.on("resize", myHandler);
27646 </code></pre>
27647  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27648  * resizer.east.setDisplayed(false);</p>
27649  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27650  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27651  * resize operation's new size (defaults to [0, 0])
27652  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27653  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27654  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27655  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27656  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27657  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27658  * @cfg {Number} width The width of the element in pixels (defaults to null)
27659  * @cfg {Number} height The height of the element in pixels (defaults to null)
27660  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27661  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27662  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27663  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27664  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27665  * in favor of the handles config option (defaults to false)
27666  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27667  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27668  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27669  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27670  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27671  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27672  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27673  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27674  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27675  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27676  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27677  * @constructor
27678  * Create a new resizable component
27679  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27680  * @param {Object} config configuration options
27681   */
27682 Roo.Resizable = function(el, config)
27683 {
27684     this.el = Roo.get(el);
27685
27686     if(config && config.wrap){
27687         config.resizeChild = this.el;
27688         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27689         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27690         this.el.setStyle("overflow", "hidden");
27691         this.el.setPositioning(config.resizeChild.getPositioning());
27692         config.resizeChild.clearPositioning();
27693         if(!config.width || !config.height){
27694             var csize = config.resizeChild.getSize();
27695             this.el.setSize(csize.width, csize.height);
27696         }
27697         if(config.pinned && !config.adjustments){
27698             config.adjustments = "auto";
27699         }
27700     }
27701
27702     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27703     this.proxy.unselectable();
27704     this.proxy.enableDisplayMode('block');
27705
27706     Roo.apply(this, config);
27707
27708     if(this.pinned){
27709         this.disableTrackOver = true;
27710         this.el.addClass("x-resizable-pinned");
27711     }
27712     // if the element isn't positioned, make it relative
27713     var position = this.el.getStyle("position");
27714     if(position != "absolute" && position != "fixed"){
27715         this.el.setStyle("position", "relative");
27716     }
27717     if(!this.handles){ // no handles passed, must be legacy style
27718         this.handles = 's,e,se';
27719         if(this.multiDirectional){
27720             this.handles += ',n,w';
27721         }
27722     }
27723     if(this.handles == "all"){
27724         this.handles = "n s e w ne nw se sw";
27725     }
27726     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27727     var ps = Roo.Resizable.positions;
27728     for(var i = 0, len = hs.length; i < len; i++){
27729         if(hs[i] && ps[hs[i]]){
27730             var pos = ps[hs[i]];
27731             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27732         }
27733     }
27734     // legacy
27735     this.corner = this.southeast;
27736     
27737     // updateBox = the box can move..
27738     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27739         this.updateBox = true;
27740     }
27741
27742     this.activeHandle = null;
27743
27744     if(this.resizeChild){
27745         if(typeof this.resizeChild == "boolean"){
27746             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27747         }else{
27748             this.resizeChild = Roo.get(this.resizeChild, true);
27749         }
27750     }
27751     
27752     if(this.adjustments == "auto"){
27753         var rc = this.resizeChild;
27754         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27755         if(rc && (hw || hn)){
27756             rc.position("relative");
27757             rc.setLeft(hw ? hw.el.getWidth() : 0);
27758             rc.setTop(hn ? hn.el.getHeight() : 0);
27759         }
27760         this.adjustments = [
27761             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27762             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27763         ];
27764     }
27765
27766     if(this.draggable){
27767         this.dd = this.dynamic ?
27768             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27769         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27770     }
27771
27772     // public events
27773     this.addEvents({
27774         /**
27775          * @event beforeresize
27776          * Fired before resize is allowed. Set enabled to false to cancel resize.
27777          * @param {Roo.Resizable} this
27778          * @param {Roo.EventObject} e The mousedown event
27779          */
27780         "beforeresize" : true,
27781         /**
27782          * @event resize
27783          * Fired after a resize.
27784          * @param {Roo.Resizable} this
27785          * @param {Number} width The new width
27786          * @param {Number} height The new height
27787          * @param {Roo.EventObject} e The mouseup event
27788          */
27789         "resize" : true
27790     });
27791
27792     if(this.width !== null && this.height !== null){
27793         this.resizeTo(this.width, this.height);
27794     }else{
27795         this.updateChildSize();
27796     }
27797     if(Roo.isIE){
27798         this.el.dom.style.zoom = 1;
27799     }
27800     Roo.Resizable.superclass.constructor.call(this);
27801 };
27802
27803 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27804         resizeChild : false,
27805         adjustments : [0, 0],
27806         minWidth : 5,
27807         minHeight : 5,
27808         maxWidth : 10000,
27809         maxHeight : 10000,
27810         enabled : true,
27811         animate : false,
27812         duration : .35,
27813         dynamic : false,
27814         handles : false,
27815         multiDirectional : false,
27816         disableTrackOver : false,
27817         easing : 'easeOutStrong',
27818         widthIncrement : 0,
27819         heightIncrement : 0,
27820         pinned : false,
27821         width : null,
27822         height : null,
27823         preserveRatio : false,
27824         transparent: false,
27825         minX: 0,
27826         minY: 0,
27827         draggable: false,
27828
27829         /**
27830          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27831          */
27832         constrainTo: undefined,
27833         /**
27834          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27835          */
27836         resizeRegion: undefined,
27837
27838
27839     /**
27840      * Perform a manual resize
27841      * @param {Number} width
27842      * @param {Number} height
27843      */
27844     resizeTo : function(width, height){
27845         this.el.setSize(width, height);
27846         this.updateChildSize();
27847         this.fireEvent("resize", this, width, height, null);
27848     },
27849
27850     // private
27851     startSizing : function(e, handle){
27852         this.fireEvent("beforeresize", this, e);
27853         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27854
27855             if(!this.overlay){
27856                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27857                 this.overlay.unselectable();
27858                 this.overlay.enableDisplayMode("block");
27859                 this.overlay.on("mousemove", this.onMouseMove, this);
27860                 this.overlay.on("mouseup", this.onMouseUp, this);
27861             }
27862             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27863
27864             this.resizing = true;
27865             this.startBox = this.el.getBox();
27866             this.startPoint = e.getXY();
27867             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27868                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27869
27870             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27871             this.overlay.show();
27872
27873             if(this.constrainTo) {
27874                 var ct = Roo.get(this.constrainTo);
27875                 this.resizeRegion = ct.getRegion().adjust(
27876                     ct.getFrameWidth('t'),
27877                     ct.getFrameWidth('l'),
27878                     -ct.getFrameWidth('b'),
27879                     -ct.getFrameWidth('r')
27880                 );
27881             }
27882
27883             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27884             this.proxy.show();
27885             this.proxy.setBox(this.startBox);
27886             if(!this.dynamic){
27887                 this.proxy.setStyle('visibility', 'visible');
27888             }
27889         }
27890     },
27891
27892     // private
27893     onMouseDown : function(handle, e){
27894         if(this.enabled){
27895             e.stopEvent();
27896             this.activeHandle = handle;
27897             this.startSizing(e, handle);
27898         }
27899     },
27900
27901     // private
27902     onMouseUp : function(e){
27903         var size = this.resizeElement();
27904         this.resizing = false;
27905         this.handleOut();
27906         this.overlay.hide();
27907         this.proxy.hide();
27908         this.fireEvent("resize", this, size.width, size.height, e);
27909     },
27910
27911     // private
27912     updateChildSize : function(){
27913         if(this.resizeChild){
27914             var el = this.el;
27915             var child = this.resizeChild;
27916             var adj = this.adjustments;
27917             if(el.dom.offsetWidth){
27918                 var b = el.getSize(true);
27919                 child.setSize(b.width+adj[0], b.height+adj[1]);
27920             }
27921             // Second call here for IE
27922             // The first call enables instant resizing and
27923             // the second call corrects scroll bars if they
27924             // exist
27925             if(Roo.isIE){
27926                 setTimeout(function(){
27927                     if(el.dom.offsetWidth){
27928                         var b = el.getSize(true);
27929                         child.setSize(b.width+adj[0], b.height+adj[1]);
27930                     }
27931                 }, 10);
27932             }
27933         }
27934     },
27935
27936     // private
27937     snap : function(value, inc, min){
27938         if(!inc || !value) return value;
27939         var newValue = value;
27940         var m = value % inc;
27941         if(m > 0){
27942             if(m > (inc/2)){
27943                 newValue = value + (inc-m);
27944             }else{
27945                 newValue = value - m;
27946             }
27947         }
27948         return Math.max(min, newValue);
27949     },
27950
27951     // private
27952     resizeElement : function(){
27953         var box = this.proxy.getBox();
27954         if(this.updateBox){
27955             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27956         }else{
27957             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27958         }
27959         this.updateChildSize();
27960         if(!this.dynamic){
27961             this.proxy.hide();
27962         }
27963         return box;
27964     },
27965
27966     // private
27967     constrain : function(v, diff, m, mx){
27968         if(v - diff < m){
27969             diff = v - m;
27970         }else if(v - diff > mx){
27971             diff = mx - v;
27972         }
27973         return diff;
27974     },
27975
27976     // private
27977     onMouseMove : function(e){
27978         if(this.enabled){
27979             try{// try catch so if something goes wrong the user doesn't get hung
27980
27981             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27982                 return;
27983             }
27984
27985             //var curXY = this.startPoint;
27986             var curSize = this.curSize || this.startBox;
27987             var x = this.startBox.x, y = this.startBox.y;
27988             var ox = x, oy = y;
27989             var w = curSize.width, h = curSize.height;
27990             var ow = w, oh = h;
27991             var mw = this.minWidth, mh = this.minHeight;
27992             var mxw = this.maxWidth, mxh = this.maxHeight;
27993             var wi = this.widthIncrement;
27994             var hi = this.heightIncrement;
27995
27996             var eventXY = e.getXY();
27997             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27998             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27999
28000             var pos = this.activeHandle.position;
28001
28002             switch(pos){
28003                 case "east":
28004                     w += diffX;
28005                     w = Math.min(Math.max(mw, w), mxw);
28006                     break;
28007              
28008                 case "south":
28009                     h += diffY;
28010                     h = Math.min(Math.max(mh, h), mxh);
28011                     break;
28012                 case "southeast":
28013                     w += diffX;
28014                     h += diffY;
28015                     w = Math.min(Math.max(mw, w), mxw);
28016                     h = Math.min(Math.max(mh, h), mxh);
28017                     break;
28018                 case "north":
28019                     diffY = this.constrain(h, diffY, mh, mxh);
28020                     y += diffY;
28021                     h -= diffY;
28022                     break;
28023                 case "hdrag":
28024                     
28025                     if (wi) {
28026                         var adiffX = Math.abs(diffX);
28027                         var sub = (adiffX % wi); // how much 
28028                         if (sub > (wi/2)) { // far enough to snap
28029                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28030                         } else {
28031                             // remove difference.. 
28032                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28033                         }
28034                     }
28035                     x += diffX;
28036                     x = Math.max(this.minX, x);
28037                     break;
28038                 case "west":
28039                     diffX = this.constrain(w, diffX, mw, mxw);
28040                     x += diffX;
28041                     w -= diffX;
28042                     break;
28043                 case "northeast":
28044                     w += diffX;
28045                     w = Math.min(Math.max(mw, w), mxw);
28046                     diffY = this.constrain(h, diffY, mh, mxh);
28047                     y += diffY;
28048                     h -= diffY;
28049                     break;
28050                 case "northwest":
28051                     diffX = this.constrain(w, diffX, mw, mxw);
28052                     diffY = this.constrain(h, diffY, mh, mxh);
28053                     y += diffY;
28054                     h -= diffY;
28055                     x += diffX;
28056                     w -= diffX;
28057                     break;
28058                case "southwest":
28059                     diffX = this.constrain(w, diffX, mw, mxw);
28060                     h += diffY;
28061                     h = Math.min(Math.max(mh, h), mxh);
28062                     x += diffX;
28063                     w -= diffX;
28064                     break;
28065             }
28066
28067             var sw = this.snap(w, wi, mw);
28068             var sh = this.snap(h, hi, mh);
28069             if(sw != w || sh != h){
28070                 switch(pos){
28071                     case "northeast":
28072                         y -= sh - h;
28073                     break;
28074                     case "north":
28075                         y -= sh - h;
28076                         break;
28077                     case "southwest":
28078                         x -= sw - w;
28079                     break;
28080                     case "west":
28081                         x -= sw - w;
28082                         break;
28083                     case "northwest":
28084                         x -= sw - w;
28085                         y -= sh - h;
28086                     break;
28087                 }
28088                 w = sw;
28089                 h = sh;
28090             }
28091
28092             if(this.preserveRatio){
28093                 switch(pos){
28094                     case "southeast":
28095                     case "east":
28096                         h = oh * (w/ow);
28097                         h = Math.min(Math.max(mh, h), mxh);
28098                         w = ow * (h/oh);
28099                        break;
28100                     case "south":
28101                         w = ow * (h/oh);
28102                         w = Math.min(Math.max(mw, w), mxw);
28103                         h = oh * (w/ow);
28104                         break;
28105                     case "northeast":
28106                         w = ow * (h/oh);
28107                         w = Math.min(Math.max(mw, w), mxw);
28108                         h = oh * (w/ow);
28109                     break;
28110                     case "north":
28111                         var tw = w;
28112                         w = ow * (h/oh);
28113                         w = Math.min(Math.max(mw, w), mxw);
28114                         h = oh * (w/ow);
28115                         x += (tw - w) / 2;
28116                         break;
28117                     case "southwest":
28118                         h = oh * (w/ow);
28119                         h = Math.min(Math.max(mh, h), mxh);
28120                         var tw = w;
28121                         w = ow * (h/oh);
28122                         x += tw - w;
28123                         break;
28124                     case "west":
28125                         var th = h;
28126                         h = oh * (w/ow);
28127                         h = Math.min(Math.max(mh, h), mxh);
28128                         y += (th - h) / 2;
28129                         var tw = w;
28130                         w = ow * (h/oh);
28131                         x += tw - w;
28132                        break;
28133                     case "northwest":
28134                         var tw = w;
28135                         var th = h;
28136                         h = oh * (w/ow);
28137                         h = Math.min(Math.max(mh, h), mxh);
28138                         w = ow * (h/oh);
28139                         y += th - h;
28140                         x += tw - w;
28141                        break;
28142
28143                 }
28144             }
28145             if (pos == 'hdrag') {
28146                 w = ow;
28147             }
28148             this.proxy.setBounds(x, y, w, h);
28149             if(this.dynamic){
28150                 this.resizeElement();
28151             }
28152             }catch(e){}
28153         }
28154     },
28155
28156     // private
28157     handleOver : function(){
28158         if(this.enabled){
28159             this.el.addClass("x-resizable-over");
28160         }
28161     },
28162
28163     // private
28164     handleOut : function(){
28165         if(!this.resizing){
28166             this.el.removeClass("x-resizable-over");
28167         }
28168     },
28169
28170     /**
28171      * Returns the element this component is bound to.
28172      * @return {Roo.Element}
28173      */
28174     getEl : function(){
28175         return this.el;
28176     },
28177
28178     /**
28179      * Returns the resizeChild element (or null).
28180      * @return {Roo.Element}
28181      */
28182     getResizeChild : function(){
28183         return this.resizeChild;
28184     },
28185
28186     /**
28187      * Destroys this resizable. If the element was wrapped and
28188      * removeEl is not true then the element remains.
28189      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28190      */
28191     destroy : function(removeEl){
28192         this.proxy.remove();
28193         if(this.overlay){
28194             this.overlay.removeAllListeners();
28195             this.overlay.remove();
28196         }
28197         var ps = Roo.Resizable.positions;
28198         for(var k in ps){
28199             if(typeof ps[k] != "function" && this[ps[k]]){
28200                 var h = this[ps[k]];
28201                 h.el.removeAllListeners();
28202                 h.el.remove();
28203             }
28204         }
28205         if(removeEl){
28206             this.el.update("");
28207             this.el.remove();
28208         }
28209     }
28210 });
28211
28212 // private
28213 // hash to map config positions to true positions
28214 Roo.Resizable.positions = {
28215     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28216     hd: "hdrag"
28217 };
28218
28219 // private
28220 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28221     if(!this.tpl){
28222         // only initialize the template if resizable is used
28223         var tpl = Roo.DomHelper.createTemplate(
28224             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28225         );
28226         tpl.compile();
28227         Roo.Resizable.Handle.prototype.tpl = tpl;
28228     }
28229     this.position = pos;
28230     this.rz = rz;
28231     // show north drag fro topdra
28232     var handlepos = pos == 'hdrag' ? 'north' : pos;
28233     
28234     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28235     if (pos == 'hdrag') {
28236         this.el.setStyle('cursor', 'pointer');
28237     }
28238     this.el.unselectable();
28239     if(transparent){
28240         this.el.setOpacity(0);
28241     }
28242     this.el.on("mousedown", this.onMouseDown, this);
28243     if(!disableTrackOver){
28244         this.el.on("mouseover", this.onMouseOver, this);
28245         this.el.on("mouseout", this.onMouseOut, this);
28246     }
28247 };
28248
28249 // private
28250 Roo.Resizable.Handle.prototype = {
28251     afterResize : function(rz){
28252         // do nothing
28253     },
28254     // private
28255     onMouseDown : function(e){
28256         this.rz.onMouseDown(this, e);
28257     },
28258     // private
28259     onMouseOver : function(e){
28260         this.rz.handleOver(this, e);
28261     },
28262     // private
28263     onMouseOut : function(e){
28264         this.rz.handleOut(this, e);
28265     }
28266 };/*
28267  * Based on:
28268  * Ext JS Library 1.1.1
28269  * Copyright(c) 2006-2007, Ext JS, LLC.
28270  *
28271  * Originally Released Under LGPL - original licence link has changed is not relivant.
28272  *
28273  * Fork - LGPL
28274  * <script type="text/javascript">
28275  */
28276
28277 /**
28278  * @class Roo.Editor
28279  * @extends Roo.Component
28280  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28281  * @constructor
28282  * Create a new Editor
28283  * @param {Roo.form.Field} field The Field object (or descendant)
28284  * @param {Object} config The config object
28285  */
28286 Roo.Editor = function(field, config){
28287     Roo.Editor.superclass.constructor.call(this, config);
28288     this.field = field;
28289     this.addEvents({
28290         /**
28291              * @event beforestartedit
28292              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28293              * false from the handler of this event.
28294              * @param {Editor} this
28295              * @param {Roo.Element} boundEl The underlying element bound to this editor
28296              * @param {Mixed} value The field value being set
28297              */
28298         "beforestartedit" : true,
28299         /**
28300              * @event startedit
28301              * Fires when this editor is displayed
28302              * @param {Roo.Element} boundEl The underlying element bound to this editor
28303              * @param {Mixed} value The starting field value
28304              */
28305         "startedit" : true,
28306         /**
28307              * @event beforecomplete
28308              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28309              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28310              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28311              * event will not fire since no edit actually occurred.
28312              * @param {Editor} this
28313              * @param {Mixed} value The current field value
28314              * @param {Mixed} startValue The original field value
28315              */
28316         "beforecomplete" : true,
28317         /**
28318              * @event complete
28319              * Fires after editing is complete and any changed value has been written to the underlying field.
28320              * @param {Editor} this
28321              * @param {Mixed} value The current field value
28322              * @param {Mixed} startValue The original field value
28323              */
28324         "complete" : true,
28325         /**
28326          * @event specialkey
28327          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28328          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28329          * @param {Roo.form.Field} this
28330          * @param {Roo.EventObject} e The event object
28331          */
28332         "specialkey" : true
28333     });
28334 };
28335
28336 Roo.extend(Roo.Editor, Roo.Component, {
28337     /**
28338      * @cfg {Boolean/String} autosize
28339      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28340      * or "height" to adopt the height only (defaults to false)
28341      */
28342     /**
28343      * @cfg {Boolean} revertInvalid
28344      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28345      * validation fails (defaults to true)
28346      */
28347     /**
28348      * @cfg {Boolean} ignoreNoChange
28349      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28350      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28351      * will never be ignored.
28352      */
28353     /**
28354      * @cfg {Boolean} hideEl
28355      * False to keep the bound element visible while the editor is displayed (defaults to true)
28356      */
28357     /**
28358      * @cfg {Mixed} value
28359      * The data value of the underlying field (defaults to "")
28360      */
28361     value : "",
28362     /**
28363      * @cfg {String} alignment
28364      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28365      */
28366     alignment: "c-c?",
28367     /**
28368      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28369      * for bottom-right shadow (defaults to "frame")
28370      */
28371     shadow : "frame",
28372     /**
28373      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28374      */
28375     constrain : false,
28376     /**
28377      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28378      */
28379     completeOnEnter : false,
28380     /**
28381      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28382      */
28383     cancelOnEsc : false,
28384     /**
28385      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28386      */
28387     updateEl : false,
28388
28389     // private
28390     onRender : function(ct, position){
28391         this.el = new Roo.Layer({
28392             shadow: this.shadow,
28393             cls: "x-editor",
28394             parentEl : ct,
28395             shim : this.shim,
28396             shadowOffset:4,
28397             id: this.id,
28398             constrain: this.constrain
28399         });
28400         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28401         if(this.field.msgTarget != 'title'){
28402             this.field.msgTarget = 'qtip';
28403         }
28404         this.field.render(this.el);
28405         if(Roo.isGecko){
28406             this.field.el.dom.setAttribute('autocomplete', 'off');
28407         }
28408         this.field.on("specialkey", this.onSpecialKey, this);
28409         if(this.swallowKeys){
28410             this.field.el.swallowEvent(['keydown','keypress']);
28411         }
28412         this.field.show();
28413         this.field.on("blur", this.onBlur, this);
28414         if(this.field.grow){
28415             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28416         }
28417     },
28418
28419     onSpecialKey : function(field, e)
28420     {
28421         //Roo.log('editor onSpecialKey');
28422         if(this.completeOnEnter && e.getKey() == e.ENTER){
28423             e.stopEvent();
28424             this.completeEdit();
28425             return;
28426         }
28427         // do not fire special key otherwise it might hide close the editor...
28428         if(e.getKey() == e.ENTER){    
28429             return;
28430         }
28431         if(this.cancelOnEsc && e.getKey() == e.ESC){
28432             this.cancelEdit();
28433             return;
28434         } 
28435         this.fireEvent('specialkey', field, e);
28436     
28437     },
28438
28439     /**
28440      * Starts the editing process and shows the editor.
28441      * @param {String/HTMLElement/Element} el The element to edit
28442      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28443       * to the innerHTML of el.
28444      */
28445     startEdit : function(el, value){
28446         if(this.editing){
28447             this.completeEdit();
28448         }
28449         this.boundEl = Roo.get(el);
28450         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28451         if(!this.rendered){
28452             this.render(this.parentEl || document.body);
28453         }
28454         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28455             return;
28456         }
28457         this.startValue = v;
28458         this.field.setValue(v);
28459         if(this.autoSize){
28460             var sz = this.boundEl.getSize();
28461             switch(this.autoSize){
28462                 case "width":
28463                 this.setSize(sz.width,  "");
28464                 break;
28465                 case "height":
28466                 this.setSize("",  sz.height);
28467                 break;
28468                 default:
28469                 this.setSize(sz.width,  sz.height);
28470             }
28471         }
28472         this.el.alignTo(this.boundEl, this.alignment);
28473         this.editing = true;
28474         if(Roo.QuickTips){
28475             Roo.QuickTips.disable();
28476         }
28477         this.show();
28478     },
28479
28480     /**
28481      * Sets the height and width of this editor.
28482      * @param {Number} width The new width
28483      * @param {Number} height The new height
28484      */
28485     setSize : function(w, h){
28486         this.field.setSize(w, h);
28487         if(this.el){
28488             this.el.sync();
28489         }
28490     },
28491
28492     /**
28493      * Realigns the editor to the bound field based on the current alignment config value.
28494      */
28495     realign : function(){
28496         this.el.alignTo(this.boundEl, this.alignment);
28497     },
28498
28499     /**
28500      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28501      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28502      */
28503     completeEdit : function(remainVisible){
28504         if(!this.editing){
28505             return;
28506         }
28507         var v = this.getValue();
28508         if(this.revertInvalid !== false && !this.field.isValid()){
28509             v = this.startValue;
28510             this.cancelEdit(true);
28511         }
28512         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28513             this.editing = false;
28514             this.hide();
28515             return;
28516         }
28517         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28518             this.editing = false;
28519             if(this.updateEl && this.boundEl){
28520                 this.boundEl.update(v);
28521             }
28522             if(remainVisible !== true){
28523                 this.hide();
28524             }
28525             this.fireEvent("complete", this, v, this.startValue);
28526         }
28527     },
28528
28529     // private
28530     onShow : function(){
28531         this.el.show();
28532         if(this.hideEl !== false){
28533             this.boundEl.hide();
28534         }
28535         this.field.show();
28536         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28537             this.fixIEFocus = true;
28538             this.deferredFocus.defer(50, this);
28539         }else{
28540             this.field.focus();
28541         }
28542         this.fireEvent("startedit", this.boundEl, this.startValue);
28543     },
28544
28545     deferredFocus : function(){
28546         if(this.editing){
28547             this.field.focus();
28548         }
28549     },
28550
28551     /**
28552      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28553      * reverted to the original starting value.
28554      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28555      * cancel (defaults to false)
28556      */
28557     cancelEdit : function(remainVisible){
28558         if(this.editing){
28559             this.setValue(this.startValue);
28560             if(remainVisible !== true){
28561                 this.hide();
28562             }
28563         }
28564     },
28565
28566     // private
28567     onBlur : function(){
28568         if(this.allowBlur !== true && this.editing){
28569             this.completeEdit();
28570         }
28571     },
28572
28573     // private
28574     onHide : function(){
28575         if(this.editing){
28576             this.completeEdit();
28577             return;
28578         }
28579         this.field.blur();
28580         if(this.field.collapse){
28581             this.field.collapse();
28582         }
28583         this.el.hide();
28584         if(this.hideEl !== false){
28585             this.boundEl.show();
28586         }
28587         if(Roo.QuickTips){
28588             Roo.QuickTips.enable();
28589         }
28590     },
28591
28592     /**
28593      * Sets the data value of the editor
28594      * @param {Mixed} value Any valid value supported by the underlying field
28595      */
28596     setValue : function(v){
28597         this.field.setValue(v);
28598     },
28599
28600     /**
28601      * Gets the data value of the editor
28602      * @return {Mixed} The data value
28603      */
28604     getValue : function(){
28605         return this.field.getValue();
28606     }
28607 });/*
28608  * Based on:
28609  * Ext JS Library 1.1.1
28610  * Copyright(c) 2006-2007, Ext JS, LLC.
28611  *
28612  * Originally Released Under LGPL - original licence link has changed is not relivant.
28613  *
28614  * Fork - LGPL
28615  * <script type="text/javascript">
28616  */
28617  
28618 /**
28619  * @class Roo.BasicDialog
28620  * @extends Roo.util.Observable
28621  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28622  * <pre><code>
28623 var dlg = new Roo.BasicDialog("my-dlg", {
28624     height: 200,
28625     width: 300,
28626     minHeight: 100,
28627     minWidth: 150,
28628     modal: true,
28629     proxyDrag: true,
28630     shadow: true
28631 });
28632 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28633 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28634 dlg.addButton('Cancel', dlg.hide, dlg);
28635 dlg.show();
28636 </code></pre>
28637   <b>A Dialog should always be a direct child of the body element.</b>
28638  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28639  * @cfg {String} title Default text to display in the title bar (defaults to null)
28640  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28641  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28642  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28643  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28644  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28645  * (defaults to null with no animation)
28646  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28647  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28648  * property for valid values (defaults to 'all')
28649  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28650  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28651  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28652  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28653  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28654  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28655  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28656  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28657  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28658  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28659  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28660  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28661  * draggable = true (defaults to false)
28662  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28663  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28664  * shadow (defaults to false)
28665  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28666  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28667  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28668  * @cfg {Array} buttons Array of buttons
28669  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28670  * @constructor
28671  * Create a new BasicDialog.
28672  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28673  * @param {Object} config Configuration options
28674  */
28675 Roo.BasicDialog = function(el, config){
28676     this.el = Roo.get(el);
28677     var dh = Roo.DomHelper;
28678     if(!this.el && config && config.autoCreate){
28679         if(typeof config.autoCreate == "object"){
28680             if(!config.autoCreate.id){
28681                 config.autoCreate.id = el;
28682             }
28683             this.el = dh.append(document.body,
28684                         config.autoCreate, true);
28685         }else{
28686             this.el = dh.append(document.body,
28687                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28688         }
28689     }
28690     el = this.el;
28691     el.setDisplayed(true);
28692     el.hide = this.hideAction;
28693     this.id = el.id;
28694     el.addClass("x-dlg");
28695
28696     Roo.apply(this, config);
28697
28698     this.proxy = el.createProxy("x-dlg-proxy");
28699     this.proxy.hide = this.hideAction;
28700     this.proxy.setOpacity(.5);
28701     this.proxy.hide();
28702
28703     if(config.width){
28704         el.setWidth(config.width);
28705     }
28706     if(config.height){
28707         el.setHeight(config.height);
28708     }
28709     this.size = el.getSize();
28710     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28711         this.xy = [config.x,config.y];
28712     }else{
28713         this.xy = el.getCenterXY(true);
28714     }
28715     /** The header element @type Roo.Element */
28716     this.header = el.child("> .x-dlg-hd");
28717     /** The body element @type Roo.Element */
28718     this.body = el.child("> .x-dlg-bd");
28719     /** The footer element @type Roo.Element */
28720     this.footer = el.child("> .x-dlg-ft");
28721
28722     if(!this.header){
28723         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28724     }
28725     if(!this.body){
28726         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28727     }
28728
28729     this.header.unselectable();
28730     if(this.title){
28731         this.header.update(this.title);
28732     }
28733     // this element allows the dialog to be focused for keyboard event
28734     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28735     this.focusEl.swallowEvent("click", true);
28736
28737     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28738
28739     // wrap the body and footer for special rendering
28740     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28741     if(this.footer){
28742         this.bwrap.dom.appendChild(this.footer.dom);
28743     }
28744
28745     this.bg = this.el.createChild({
28746         tag: "div", cls:"x-dlg-bg",
28747         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28748     });
28749     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28750
28751
28752     if(this.autoScroll !== false && !this.autoTabs){
28753         this.body.setStyle("overflow", "auto");
28754     }
28755
28756     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28757
28758     if(this.closable !== false){
28759         this.el.addClass("x-dlg-closable");
28760         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28761         this.close.on("click", this.closeClick, this);
28762         this.close.addClassOnOver("x-dlg-close-over");
28763     }
28764     if(this.collapsible !== false){
28765         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28766         this.collapseBtn.on("click", this.collapseClick, this);
28767         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28768         this.header.on("dblclick", this.collapseClick, this);
28769     }
28770     if(this.resizable !== false){
28771         this.el.addClass("x-dlg-resizable");
28772         this.resizer = new Roo.Resizable(el, {
28773             minWidth: this.minWidth || 80,
28774             minHeight:this.minHeight || 80,
28775             handles: this.resizeHandles || "all",
28776             pinned: true
28777         });
28778         this.resizer.on("beforeresize", this.beforeResize, this);
28779         this.resizer.on("resize", this.onResize, this);
28780     }
28781     if(this.draggable !== false){
28782         el.addClass("x-dlg-draggable");
28783         if (!this.proxyDrag) {
28784             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28785         }
28786         else {
28787             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28788         }
28789         dd.setHandleElId(this.header.id);
28790         dd.endDrag = this.endMove.createDelegate(this);
28791         dd.startDrag = this.startMove.createDelegate(this);
28792         dd.onDrag = this.onDrag.createDelegate(this);
28793         dd.scroll = false;
28794         this.dd = dd;
28795     }
28796     if(this.modal){
28797         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28798         this.mask.enableDisplayMode("block");
28799         this.mask.hide();
28800         this.el.addClass("x-dlg-modal");
28801     }
28802     if(this.shadow){
28803         this.shadow = new Roo.Shadow({
28804             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28805             offset : this.shadowOffset
28806         });
28807     }else{
28808         this.shadowOffset = 0;
28809     }
28810     if(Roo.useShims && this.shim !== false){
28811         this.shim = this.el.createShim();
28812         this.shim.hide = this.hideAction;
28813         this.shim.hide();
28814     }else{
28815         this.shim = false;
28816     }
28817     if(this.autoTabs){
28818         this.initTabs();
28819     }
28820     if (this.buttons) { 
28821         var bts= this.buttons;
28822         this.buttons = [];
28823         Roo.each(bts, function(b) {
28824             this.addButton(b);
28825         }, this);
28826     }
28827     
28828     
28829     this.addEvents({
28830         /**
28831          * @event keydown
28832          * Fires when a key is pressed
28833          * @param {Roo.BasicDialog} this
28834          * @param {Roo.EventObject} e
28835          */
28836         "keydown" : true,
28837         /**
28838          * @event move
28839          * Fires when this dialog is moved by the user.
28840          * @param {Roo.BasicDialog} this
28841          * @param {Number} x The new page X
28842          * @param {Number} y The new page Y
28843          */
28844         "move" : true,
28845         /**
28846          * @event resize
28847          * Fires when this dialog is resized by the user.
28848          * @param {Roo.BasicDialog} this
28849          * @param {Number} width The new width
28850          * @param {Number} height The new height
28851          */
28852         "resize" : true,
28853         /**
28854          * @event beforehide
28855          * Fires before this dialog is hidden.
28856          * @param {Roo.BasicDialog} this
28857          */
28858         "beforehide" : true,
28859         /**
28860          * @event hide
28861          * Fires when this dialog is hidden.
28862          * @param {Roo.BasicDialog} this
28863          */
28864         "hide" : true,
28865         /**
28866          * @event beforeshow
28867          * Fires before this dialog is shown.
28868          * @param {Roo.BasicDialog} this
28869          */
28870         "beforeshow" : true,
28871         /**
28872          * @event show
28873          * Fires when this dialog is shown.
28874          * @param {Roo.BasicDialog} this
28875          */
28876         "show" : true
28877     });
28878     el.on("keydown", this.onKeyDown, this);
28879     el.on("mousedown", this.toFront, this);
28880     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28881     this.el.hide();
28882     Roo.DialogManager.register(this);
28883     Roo.BasicDialog.superclass.constructor.call(this);
28884 };
28885
28886 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28887     shadowOffset: Roo.isIE ? 6 : 5,
28888     minHeight: 80,
28889     minWidth: 200,
28890     minButtonWidth: 75,
28891     defaultButton: null,
28892     buttonAlign: "right",
28893     tabTag: 'div',
28894     firstShow: true,
28895
28896     /**
28897      * Sets the dialog title text
28898      * @param {String} text The title text to display
28899      * @return {Roo.BasicDialog} this
28900      */
28901     setTitle : function(text){
28902         this.header.update(text);
28903         return this;
28904     },
28905
28906     // private
28907     closeClick : function(){
28908         this.hide();
28909     },
28910
28911     // private
28912     collapseClick : function(){
28913         this[this.collapsed ? "expand" : "collapse"]();
28914     },
28915
28916     /**
28917      * Collapses the dialog to its minimized state (only the title bar is visible).
28918      * Equivalent to the user clicking the collapse dialog button.
28919      */
28920     collapse : function(){
28921         if(!this.collapsed){
28922             this.collapsed = true;
28923             this.el.addClass("x-dlg-collapsed");
28924             this.restoreHeight = this.el.getHeight();
28925             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28926         }
28927     },
28928
28929     /**
28930      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28931      * clicking the expand dialog button.
28932      */
28933     expand : function(){
28934         if(this.collapsed){
28935             this.collapsed = false;
28936             this.el.removeClass("x-dlg-collapsed");
28937             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28938         }
28939     },
28940
28941     /**
28942      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28943      * @return {Roo.TabPanel} The tabs component
28944      */
28945     initTabs : function(){
28946         var tabs = this.getTabs();
28947         while(tabs.getTab(0)){
28948             tabs.removeTab(0);
28949         }
28950         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28951             var dom = el.dom;
28952             tabs.addTab(Roo.id(dom), dom.title);
28953             dom.title = "";
28954         });
28955         tabs.activate(0);
28956         return tabs;
28957     },
28958
28959     // private
28960     beforeResize : function(){
28961         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28962     },
28963
28964     // private
28965     onResize : function(){
28966         this.refreshSize();
28967         this.syncBodyHeight();
28968         this.adjustAssets();
28969         this.focus();
28970         this.fireEvent("resize", this, this.size.width, this.size.height);
28971     },
28972
28973     // private
28974     onKeyDown : function(e){
28975         if(this.isVisible()){
28976             this.fireEvent("keydown", this, e);
28977         }
28978     },
28979
28980     /**
28981      * Resizes the dialog.
28982      * @param {Number} width
28983      * @param {Number} height
28984      * @return {Roo.BasicDialog} this
28985      */
28986     resizeTo : function(width, height){
28987         this.el.setSize(width, height);
28988         this.size = {width: width, height: height};
28989         this.syncBodyHeight();
28990         if(this.fixedcenter){
28991             this.center();
28992         }
28993         if(this.isVisible()){
28994             this.constrainXY();
28995             this.adjustAssets();
28996         }
28997         this.fireEvent("resize", this, width, height);
28998         return this;
28999     },
29000
29001
29002     /**
29003      * Resizes the dialog to fit the specified content size.
29004      * @param {Number} width
29005      * @param {Number} height
29006      * @return {Roo.BasicDialog} this
29007      */
29008     setContentSize : function(w, h){
29009         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29010         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29011         //if(!this.el.isBorderBox()){
29012             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29013             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29014         //}
29015         if(this.tabs){
29016             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29017             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29018         }
29019         this.resizeTo(w, h);
29020         return this;
29021     },
29022
29023     /**
29024      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29025      * executed in response to a particular key being pressed while the dialog is active.
29026      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29027      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29028      * @param {Function} fn The function to call
29029      * @param {Object} scope (optional) The scope of the function
29030      * @return {Roo.BasicDialog} this
29031      */
29032     addKeyListener : function(key, fn, scope){
29033         var keyCode, shift, ctrl, alt;
29034         if(typeof key == "object" && !(key instanceof Array)){
29035             keyCode = key["key"];
29036             shift = key["shift"];
29037             ctrl = key["ctrl"];
29038             alt = key["alt"];
29039         }else{
29040             keyCode = key;
29041         }
29042         var handler = function(dlg, e){
29043             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29044                 var k = e.getKey();
29045                 if(keyCode instanceof Array){
29046                     for(var i = 0, len = keyCode.length; i < len; i++){
29047                         if(keyCode[i] == k){
29048                           fn.call(scope || window, dlg, k, e);
29049                           return;
29050                         }
29051                     }
29052                 }else{
29053                     if(k == keyCode){
29054                         fn.call(scope || window, dlg, k, e);
29055                     }
29056                 }
29057             }
29058         };
29059         this.on("keydown", handler);
29060         return this;
29061     },
29062
29063     /**
29064      * Returns the TabPanel component (creates it if it doesn't exist).
29065      * Note: If you wish to simply check for the existence of tabs without creating them,
29066      * check for a null 'tabs' property.
29067      * @return {Roo.TabPanel} The tabs component
29068      */
29069     getTabs : function(){
29070         if(!this.tabs){
29071             this.el.addClass("x-dlg-auto-tabs");
29072             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29073             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29074         }
29075         return this.tabs;
29076     },
29077
29078     /**
29079      * Adds a button to the footer section of the dialog.
29080      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29081      * object or a valid Roo.DomHelper element config
29082      * @param {Function} handler The function called when the button is clicked
29083      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29084      * @return {Roo.Button} The new button
29085      */
29086     addButton : function(config, handler, scope){
29087         var dh = Roo.DomHelper;
29088         if(!this.footer){
29089             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29090         }
29091         if(!this.btnContainer){
29092             var tb = this.footer.createChild({
29093
29094                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29095                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29096             }, null, true);
29097             this.btnContainer = tb.firstChild.firstChild.firstChild;
29098         }
29099         var bconfig = {
29100             handler: handler,
29101             scope: scope,
29102             minWidth: this.minButtonWidth,
29103             hideParent:true
29104         };
29105         if(typeof config == "string"){
29106             bconfig.text = config;
29107         }else{
29108             if(config.tag){
29109                 bconfig.dhconfig = config;
29110             }else{
29111                 Roo.apply(bconfig, config);
29112             }
29113         }
29114         var fc = false;
29115         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29116             bconfig.position = Math.max(0, bconfig.position);
29117             fc = this.btnContainer.childNodes[bconfig.position];
29118         }
29119          
29120         var btn = new Roo.Button(
29121             fc ? 
29122                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29123                 : this.btnContainer.appendChild(document.createElement("td")),
29124             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29125             bconfig
29126         );
29127         this.syncBodyHeight();
29128         if(!this.buttons){
29129             /**
29130              * Array of all the buttons that have been added to this dialog via addButton
29131              * @type Array
29132              */
29133             this.buttons = [];
29134         }
29135         this.buttons.push(btn);
29136         return btn;
29137     },
29138
29139     /**
29140      * Sets the default button to be focused when the dialog is displayed.
29141      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29142      * @return {Roo.BasicDialog} this
29143      */
29144     setDefaultButton : function(btn){
29145         this.defaultButton = btn;
29146         return this;
29147     },
29148
29149     // private
29150     getHeaderFooterHeight : function(safe){
29151         var height = 0;
29152         if(this.header){
29153            height += this.header.getHeight();
29154         }
29155         if(this.footer){
29156            var fm = this.footer.getMargins();
29157             height += (this.footer.getHeight()+fm.top+fm.bottom);
29158         }
29159         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29160         height += this.centerBg.getPadding("tb");
29161         return height;
29162     },
29163
29164     // private
29165     syncBodyHeight : function(){
29166         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29167         var height = this.size.height - this.getHeaderFooterHeight(false);
29168         bd.setHeight(height-bd.getMargins("tb"));
29169         var hh = this.header.getHeight();
29170         var h = this.size.height-hh;
29171         cb.setHeight(h);
29172         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29173         bw.setHeight(h-cb.getPadding("tb"));
29174         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29175         bd.setWidth(bw.getWidth(true));
29176         if(this.tabs){
29177             this.tabs.syncHeight();
29178             if(Roo.isIE){
29179                 this.tabs.el.repaint();
29180             }
29181         }
29182     },
29183
29184     /**
29185      * Restores the previous state of the dialog if Roo.state is configured.
29186      * @return {Roo.BasicDialog} this
29187      */
29188     restoreState : function(){
29189         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29190         if(box && box.width){
29191             this.xy = [box.x, box.y];
29192             this.resizeTo(box.width, box.height);
29193         }
29194         return this;
29195     },
29196
29197     // private
29198     beforeShow : function(){
29199         this.expand();
29200         if(this.fixedcenter){
29201             this.xy = this.el.getCenterXY(true);
29202         }
29203         if(this.modal){
29204             Roo.get(document.body).addClass("x-body-masked");
29205             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29206             this.mask.show();
29207         }
29208         this.constrainXY();
29209     },
29210
29211     // private
29212     animShow : function(){
29213         var b = Roo.get(this.animateTarget).getBox();
29214         this.proxy.setSize(b.width, b.height);
29215         this.proxy.setLocation(b.x, b.y);
29216         this.proxy.show();
29217         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29218                     true, .35, this.showEl.createDelegate(this));
29219     },
29220
29221     /**
29222      * Shows the dialog.
29223      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29224      * @return {Roo.BasicDialog} this
29225      */
29226     show : function(animateTarget){
29227         if (this.fireEvent("beforeshow", this) === false){
29228             return;
29229         }
29230         if(this.syncHeightBeforeShow){
29231             this.syncBodyHeight();
29232         }else if(this.firstShow){
29233             this.firstShow = false;
29234             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29235         }
29236         this.animateTarget = animateTarget || this.animateTarget;
29237         if(!this.el.isVisible()){
29238             this.beforeShow();
29239             if(this.animateTarget && Roo.get(this.animateTarget)){
29240                 this.animShow();
29241             }else{
29242                 this.showEl();
29243             }
29244         }
29245         return this;
29246     },
29247
29248     // private
29249     showEl : function(){
29250         this.proxy.hide();
29251         this.el.setXY(this.xy);
29252         this.el.show();
29253         this.adjustAssets(true);
29254         this.toFront();
29255         this.focus();
29256         // IE peekaboo bug - fix found by Dave Fenwick
29257         if(Roo.isIE){
29258             this.el.repaint();
29259         }
29260         this.fireEvent("show", this);
29261     },
29262
29263     /**
29264      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29265      * dialog itself will receive focus.
29266      */
29267     focus : function(){
29268         if(this.defaultButton){
29269             this.defaultButton.focus();
29270         }else{
29271             this.focusEl.focus();
29272         }
29273     },
29274
29275     // private
29276     constrainXY : function(){
29277         if(this.constraintoviewport !== false){
29278             if(!this.viewSize){
29279                 if(this.container){
29280                     var s = this.container.getSize();
29281                     this.viewSize = [s.width, s.height];
29282                 }else{
29283                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29284                 }
29285             }
29286             var s = Roo.get(this.container||document).getScroll();
29287
29288             var x = this.xy[0], y = this.xy[1];
29289             var w = this.size.width, h = this.size.height;
29290             var vw = this.viewSize[0], vh = this.viewSize[1];
29291             // only move it if it needs it
29292             var moved = false;
29293             // first validate right/bottom
29294             if(x + w > vw+s.left){
29295                 x = vw - w;
29296                 moved = true;
29297             }
29298             if(y + h > vh+s.top){
29299                 y = vh - h;
29300                 moved = true;
29301             }
29302             // then make sure top/left isn't negative
29303             if(x < s.left){
29304                 x = s.left;
29305                 moved = true;
29306             }
29307             if(y < s.top){
29308                 y = s.top;
29309                 moved = true;
29310             }
29311             if(moved){
29312                 // cache xy
29313                 this.xy = [x, y];
29314                 if(this.isVisible()){
29315                     this.el.setLocation(x, y);
29316                     this.adjustAssets();
29317                 }
29318             }
29319         }
29320     },
29321
29322     // private
29323     onDrag : function(){
29324         if(!this.proxyDrag){
29325             this.xy = this.el.getXY();
29326             this.adjustAssets();
29327         }
29328     },
29329
29330     // private
29331     adjustAssets : function(doShow){
29332         var x = this.xy[0], y = this.xy[1];
29333         var w = this.size.width, h = this.size.height;
29334         if(doShow === true){
29335             if(this.shadow){
29336                 this.shadow.show(this.el);
29337             }
29338             if(this.shim){
29339                 this.shim.show();
29340             }
29341         }
29342         if(this.shadow && this.shadow.isVisible()){
29343             this.shadow.show(this.el);
29344         }
29345         if(this.shim && this.shim.isVisible()){
29346             this.shim.setBounds(x, y, w, h);
29347         }
29348     },
29349
29350     // private
29351     adjustViewport : function(w, h){
29352         if(!w || !h){
29353             w = Roo.lib.Dom.getViewWidth();
29354             h = Roo.lib.Dom.getViewHeight();
29355         }
29356         // cache the size
29357         this.viewSize = [w, h];
29358         if(this.modal && this.mask.isVisible()){
29359             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29360             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29361         }
29362         if(this.isVisible()){
29363             this.constrainXY();
29364         }
29365     },
29366
29367     /**
29368      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29369      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29370      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29371      */
29372     destroy : function(removeEl){
29373         if(this.isVisible()){
29374             this.animateTarget = null;
29375             this.hide();
29376         }
29377         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29378         if(this.tabs){
29379             this.tabs.destroy(removeEl);
29380         }
29381         Roo.destroy(
29382              this.shim,
29383              this.proxy,
29384              this.resizer,
29385              this.close,
29386              this.mask
29387         );
29388         if(this.dd){
29389             this.dd.unreg();
29390         }
29391         if(this.buttons){
29392            for(var i = 0, len = this.buttons.length; i < len; i++){
29393                this.buttons[i].destroy();
29394            }
29395         }
29396         this.el.removeAllListeners();
29397         if(removeEl === true){
29398             this.el.update("");
29399             this.el.remove();
29400         }
29401         Roo.DialogManager.unregister(this);
29402     },
29403
29404     // private
29405     startMove : function(){
29406         if(this.proxyDrag){
29407             this.proxy.show();
29408         }
29409         if(this.constraintoviewport !== false){
29410             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29411         }
29412     },
29413
29414     // private
29415     endMove : function(){
29416         if(!this.proxyDrag){
29417             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29418         }else{
29419             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29420             this.proxy.hide();
29421         }
29422         this.refreshSize();
29423         this.adjustAssets();
29424         this.focus();
29425         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29426     },
29427
29428     /**
29429      * Brings this dialog to the front of any other visible dialogs
29430      * @return {Roo.BasicDialog} this
29431      */
29432     toFront : function(){
29433         Roo.DialogManager.bringToFront(this);
29434         return this;
29435     },
29436
29437     /**
29438      * Sends this dialog to the back (under) of any other visible dialogs
29439      * @return {Roo.BasicDialog} this
29440      */
29441     toBack : function(){
29442         Roo.DialogManager.sendToBack(this);
29443         return this;
29444     },
29445
29446     /**
29447      * Centers this dialog in the viewport
29448      * @return {Roo.BasicDialog} this
29449      */
29450     center : function(){
29451         var xy = this.el.getCenterXY(true);
29452         this.moveTo(xy[0], xy[1]);
29453         return this;
29454     },
29455
29456     /**
29457      * Moves the dialog's top-left corner to the specified point
29458      * @param {Number} x
29459      * @param {Number} y
29460      * @return {Roo.BasicDialog} this
29461      */
29462     moveTo : function(x, y){
29463         this.xy = [x,y];
29464         if(this.isVisible()){
29465             this.el.setXY(this.xy);
29466             this.adjustAssets();
29467         }
29468         return this;
29469     },
29470
29471     /**
29472      * Aligns the dialog to the specified element
29473      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29474      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29475      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29476      * @return {Roo.BasicDialog} this
29477      */
29478     alignTo : function(element, position, offsets){
29479         this.xy = this.el.getAlignToXY(element, position, offsets);
29480         if(this.isVisible()){
29481             this.el.setXY(this.xy);
29482             this.adjustAssets();
29483         }
29484         return this;
29485     },
29486
29487     /**
29488      * Anchors an element to another element and realigns it when the window is resized.
29489      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29490      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29491      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29492      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29493      * is a number, it is used as the buffer delay (defaults to 50ms).
29494      * @return {Roo.BasicDialog} this
29495      */
29496     anchorTo : function(el, alignment, offsets, monitorScroll){
29497         var action = function(){
29498             this.alignTo(el, alignment, offsets);
29499         };
29500         Roo.EventManager.onWindowResize(action, this);
29501         var tm = typeof monitorScroll;
29502         if(tm != 'undefined'){
29503             Roo.EventManager.on(window, 'scroll', action, this,
29504                 {buffer: tm == 'number' ? monitorScroll : 50});
29505         }
29506         action.call(this);
29507         return this;
29508     },
29509
29510     /**
29511      * Returns true if the dialog is visible
29512      * @return {Boolean}
29513      */
29514     isVisible : function(){
29515         return this.el.isVisible();
29516     },
29517
29518     // private
29519     animHide : function(callback){
29520         var b = Roo.get(this.animateTarget).getBox();
29521         this.proxy.show();
29522         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29523         this.el.hide();
29524         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29525                     this.hideEl.createDelegate(this, [callback]));
29526     },
29527
29528     /**
29529      * Hides the dialog.
29530      * @param {Function} callback (optional) Function to call when the dialog is hidden
29531      * @return {Roo.BasicDialog} this
29532      */
29533     hide : function(callback){
29534         if (this.fireEvent("beforehide", this) === false){
29535             return;
29536         }
29537         if(this.shadow){
29538             this.shadow.hide();
29539         }
29540         if(this.shim) {
29541           this.shim.hide();
29542         }
29543         // sometimes animateTarget seems to get set.. causing problems...
29544         // this just double checks..
29545         if(this.animateTarget && Roo.get(this.animateTarget)) {
29546            this.animHide(callback);
29547         }else{
29548             this.el.hide();
29549             this.hideEl(callback);
29550         }
29551         return this;
29552     },
29553
29554     // private
29555     hideEl : function(callback){
29556         this.proxy.hide();
29557         if(this.modal){
29558             this.mask.hide();
29559             Roo.get(document.body).removeClass("x-body-masked");
29560         }
29561         this.fireEvent("hide", this);
29562         if(typeof callback == "function"){
29563             callback();
29564         }
29565     },
29566
29567     // private
29568     hideAction : function(){
29569         this.setLeft("-10000px");
29570         this.setTop("-10000px");
29571         this.setStyle("visibility", "hidden");
29572     },
29573
29574     // private
29575     refreshSize : function(){
29576         this.size = this.el.getSize();
29577         this.xy = this.el.getXY();
29578         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29579     },
29580
29581     // private
29582     // z-index is managed by the DialogManager and may be overwritten at any time
29583     setZIndex : function(index){
29584         if(this.modal){
29585             this.mask.setStyle("z-index", index);
29586         }
29587         if(this.shim){
29588             this.shim.setStyle("z-index", ++index);
29589         }
29590         if(this.shadow){
29591             this.shadow.setZIndex(++index);
29592         }
29593         this.el.setStyle("z-index", ++index);
29594         if(this.proxy){
29595             this.proxy.setStyle("z-index", ++index);
29596         }
29597         if(this.resizer){
29598             this.resizer.proxy.setStyle("z-index", ++index);
29599         }
29600
29601         this.lastZIndex = index;
29602     },
29603
29604     /**
29605      * Returns the element for this dialog
29606      * @return {Roo.Element} The underlying dialog Element
29607      */
29608     getEl : function(){
29609         return this.el;
29610     }
29611 });
29612
29613 /**
29614  * @class Roo.DialogManager
29615  * Provides global access to BasicDialogs that have been created and
29616  * support for z-indexing (layering) multiple open dialogs.
29617  */
29618 Roo.DialogManager = function(){
29619     var list = {};
29620     var accessList = [];
29621     var front = null;
29622
29623     // private
29624     var sortDialogs = function(d1, d2){
29625         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29626     };
29627
29628     // private
29629     var orderDialogs = function(){
29630         accessList.sort(sortDialogs);
29631         var seed = Roo.DialogManager.zseed;
29632         for(var i = 0, len = accessList.length; i < len; i++){
29633             var dlg = accessList[i];
29634             if(dlg){
29635                 dlg.setZIndex(seed + (i*10));
29636             }
29637         }
29638     };
29639
29640     return {
29641         /**
29642          * The starting z-index for BasicDialogs (defaults to 9000)
29643          * @type Number The z-index value
29644          */
29645         zseed : 9000,
29646
29647         // private
29648         register : function(dlg){
29649             list[dlg.id] = dlg;
29650             accessList.push(dlg);
29651         },
29652
29653         // private
29654         unregister : function(dlg){
29655             delete list[dlg.id];
29656             var i=0;
29657             var len=0;
29658             if(!accessList.indexOf){
29659                 for(  i = 0, len = accessList.length; i < len; i++){
29660                     if(accessList[i] == dlg){
29661                         accessList.splice(i, 1);
29662                         return;
29663                     }
29664                 }
29665             }else{
29666                  i = accessList.indexOf(dlg);
29667                 if(i != -1){
29668                     accessList.splice(i, 1);
29669                 }
29670             }
29671         },
29672
29673         /**
29674          * Gets a registered dialog by id
29675          * @param {String/Object} id The id of the dialog or a dialog
29676          * @return {Roo.BasicDialog} this
29677          */
29678         get : function(id){
29679             return typeof id == "object" ? id : list[id];
29680         },
29681
29682         /**
29683          * Brings the specified dialog to the front
29684          * @param {String/Object} dlg The id of the dialog or a dialog
29685          * @return {Roo.BasicDialog} this
29686          */
29687         bringToFront : function(dlg){
29688             dlg = this.get(dlg);
29689             if(dlg != front){
29690                 front = dlg;
29691                 dlg._lastAccess = new Date().getTime();
29692                 orderDialogs();
29693             }
29694             return dlg;
29695         },
29696
29697         /**
29698          * Sends the specified dialog to the back
29699          * @param {String/Object} dlg The id of the dialog or a dialog
29700          * @return {Roo.BasicDialog} this
29701          */
29702         sendToBack : function(dlg){
29703             dlg = this.get(dlg);
29704             dlg._lastAccess = -(new Date().getTime());
29705             orderDialogs();
29706             return dlg;
29707         },
29708
29709         /**
29710          * Hides all dialogs
29711          */
29712         hideAll : function(){
29713             for(var id in list){
29714                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29715                     list[id].hide();
29716                 }
29717             }
29718         }
29719     };
29720 }();
29721
29722 /**
29723  * @class Roo.LayoutDialog
29724  * @extends Roo.BasicDialog
29725  * Dialog which provides adjustments for working with a layout in a Dialog.
29726  * Add your necessary layout config options to the dialog's config.<br>
29727  * Example usage (including a nested layout):
29728  * <pre><code>
29729 if(!dialog){
29730     dialog = new Roo.LayoutDialog("download-dlg", {
29731         modal: true,
29732         width:600,
29733         height:450,
29734         shadow:true,
29735         minWidth:500,
29736         minHeight:350,
29737         autoTabs:true,
29738         proxyDrag:true,
29739         // layout config merges with the dialog config
29740         center:{
29741             tabPosition: "top",
29742             alwaysShowTabs: true
29743         }
29744     });
29745     dialog.addKeyListener(27, dialog.hide, dialog);
29746     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29747     dialog.addButton("Build It!", this.getDownload, this);
29748
29749     // we can even add nested layouts
29750     var innerLayout = new Roo.BorderLayout("dl-inner", {
29751         east: {
29752             initialSize: 200,
29753             autoScroll:true,
29754             split:true
29755         },
29756         center: {
29757             autoScroll:true
29758         }
29759     });
29760     innerLayout.beginUpdate();
29761     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29762     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29763     innerLayout.endUpdate(true);
29764
29765     var layout = dialog.getLayout();
29766     layout.beginUpdate();
29767     layout.add("center", new Roo.ContentPanel("standard-panel",
29768                         {title: "Download the Source", fitToFrame:true}));
29769     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29770                {title: "Build your own roo.js"}));
29771     layout.getRegion("center").showPanel(sp);
29772     layout.endUpdate();
29773 }
29774 </code></pre>
29775     * @constructor
29776     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29777     * @param {Object} config configuration options
29778   */
29779 Roo.LayoutDialog = function(el, cfg){
29780     
29781     var config=  cfg;
29782     if (typeof(cfg) == 'undefined') {
29783         config = Roo.apply({}, el);
29784         // not sure why we use documentElement here.. - it should always be body.
29785         // IE7 borks horribly if we use documentElement.
29786         // webkit also does not like documentElement - it creates a body element...
29787         el = Roo.get( document.body || document.documentElement ).createChild();
29788         //config.autoCreate = true;
29789     }
29790     
29791     
29792     config.autoTabs = false;
29793     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29794     this.body.setStyle({overflow:"hidden", position:"relative"});
29795     this.layout = new Roo.BorderLayout(this.body.dom, config);
29796     this.layout.monitorWindowResize = false;
29797     this.el.addClass("x-dlg-auto-layout");
29798     // fix case when center region overwrites center function
29799     this.center = Roo.BasicDialog.prototype.center;
29800     this.on("show", this.layout.layout, this.layout, true);
29801     if (config.items) {
29802         var xitems = config.items;
29803         delete config.items;
29804         Roo.each(xitems, this.addxtype, this);
29805     }
29806     
29807     
29808 };
29809 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29810     /**
29811      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29812      * @deprecated
29813      */
29814     endUpdate : function(){
29815         this.layout.endUpdate();
29816     },
29817
29818     /**
29819      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29820      *  @deprecated
29821      */
29822     beginUpdate : function(){
29823         this.layout.beginUpdate();
29824     },
29825
29826     /**
29827      * Get the BorderLayout for this dialog
29828      * @return {Roo.BorderLayout}
29829      */
29830     getLayout : function(){
29831         return this.layout;
29832     },
29833
29834     showEl : function(){
29835         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29836         if(Roo.isIE7){
29837             this.layout.layout();
29838         }
29839     },
29840
29841     // private
29842     // Use the syncHeightBeforeShow config option to control this automatically
29843     syncBodyHeight : function(){
29844         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29845         if(this.layout){this.layout.layout();}
29846     },
29847     
29848       /**
29849      * Add an xtype element (actually adds to the layout.)
29850      * @return {Object} xdata xtype object data.
29851      */
29852     
29853     addxtype : function(c) {
29854         return this.layout.addxtype(c);
29855     }
29856 });/*
29857  * Based on:
29858  * Ext JS Library 1.1.1
29859  * Copyright(c) 2006-2007, Ext JS, LLC.
29860  *
29861  * Originally Released Under LGPL - original licence link has changed is not relivant.
29862  *
29863  * Fork - LGPL
29864  * <script type="text/javascript">
29865  */
29866  
29867 /**
29868  * @class Roo.MessageBox
29869  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29870  * Example usage:
29871  *<pre><code>
29872 // Basic alert:
29873 Roo.Msg.alert('Status', 'Changes saved successfully.');
29874
29875 // Prompt for user data:
29876 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29877     if (btn == 'ok'){
29878         // process text value...
29879     }
29880 });
29881
29882 // Show a dialog using config options:
29883 Roo.Msg.show({
29884    title:'Save Changes?',
29885    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29886    buttons: Roo.Msg.YESNOCANCEL,
29887    fn: processResult,
29888    animEl: 'elId'
29889 });
29890 </code></pre>
29891  * @singleton
29892  */
29893 Roo.MessageBox = function(){
29894     var dlg, opt, mask, waitTimer;
29895     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29896     var buttons, activeTextEl, bwidth;
29897
29898     // private
29899     var handleButton = function(button){
29900         dlg.hide();
29901         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29902     };
29903
29904     // private
29905     var handleHide = function(){
29906         if(opt && opt.cls){
29907             dlg.el.removeClass(opt.cls);
29908         }
29909         if(waitTimer){
29910             Roo.TaskMgr.stop(waitTimer);
29911             waitTimer = null;
29912         }
29913     };
29914
29915     // private
29916     var updateButtons = function(b){
29917         var width = 0;
29918         if(!b){
29919             buttons["ok"].hide();
29920             buttons["cancel"].hide();
29921             buttons["yes"].hide();
29922             buttons["no"].hide();
29923             dlg.footer.dom.style.display = 'none';
29924             return width;
29925         }
29926         dlg.footer.dom.style.display = '';
29927         for(var k in buttons){
29928             if(typeof buttons[k] != "function"){
29929                 if(b[k]){
29930                     buttons[k].show();
29931                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29932                     width += buttons[k].el.getWidth()+15;
29933                 }else{
29934                     buttons[k].hide();
29935                 }
29936             }
29937         }
29938         return width;
29939     };
29940
29941     // private
29942     var handleEsc = function(d, k, e){
29943         if(opt && opt.closable !== false){
29944             dlg.hide();
29945         }
29946         if(e){
29947             e.stopEvent();
29948         }
29949     };
29950
29951     return {
29952         /**
29953          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29954          * @return {Roo.BasicDialog} The BasicDialog element
29955          */
29956         getDialog : function(){
29957            if(!dlg){
29958                 dlg = new Roo.BasicDialog("x-msg-box", {
29959                     autoCreate : true,
29960                     shadow: true,
29961                     draggable: true,
29962                     resizable:false,
29963                     constraintoviewport:false,
29964                     fixedcenter:true,
29965                     collapsible : false,
29966                     shim:true,
29967                     modal: true,
29968                     width:400, height:100,
29969                     buttonAlign:"center",
29970                     closeClick : function(){
29971                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29972                             handleButton("no");
29973                         }else{
29974                             handleButton("cancel");
29975                         }
29976                     }
29977                 });
29978                 dlg.on("hide", handleHide);
29979                 mask = dlg.mask;
29980                 dlg.addKeyListener(27, handleEsc);
29981                 buttons = {};
29982                 var bt = this.buttonText;
29983                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29984                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29985                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29986                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29987                 bodyEl = dlg.body.createChild({
29988
29989                     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>'
29990                 });
29991                 msgEl = bodyEl.dom.firstChild;
29992                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29993                 textboxEl.enableDisplayMode();
29994                 textboxEl.addKeyListener([10,13], function(){
29995                     if(dlg.isVisible() && opt && opt.buttons){
29996                         if(opt.buttons.ok){
29997                             handleButton("ok");
29998                         }else if(opt.buttons.yes){
29999                             handleButton("yes");
30000                         }
30001                     }
30002                 });
30003                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30004                 textareaEl.enableDisplayMode();
30005                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30006                 progressEl.enableDisplayMode();
30007                 var pf = progressEl.dom.firstChild;
30008                 if (pf) {
30009                     pp = Roo.get(pf.firstChild);
30010                     pp.setHeight(pf.offsetHeight);
30011                 }
30012                 
30013             }
30014             return dlg;
30015         },
30016
30017         /**
30018          * Updates the message box body text
30019          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30020          * the XHTML-compliant non-breaking space character '&amp;#160;')
30021          * @return {Roo.MessageBox} This message box
30022          */
30023         updateText : function(text){
30024             if(!dlg.isVisible() && !opt.width){
30025                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30026             }
30027             msgEl.innerHTML = text || '&#160;';
30028             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30029                         Math.max(opt.minWidth || this.minWidth, bwidth));
30030             if(opt.prompt){
30031                 activeTextEl.setWidth(w);
30032             }
30033             if(dlg.isVisible()){
30034                 dlg.fixedcenter = false;
30035             }
30036             dlg.setContentSize(w, bodyEl.getHeight());
30037             if(dlg.isVisible()){
30038                 dlg.fixedcenter = true;
30039             }
30040             return this;
30041         },
30042
30043         /**
30044          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30045          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30046          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30047          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30048          * @return {Roo.MessageBox} This message box
30049          */
30050         updateProgress : function(value, text){
30051             if(text){
30052                 this.updateText(text);
30053             }
30054             if (pp) { // weird bug on my firefox - for some reason this is not defined
30055                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30056             }
30057             return this;
30058         },        
30059
30060         /**
30061          * Returns true if the message box is currently displayed
30062          * @return {Boolean} True if the message box is visible, else false
30063          */
30064         isVisible : function(){
30065             return dlg && dlg.isVisible();  
30066         },
30067
30068         /**
30069          * Hides the message box if it is displayed
30070          */
30071         hide : function(){
30072             if(this.isVisible()){
30073                 dlg.hide();
30074             }  
30075         },
30076
30077         /**
30078          * Displays a new message box, or reinitializes an existing message box, based on the config options
30079          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30080          * The following config object properties are supported:
30081          * <pre>
30082 Property    Type             Description
30083 ----------  ---------------  ------------------------------------------------------------------------------------
30084 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30085                                    closes (defaults to undefined)
30086 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30087                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30088 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30089                                    progress and wait dialogs will ignore this property and always hide the
30090                                    close button as they can only be closed programmatically.
30091 cls               String           A custom CSS class to apply to the message box element
30092 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30093                                    displayed (defaults to 75)
30094 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30095                                    function will be btn (the name of the button that was clicked, if applicable,
30096                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30097                                    Progress and wait dialogs will ignore this option since they do not respond to
30098                                    user actions and can only be closed programmatically, so any required function
30099                                    should be called by the same code after it closes the dialog.
30100 icon              String           A CSS class that provides a background image to be used as an icon for
30101                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30102 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30103 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30104 modal             Boolean          False to allow user interaction with the page while the message box is
30105                                    displayed (defaults to true)
30106 msg               String           A string that will replace the existing message box body text (defaults
30107                                    to the XHTML-compliant non-breaking space character '&#160;')
30108 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30109 progress          Boolean          True to display a progress bar (defaults to false)
30110 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30111 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30112 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30113 title             String           The title text
30114 value             String           The string value to set into the active textbox element if displayed
30115 wait              Boolean          True to display a progress bar (defaults to false)
30116 width             Number           The width of the dialog in pixels
30117 </pre>
30118          *
30119          * Example usage:
30120          * <pre><code>
30121 Roo.Msg.show({
30122    title: 'Address',
30123    msg: 'Please enter your address:',
30124    width: 300,
30125    buttons: Roo.MessageBox.OKCANCEL,
30126    multiline: true,
30127    fn: saveAddress,
30128    animEl: 'addAddressBtn'
30129 });
30130 </code></pre>
30131          * @param {Object} config Configuration options
30132          * @return {Roo.MessageBox} This message box
30133          */
30134         show : function(options){
30135             if(this.isVisible()){
30136                 this.hide();
30137             }
30138             var d = this.getDialog();
30139             opt = options;
30140             d.setTitle(opt.title || "&#160;");
30141             d.close.setDisplayed(opt.closable !== false);
30142             activeTextEl = textboxEl;
30143             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30144             if(opt.prompt){
30145                 if(opt.multiline){
30146                     textboxEl.hide();
30147                     textareaEl.show();
30148                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30149                         opt.multiline : this.defaultTextHeight);
30150                     activeTextEl = textareaEl;
30151                 }else{
30152                     textboxEl.show();
30153                     textareaEl.hide();
30154                 }
30155             }else{
30156                 textboxEl.hide();
30157                 textareaEl.hide();
30158             }
30159             progressEl.setDisplayed(opt.progress === true);
30160             this.updateProgress(0);
30161             activeTextEl.dom.value = opt.value || "";
30162             if(opt.prompt){
30163                 dlg.setDefaultButton(activeTextEl);
30164             }else{
30165                 var bs = opt.buttons;
30166                 var db = null;
30167                 if(bs && bs.ok){
30168                     db = buttons["ok"];
30169                 }else if(bs && bs.yes){
30170                     db = buttons["yes"];
30171                 }
30172                 dlg.setDefaultButton(db);
30173             }
30174             bwidth = updateButtons(opt.buttons);
30175             this.updateText(opt.msg);
30176             if(opt.cls){
30177                 d.el.addClass(opt.cls);
30178             }
30179             d.proxyDrag = opt.proxyDrag === true;
30180             d.modal = opt.modal !== false;
30181             d.mask = opt.modal !== false ? mask : false;
30182             if(!d.isVisible()){
30183                 // force it to the end of the z-index stack so it gets a cursor in FF
30184                 document.body.appendChild(dlg.el.dom);
30185                 d.animateTarget = null;
30186                 d.show(options.animEl);
30187             }
30188             return this;
30189         },
30190
30191         /**
30192          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30193          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30194          * and closing the message box when the process is complete.
30195          * @param {String} title The title bar text
30196          * @param {String} msg The message box body text
30197          * @return {Roo.MessageBox} This message box
30198          */
30199         progress : function(title, msg){
30200             this.show({
30201                 title : title,
30202                 msg : msg,
30203                 buttons: false,
30204                 progress:true,
30205                 closable:false,
30206                 minWidth: this.minProgressWidth,
30207                 modal : true
30208             });
30209             return this;
30210         },
30211
30212         /**
30213          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30214          * If a callback function is passed it will be called after the user clicks the button, and the
30215          * id of the button that was clicked will be passed as the only parameter to the callback
30216          * (could also be the top-right close button).
30217          * @param {String} title The title bar text
30218          * @param {String} msg The message box body text
30219          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30220          * @param {Object} scope (optional) The scope of the callback function
30221          * @return {Roo.MessageBox} This message box
30222          */
30223         alert : function(title, msg, fn, scope){
30224             this.show({
30225                 title : title,
30226                 msg : msg,
30227                 buttons: this.OK,
30228                 fn: fn,
30229                 scope : scope,
30230                 modal : true
30231             });
30232             return this;
30233         },
30234
30235         /**
30236          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30237          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30238          * You are responsible for closing the message box when the process is complete.
30239          * @param {String} msg The message box body text
30240          * @param {String} title (optional) The title bar text
30241          * @return {Roo.MessageBox} This message box
30242          */
30243         wait : function(msg, title){
30244             this.show({
30245                 title : title,
30246                 msg : msg,
30247                 buttons: false,
30248                 closable:false,
30249                 progress:true,
30250                 modal:true,
30251                 width:300,
30252                 wait:true
30253             });
30254             waitTimer = Roo.TaskMgr.start({
30255                 run: function(i){
30256                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30257                 },
30258                 interval: 1000
30259             });
30260             return this;
30261         },
30262
30263         /**
30264          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30265          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30266          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30267          * @param {String} title The title bar text
30268          * @param {String} msg The message box body text
30269          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30270          * @param {Object} scope (optional) The scope of the callback function
30271          * @return {Roo.MessageBox} This message box
30272          */
30273         confirm : function(title, msg, fn, scope){
30274             this.show({
30275                 title : title,
30276                 msg : msg,
30277                 buttons: this.YESNO,
30278                 fn: fn,
30279                 scope : scope,
30280                 modal : true
30281             });
30282             return this;
30283         },
30284
30285         /**
30286          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30287          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30288          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30289          * (could also be the top-right close button) and the text that was entered will be passed as the two
30290          * parameters to the callback.
30291          * @param {String} title The title bar text
30292          * @param {String} msg The message box body text
30293          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30294          * @param {Object} scope (optional) The scope of the callback function
30295          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30296          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30297          * @return {Roo.MessageBox} This message box
30298          */
30299         prompt : function(title, msg, fn, scope, multiline){
30300             this.show({
30301                 title : title,
30302                 msg : msg,
30303                 buttons: this.OKCANCEL,
30304                 fn: fn,
30305                 minWidth:250,
30306                 scope : scope,
30307                 prompt:true,
30308                 multiline: multiline,
30309                 modal : true
30310             });
30311             return this;
30312         },
30313
30314         /**
30315          * Button config that displays a single OK button
30316          * @type Object
30317          */
30318         OK : {ok:true},
30319         /**
30320          * Button config that displays Yes and No buttons
30321          * @type Object
30322          */
30323         YESNO : {yes:true, no:true},
30324         /**
30325          * Button config that displays OK and Cancel buttons
30326          * @type Object
30327          */
30328         OKCANCEL : {ok:true, cancel:true},
30329         /**
30330          * Button config that displays Yes, No and Cancel buttons
30331          * @type Object
30332          */
30333         YESNOCANCEL : {yes:true, no:true, cancel:true},
30334
30335         /**
30336          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30337          * @type Number
30338          */
30339         defaultTextHeight : 75,
30340         /**
30341          * The maximum width in pixels of the message box (defaults to 600)
30342          * @type Number
30343          */
30344         maxWidth : 600,
30345         /**
30346          * The minimum width in pixels of the message box (defaults to 100)
30347          * @type Number
30348          */
30349         minWidth : 100,
30350         /**
30351          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30352          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30353          * @type Number
30354          */
30355         minProgressWidth : 250,
30356         /**
30357          * An object containing the default button text strings that can be overriden for localized language support.
30358          * Supported properties are: ok, cancel, yes and no.
30359          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30360          * @type Object
30361          */
30362         buttonText : {
30363             ok : "OK",
30364             cancel : "Cancel",
30365             yes : "Yes",
30366             no : "No"
30367         }
30368     };
30369 }();
30370
30371 /**
30372  * Shorthand for {@link Roo.MessageBox}
30373  */
30374 Roo.Msg = Roo.MessageBox;/*
30375  * Based on:
30376  * Ext JS Library 1.1.1
30377  * Copyright(c) 2006-2007, Ext JS, LLC.
30378  *
30379  * Originally Released Under LGPL - original licence link has changed is not relivant.
30380  *
30381  * Fork - LGPL
30382  * <script type="text/javascript">
30383  */
30384 /**
30385  * @class Roo.QuickTips
30386  * Provides attractive and customizable tooltips for any element.
30387  * @singleton
30388  */
30389 Roo.QuickTips = function(){
30390     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30391     var ce, bd, xy, dd;
30392     var visible = false, disabled = true, inited = false;
30393     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30394     
30395     var onOver = function(e){
30396         if(disabled){
30397             return;
30398         }
30399         var t = e.getTarget();
30400         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30401             return;
30402         }
30403         if(ce && t == ce.el){
30404             clearTimeout(hideProc);
30405             return;
30406         }
30407         if(t && tagEls[t.id]){
30408             tagEls[t.id].el = t;
30409             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30410             return;
30411         }
30412         var ttp, et = Roo.fly(t);
30413         var ns = cfg.namespace;
30414         if(tm.interceptTitles && t.title){
30415             ttp = t.title;
30416             t.qtip = ttp;
30417             t.removeAttribute("title");
30418             e.preventDefault();
30419         }else{
30420             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30421         }
30422         if(ttp){
30423             showProc = show.defer(tm.showDelay, tm, [{
30424                 el: t, 
30425                 text: ttp, 
30426                 width: et.getAttributeNS(ns, cfg.width),
30427                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30428                 title: et.getAttributeNS(ns, cfg.title),
30429                     cls: et.getAttributeNS(ns, cfg.cls)
30430             }]);
30431         }
30432     };
30433     
30434     var onOut = function(e){
30435         clearTimeout(showProc);
30436         var t = e.getTarget();
30437         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30438             hideProc = setTimeout(hide, tm.hideDelay);
30439         }
30440     };
30441     
30442     var onMove = function(e){
30443         if(disabled){
30444             return;
30445         }
30446         xy = e.getXY();
30447         xy[1] += 18;
30448         if(tm.trackMouse && ce){
30449             el.setXY(xy);
30450         }
30451     };
30452     
30453     var onDown = function(e){
30454         clearTimeout(showProc);
30455         clearTimeout(hideProc);
30456         if(!e.within(el)){
30457             if(tm.hideOnClick){
30458                 hide();
30459                 tm.disable();
30460                 tm.enable.defer(100, tm);
30461             }
30462         }
30463     };
30464     
30465     var getPad = function(){
30466         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30467     };
30468
30469     var show = function(o){
30470         if(disabled){
30471             return;
30472         }
30473         clearTimeout(dismissProc);
30474         ce = o;
30475         if(removeCls){ // in case manually hidden
30476             el.removeClass(removeCls);
30477             removeCls = null;
30478         }
30479         if(ce.cls){
30480             el.addClass(ce.cls);
30481             removeCls = ce.cls;
30482         }
30483         if(ce.title){
30484             tipTitle.update(ce.title);
30485             tipTitle.show();
30486         }else{
30487             tipTitle.update('');
30488             tipTitle.hide();
30489         }
30490         el.dom.style.width  = tm.maxWidth+'px';
30491         //tipBody.dom.style.width = '';
30492         tipBodyText.update(o.text);
30493         var p = getPad(), w = ce.width;
30494         if(!w){
30495             var td = tipBodyText.dom;
30496             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30497             if(aw > tm.maxWidth){
30498                 w = tm.maxWidth;
30499             }else if(aw < tm.minWidth){
30500                 w = tm.minWidth;
30501             }else{
30502                 w = aw;
30503             }
30504         }
30505         //tipBody.setWidth(w);
30506         el.setWidth(parseInt(w, 10) + p);
30507         if(ce.autoHide === false){
30508             close.setDisplayed(true);
30509             if(dd){
30510                 dd.unlock();
30511             }
30512         }else{
30513             close.setDisplayed(false);
30514             if(dd){
30515                 dd.lock();
30516             }
30517         }
30518         if(xy){
30519             el.avoidY = xy[1]-18;
30520             el.setXY(xy);
30521         }
30522         if(tm.animate){
30523             el.setOpacity(.1);
30524             el.setStyle("visibility", "visible");
30525             el.fadeIn({callback: afterShow});
30526         }else{
30527             afterShow();
30528         }
30529     };
30530     
30531     var afterShow = function(){
30532         if(ce){
30533             el.show();
30534             esc.enable();
30535             if(tm.autoDismiss && ce.autoHide !== false){
30536                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30537             }
30538         }
30539     };
30540     
30541     var hide = function(noanim){
30542         clearTimeout(dismissProc);
30543         clearTimeout(hideProc);
30544         ce = null;
30545         if(el.isVisible()){
30546             esc.disable();
30547             if(noanim !== true && tm.animate){
30548                 el.fadeOut({callback: afterHide});
30549             }else{
30550                 afterHide();
30551             } 
30552         }
30553     };
30554     
30555     var afterHide = function(){
30556         el.hide();
30557         if(removeCls){
30558             el.removeClass(removeCls);
30559             removeCls = null;
30560         }
30561     };
30562     
30563     return {
30564         /**
30565         * @cfg {Number} minWidth
30566         * The minimum width of the quick tip (defaults to 40)
30567         */
30568        minWidth : 40,
30569         /**
30570         * @cfg {Number} maxWidth
30571         * The maximum width of the quick tip (defaults to 300)
30572         */
30573        maxWidth : 300,
30574         /**
30575         * @cfg {Boolean} interceptTitles
30576         * True to automatically use the element's DOM title value if available (defaults to false)
30577         */
30578        interceptTitles : false,
30579         /**
30580         * @cfg {Boolean} trackMouse
30581         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30582         */
30583        trackMouse : false,
30584         /**
30585         * @cfg {Boolean} hideOnClick
30586         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30587         */
30588        hideOnClick : true,
30589         /**
30590         * @cfg {Number} showDelay
30591         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30592         */
30593        showDelay : 500,
30594         /**
30595         * @cfg {Number} hideDelay
30596         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30597         */
30598        hideDelay : 200,
30599         /**
30600         * @cfg {Boolean} autoHide
30601         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30602         * Used in conjunction with hideDelay.
30603         */
30604        autoHide : true,
30605         /**
30606         * @cfg {Boolean}
30607         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30608         * (defaults to true).  Used in conjunction with autoDismissDelay.
30609         */
30610        autoDismiss : true,
30611         /**
30612         * @cfg {Number}
30613         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30614         */
30615        autoDismissDelay : 5000,
30616        /**
30617         * @cfg {Boolean} animate
30618         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30619         */
30620        animate : false,
30621
30622        /**
30623         * @cfg {String} title
30624         * Title text to display (defaults to '').  This can be any valid HTML markup.
30625         */
30626         title: '',
30627        /**
30628         * @cfg {String} text
30629         * Body text to display (defaults to '').  This can be any valid HTML markup.
30630         */
30631         text : '',
30632        /**
30633         * @cfg {String} cls
30634         * A CSS class to apply to the base quick tip element (defaults to '').
30635         */
30636         cls : '',
30637        /**
30638         * @cfg {Number} width
30639         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30640         * minWidth or maxWidth.
30641         */
30642         width : null,
30643
30644     /**
30645      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30646      * or display QuickTips in a page.
30647      */
30648        init : function(){
30649           tm = Roo.QuickTips;
30650           cfg = tm.tagConfig;
30651           if(!inited){
30652               if(!Roo.isReady){ // allow calling of init() before onReady
30653                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30654                   return;
30655               }
30656               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30657               el.fxDefaults = {stopFx: true};
30658               // maximum custom styling
30659               //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>');
30660               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>');              
30661               tipTitle = el.child('h3');
30662               tipTitle.enableDisplayMode("block");
30663               tipBody = el.child('div.x-tip-bd');
30664               tipBodyText = el.child('div.x-tip-bd-inner');
30665               //bdLeft = el.child('div.x-tip-bd-left');
30666               //bdRight = el.child('div.x-tip-bd-right');
30667               close = el.child('div.x-tip-close');
30668               close.enableDisplayMode("block");
30669               close.on("click", hide);
30670               var d = Roo.get(document);
30671               d.on("mousedown", onDown);
30672               d.on("mouseover", onOver);
30673               d.on("mouseout", onOut);
30674               d.on("mousemove", onMove);
30675               esc = d.addKeyListener(27, hide);
30676               esc.disable();
30677               if(Roo.dd.DD){
30678                   dd = el.initDD("default", null, {
30679                       onDrag : function(){
30680                           el.sync();  
30681                       }
30682                   });
30683                   dd.setHandleElId(tipTitle.id);
30684                   dd.lock();
30685               }
30686               inited = true;
30687           }
30688           this.enable(); 
30689        },
30690
30691     /**
30692      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30693      * are supported:
30694      * <pre>
30695 Property    Type                   Description
30696 ----------  ---------------------  ------------------------------------------------------------------------
30697 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30698      * </ul>
30699      * @param {Object} config The config object
30700      */
30701        register : function(config){
30702            var cs = config instanceof Array ? config : arguments;
30703            for(var i = 0, len = cs.length; i < len; i++) {
30704                var c = cs[i];
30705                var target = c.target;
30706                if(target){
30707                    if(target instanceof Array){
30708                        for(var j = 0, jlen = target.length; j < jlen; j++){
30709                            tagEls[target[j]] = c;
30710                        }
30711                    }else{
30712                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30713                    }
30714                }
30715            }
30716        },
30717
30718     /**
30719      * Removes this quick tip from its element and destroys it.
30720      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30721      */
30722        unregister : function(el){
30723            delete tagEls[Roo.id(el)];
30724        },
30725
30726     /**
30727      * Enable this quick tip.
30728      */
30729        enable : function(){
30730            if(inited && disabled){
30731                locks.pop();
30732                if(locks.length < 1){
30733                    disabled = false;
30734                }
30735            }
30736        },
30737
30738     /**
30739      * Disable this quick tip.
30740      */
30741        disable : function(){
30742           disabled = true;
30743           clearTimeout(showProc);
30744           clearTimeout(hideProc);
30745           clearTimeout(dismissProc);
30746           if(ce){
30747               hide(true);
30748           }
30749           locks.push(1);
30750        },
30751
30752     /**
30753      * Returns true if the quick tip is enabled, else false.
30754      */
30755        isEnabled : function(){
30756             return !disabled;
30757        },
30758
30759         // private
30760        tagConfig : {
30761            namespace : "ext",
30762            attribute : "qtip",
30763            width : "width",
30764            target : "target",
30765            title : "qtitle",
30766            hide : "hide",
30767            cls : "qclass"
30768        }
30769    };
30770 }();
30771
30772 // backwards compat
30773 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30774  * Based on:
30775  * Ext JS Library 1.1.1
30776  * Copyright(c) 2006-2007, Ext JS, LLC.
30777  *
30778  * Originally Released Under LGPL - original licence link has changed is not relivant.
30779  *
30780  * Fork - LGPL
30781  * <script type="text/javascript">
30782  */
30783  
30784
30785 /**
30786  * @class Roo.tree.TreePanel
30787  * @extends Roo.data.Tree
30788
30789  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30790  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30791  * @cfg {Boolean} enableDD true to enable drag and drop
30792  * @cfg {Boolean} enableDrag true to enable just drag
30793  * @cfg {Boolean} enableDrop true to enable just drop
30794  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30795  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30796  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30797  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30798  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30799  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30800  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30801  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30802  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30803  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30804  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30805  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30806  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30807  * @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>
30808  * @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>
30809  * 
30810  * @constructor
30811  * @param {String/HTMLElement/Element} el The container element
30812  * @param {Object} config
30813  */
30814 Roo.tree.TreePanel = function(el, config){
30815     var root = false;
30816     var loader = false;
30817     if (config.root) {
30818         root = config.root;
30819         delete config.root;
30820     }
30821     if (config.loader) {
30822         loader = config.loader;
30823         delete config.loader;
30824     }
30825     
30826     Roo.apply(this, config);
30827     Roo.tree.TreePanel.superclass.constructor.call(this);
30828     this.el = Roo.get(el);
30829     this.el.addClass('x-tree');
30830     //console.log(root);
30831     if (root) {
30832         this.setRootNode( Roo.factory(root, Roo.tree));
30833     }
30834     if (loader) {
30835         this.loader = Roo.factory(loader, Roo.tree);
30836     }
30837    /**
30838     * Read-only. The id of the container element becomes this TreePanel's id.
30839     */
30840    this.id = this.el.id;
30841    this.addEvents({
30842         /**
30843         * @event beforeload
30844         * Fires before a node is loaded, return false to cancel
30845         * @param {Node} node The node being loaded
30846         */
30847         "beforeload" : true,
30848         /**
30849         * @event load
30850         * Fires when a node is loaded
30851         * @param {Node} node The node that was loaded
30852         */
30853         "load" : true,
30854         /**
30855         * @event textchange
30856         * Fires when the text for a node is changed
30857         * @param {Node} node The node
30858         * @param {String} text The new text
30859         * @param {String} oldText The old text
30860         */
30861         "textchange" : true,
30862         /**
30863         * @event beforeexpand
30864         * Fires before a node is expanded, return false to cancel.
30865         * @param {Node} node The node
30866         * @param {Boolean} deep
30867         * @param {Boolean} anim
30868         */
30869         "beforeexpand" : true,
30870         /**
30871         * @event beforecollapse
30872         * Fires before a node is collapsed, return false to cancel.
30873         * @param {Node} node The node
30874         * @param {Boolean} deep
30875         * @param {Boolean} anim
30876         */
30877         "beforecollapse" : true,
30878         /**
30879         * @event expand
30880         * Fires when a node is expanded
30881         * @param {Node} node The node
30882         */
30883         "expand" : true,
30884         /**
30885         * @event disabledchange
30886         * Fires when the disabled status of a node changes
30887         * @param {Node} node The node
30888         * @param {Boolean} disabled
30889         */
30890         "disabledchange" : true,
30891         /**
30892         * @event collapse
30893         * Fires when a node is collapsed
30894         * @param {Node} node The node
30895         */
30896         "collapse" : true,
30897         /**
30898         * @event beforeclick
30899         * Fires before click processing on a node. Return false to cancel the default action.
30900         * @param {Node} node The node
30901         * @param {Roo.EventObject} e The event object
30902         */
30903         "beforeclick":true,
30904         /**
30905         * @event checkchange
30906         * Fires when a node with a checkbox's checked property changes
30907         * @param {Node} this This node
30908         * @param {Boolean} checked
30909         */
30910         "checkchange":true,
30911         /**
30912         * @event click
30913         * Fires when a node is clicked
30914         * @param {Node} node The node
30915         * @param {Roo.EventObject} e The event object
30916         */
30917         "click":true,
30918         /**
30919         * @event dblclick
30920         * Fires when a node is double clicked
30921         * @param {Node} node The node
30922         * @param {Roo.EventObject} e The event object
30923         */
30924         "dblclick":true,
30925         /**
30926         * @event contextmenu
30927         * Fires when a node is right clicked
30928         * @param {Node} node The node
30929         * @param {Roo.EventObject} e The event object
30930         */
30931         "contextmenu":true,
30932         /**
30933         * @event beforechildrenrendered
30934         * Fires right before the child nodes for a node are rendered
30935         * @param {Node} node The node
30936         */
30937         "beforechildrenrendered":true,
30938        /**
30939              * @event startdrag
30940              * Fires when a node starts being dragged
30941              * @param {Roo.tree.TreePanel} this
30942              * @param {Roo.tree.TreeNode} node
30943              * @param {event} e The raw browser event
30944              */ 
30945             "startdrag" : true,
30946             /**
30947              * @event enddrag
30948              * Fires when a drag operation is complete
30949              * @param {Roo.tree.TreePanel} this
30950              * @param {Roo.tree.TreeNode} node
30951              * @param {event} e The raw browser event
30952              */
30953             "enddrag" : true,
30954             /**
30955              * @event dragdrop
30956              * Fires when a dragged node is dropped on a valid DD target
30957              * @param {Roo.tree.TreePanel} this
30958              * @param {Roo.tree.TreeNode} node
30959              * @param {DD} dd The dd it was dropped on
30960              * @param {event} e The raw browser event
30961              */
30962             "dragdrop" : true,
30963             /**
30964              * @event beforenodedrop
30965              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30966              * passed to handlers has the following properties:<br />
30967              * <ul style="padding:5px;padding-left:16px;">
30968              * <li>tree - The TreePanel</li>
30969              * <li>target - The node being targeted for the drop</li>
30970              * <li>data - The drag data from the drag source</li>
30971              * <li>point - The point of the drop - append, above or below</li>
30972              * <li>source - The drag source</li>
30973              * <li>rawEvent - Raw mouse event</li>
30974              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30975              * to be inserted by setting them on this object.</li>
30976              * <li>cancel - Set this to true to cancel the drop.</li>
30977              * </ul>
30978              * @param {Object} dropEvent
30979              */
30980             "beforenodedrop" : true,
30981             /**
30982              * @event nodedrop
30983              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30984              * passed to handlers has the following properties:<br />
30985              * <ul style="padding:5px;padding-left:16px;">
30986              * <li>tree - The TreePanel</li>
30987              * <li>target - The node being targeted for the drop</li>
30988              * <li>data - The drag data from the drag source</li>
30989              * <li>point - The point of the drop - append, above or below</li>
30990              * <li>source - The drag source</li>
30991              * <li>rawEvent - Raw mouse event</li>
30992              * <li>dropNode - Dropped node(s).</li>
30993              * </ul>
30994              * @param {Object} dropEvent
30995              */
30996             "nodedrop" : true,
30997              /**
30998              * @event nodedragover
30999              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31000              * passed to handlers has the following properties:<br />
31001              * <ul style="padding:5px;padding-left:16px;">
31002              * <li>tree - The TreePanel</li>
31003              * <li>target - The node being targeted for the drop</li>
31004              * <li>data - The drag data from the drag source</li>
31005              * <li>point - The point of the drop - append, above or below</li>
31006              * <li>source - The drag source</li>
31007              * <li>rawEvent - Raw mouse event</li>
31008              * <li>dropNode - Drop node(s) provided by the source.</li>
31009              * <li>cancel - Set this to true to signal drop not allowed.</li>
31010              * </ul>
31011              * @param {Object} dragOverEvent
31012              */
31013             "nodedragover" : true
31014         
31015    });
31016    if(this.singleExpand){
31017        this.on("beforeexpand", this.restrictExpand, this);
31018    }
31019 };
31020 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31021     rootVisible : true,
31022     animate: Roo.enableFx,
31023     lines : true,
31024     enableDD : false,
31025     hlDrop : Roo.enableFx,
31026   
31027     renderer: false,
31028     
31029     rendererTip: false,
31030     // private
31031     restrictExpand : function(node){
31032         var p = node.parentNode;
31033         if(p){
31034             if(p.expandedChild && p.expandedChild.parentNode == p){
31035                 p.expandedChild.collapse();
31036             }
31037             p.expandedChild = node;
31038         }
31039     },
31040
31041     // private override
31042     setRootNode : function(node){
31043         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31044         if(!this.rootVisible){
31045             node.ui = new Roo.tree.RootTreeNodeUI(node);
31046         }
31047         return node;
31048     },
31049
31050     /**
31051      * Returns the container element for this TreePanel
31052      */
31053     getEl : function(){
31054         return this.el;
31055     },
31056
31057     /**
31058      * Returns the default TreeLoader for this TreePanel
31059      */
31060     getLoader : function(){
31061         return this.loader;
31062     },
31063
31064     /**
31065      * Expand all nodes
31066      */
31067     expandAll : function(){
31068         this.root.expand(true);
31069     },
31070
31071     /**
31072      * Collapse all nodes
31073      */
31074     collapseAll : function(){
31075         this.root.collapse(true);
31076     },
31077
31078     /**
31079      * Returns the selection model used by this TreePanel
31080      */
31081     getSelectionModel : function(){
31082         if(!this.selModel){
31083             this.selModel = new Roo.tree.DefaultSelectionModel();
31084         }
31085         return this.selModel;
31086     },
31087
31088     /**
31089      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31090      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31091      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31092      * @return {Array}
31093      */
31094     getChecked : function(a, startNode){
31095         startNode = startNode || this.root;
31096         var r = [];
31097         var f = function(){
31098             if(this.attributes.checked){
31099                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31100             }
31101         }
31102         startNode.cascade(f);
31103         return r;
31104     },
31105
31106     /**
31107      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31108      * @param {String} path
31109      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31110      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31111      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31112      */
31113     expandPath : function(path, attr, callback){
31114         attr = attr || "id";
31115         var keys = path.split(this.pathSeparator);
31116         var curNode = this.root;
31117         if(curNode.attributes[attr] != keys[1]){ // invalid root
31118             if(callback){
31119                 callback(false, null);
31120             }
31121             return;
31122         }
31123         var index = 1;
31124         var f = function(){
31125             if(++index == keys.length){
31126                 if(callback){
31127                     callback(true, curNode);
31128                 }
31129                 return;
31130             }
31131             var c = curNode.findChild(attr, keys[index]);
31132             if(!c){
31133                 if(callback){
31134                     callback(false, curNode);
31135                 }
31136                 return;
31137             }
31138             curNode = c;
31139             c.expand(false, false, f);
31140         };
31141         curNode.expand(false, false, f);
31142     },
31143
31144     /**
31145      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31146      * @param {String} path
31147      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31148      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31149      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31150      */
31151     selectPath : function(path, attr, callback){
31152         attr = attr || "id";
31153         var keys = path.split(this.pathSeparator);
31154         var v = keys.pop();
31155         if(keys.length > 0){
31156             var f = function(success, node){
31157                 if(success && node){
31158                     var n = node.findChild(attr, v);
31159                     if(n){
31160                         n.select();
31161                         if(callback){
31162                             callback(true, n);
31163                         }
31164                     }else if(callback){
31165                         callback(false, n);
31166                     }
31167                 }else{
31168                     if(callback){
31169                         callback(false, n);
31170                     }
31171                 }
31172             };
31173             this.expandPath(keys.join(this.pathSeparator), attr, f);
31174         }else{
31175             this.root.select();
31176             if(callback){
31177                 callback(true, this.root);
31178             }
31179         }
31180     },
31181
31182     getTreeEl : function(){
31183         return this.el;
31184     },
31185
31186     /**
31187      * Trigger rendering of this TreePanel
31188      */
31189     render : function(){
31190         if (this.innerCt) {
31191             return this; // stop it rendering more than once!!
31192         }
31193         
31194         this.innerCt = this.el.createChild({tag:"ul",
31195                cls:"x-tree-root-ct " +
31196                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31197
31198         if(this.containerScroll){
31199             Roo.dd.ScrollManager.register(this.el);
31200         }
31201         if((this.enableDD || this.enableDrop) && !this.dropZone){
31202            /**
31203             * The dropZone used by this tree if drop is enabled
31204             * @type Roo.tree.TreeDropZone
31205             */
31206              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31207                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31208            });
31209         }
31210         if((this.enableDD || this.enableDrag) && !this.dragZone){
31211            /**
31212             * The dragZone used by this tree if drag is enabled
31213             * @type Roo.tree.TreeDragZone
31214             */
31215             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31216                ddGroup: this.ddGroup || "TreeDD",
31217                scroll: this.ddScroll
31218            });
31219         }
31220         this.getSelectionModel().init(this);
31221         if (!this.root) {
31222             console.log("ROOT not set in tree");
31223             return;
31224         }
31225         this.root.render();
31226         if(!this.rootVisible){
31227             this.root.renderChildren();
31228         }
31229         return this;
31230     }
31231 });/*
31232  * Based on:
31233  * Ext JS Library 1.1.1
31234  * Copyright(c) 2006-2007, Ext JS, LLC.
31235  *
31236  * Originally Released Under LGPL - original licence link has changed is not relivant.
31237  *
31238  * Fork - LGPL
31239  * <script type="text/javascript">
31240  */
31241  
31242
31243 /**
31244  * @class Roo.tree.DefaultSelectionModel
31245  * @extends Roo.util.Observable
31246  * The default single selection for a TreePanel.
31247  */
31248 Roo.tree.DefaultSelectionModel = function(){
31249    this.selNode = null;
31250    
31251    this.addEvents({
31252        /**
31253         * @event selectionchange
31254         * Fires when the selected node changes
31255         * @param {DefaultSelectionModel} this
31256         * @param {TreeNode} node the new selection
31257         */
31258        "selectionchange" : true,
31259
31260        /**
31261         * @event beforeselect
31262         * Fires before the selected node changes, return false to cancel the change
31263         * @param {DefaultSelectionModel} this
31264         * @param {TreeNode} node the new selection
31265         * @param {TreeNode} node the old selection
31266         */
31267        "beforeselect" : true
31268    });
31269 };
31270
31271 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31272     init : function(tree){
31273         this.tree = tree;
31274         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31275         tree.on("click", this.onNodeClick, this);
31276     },
31277     
31278     onNodeClick : function(node, e){
31279         if (e.ctrlKey && this.selNode == node)  {
31280             this.unselect(node);
31281             return;
31282         }
31283         this.select(node);
31284     },
31285     
31286     /**
31287      * Select a node.
31288      * @param {TreeNode} node The node to select
31289      * @return {TreeNode} The selected node
31290      */
31291     select : function(node){
31292         var last = this.selNode;
31293         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31294             if(last){
31295                 last.ui.onSelectedChange(false);
31296             }
31297             this.selNode = node;
31298             node.ui.onSelectedChange(true);
31299             this.fireEvent("selectionchange", this, node, last);
31300         }
31301         return node;
31302     },
31303     
31304     /**
31305      * Deselect a node.
31306      * @param {TreeNode} node The node to unselect
31307      */
31308     unselect : function(node){
31309         if(this.selNode == node){
31310             this.clearSelections();
31311         }    
31312     },
31313     
31314     /**
31315      * Clear all selections
31316      */
31317     clearSelections : function(){
31318         var n = this.selNode;
31319         if(n){
31320             n.ui.onSelectedChange(false);
31321             this.selNode = null;
31322             this.fireEvent("selectionchange", this, null);
31323         }
31324         return n;
31325     },
31326     
31327     /**
31328      * Get the selected node
31329      * @return {TreeNode} The selected node
31330      */
31331     getSelectedNode : function(){
31332         return this.selNode;    
31333     },
31334     
31335     /**
31336      * Returns true if the node is selected
31337      * @param {TreeNode} node The node to check
31338      * @return {Boolean}
31339      */
31340     isSelected : function(node){
31341         return this.selNode == node;  
31342     },
31343
31344     /**
31345      * Selects the node above the selected node in the tree, intelligently walking the nodes
31346      * @return TreeNode The new selection
31347      */
31348     selectPrevious : function(){
31349         var s = this.selNode || this.lastSelNode;
31350         if(!s){
31351             return null;
31352         }
31353         var ps = s.previousSibling;
31354         if(ps){
31355             if(!ps.isExpanded() || ps.childNodes.length < 1){
31356                 return this.select(ps);
31357             } else{
31358                 var lc = ps.lastChild;
31359                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31360                     lc = lc.lastChild;
31361                 }
31362                 return this.select(lc);
31363             }
31364         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31365             return this.select(s.parentNode);
31366         }
31367         return null;
31368     },
31369
31370     /**
31371      * Selects the node above the selected node in the tree, intelligently walking the nodes
31372      * @return TreeNode The new selection
31373      */
31374     selectNext : function(){
31375         var s = this.selNode || this.lastSelNode;
31376         if(!s){
31377             return null;
31378         }
31379         if(s.firstChild && s.isExpanded()){
31380              return this.select(s.firstChild);
31381          }else if(s.nextSibling){
31382              return this.select(s.nextSibling);
31383          }else if(s.parentNode){
31384             var newS = null;
31385             s.parentNode.bubble(function(){
31386                 if(this.nextSibling){
31387                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31388                     return false;
31389                 }
31390             });
31391             return newS;
31392          }
31393         return null;
31394     },
31395
31396     onKeyDown : function(e){
31397         var s = this.selNode || this.lastSelNode;
31398         // undesirable, but required
31399         var sm = this;
31400         if(!s){
31401             return;
31402         }
31403         var k = e.getKey();
31404         switch(k){
31405              case e.DOWN:
31406                  e.stopEvent();
31407                  this.selectNext();
31408              break;
31409              case e.UP:
31410                  e.stopEvent();
31411                  this.selectPrevious();
31412              break;
31413              case e.RIGHT:
31414                  e.preventDefault();
31415                  if(s.hasChildNodes()){
31416                      if(!s.isExpanded()){
31417                          s.expand();
31418                      }else if(s.firstChild){
31419                          this.select(s.firstChild, e);
31420                      }
31421                  }
31422              break;
31423              case e.LEFT:
31424                  e.preventDefault();
31425                  if(s.hasChildNodes() && s.isExpanded()){
31426                      s.collapse();
31427                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31428                      this.select(s.parentNode, e);
31429                  }
31430              break;
31431         };
31432     }
31433 });
31434
31435 /**
31436  * @class Roo.tree.MultiSelectionModel
31437  * @extends Roo.util.Observable
31438  * Multi selection for a TreePanel.
31439  */
31440 Roo.tree.MultiSelectionModel = function(){
31441    this.selNodes = [];
31442    this.selMap = {};
31443    this.addEvents({
31444        /**
31445         * @event selectionchange
31446         * Fires when the selected nodes change
31447         * @param {MultiSelectionModel} this
31448         * @param {Array} nodes Array of the selected nodes
31449         */
31450        "selectionchange" : true
31451    });
31452 };
31453
31454 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31455     init : function(tree){
31456         this.tree = tree;
31457         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31458         tree.on("click", this.onNodeClick, this);
31459     },
31460     
31461     onNodeClick : function(node, e){
31462         this.select(node, e, e.ctrlKey);
31463     },
31464     
31465     /**
31466      * Select a node.
31467      * @param {TreeNode} node The node to select
31468      * @param {EventObject} e (optional) An event associated with the selection
31469      * @param {Boolean} keepExisting True to retain existing selections
31470      * @return {TreeNode} The selected node
31471      */
31472     select : function(node, e, keepExisting){
31473         if(keepExisting !== true){
31474             this.clearSelections(true);
31475         }
31476         if(this.isSelected(node)){
31477             this.lastSelNode = node;
31478             return node;
31479         }
31480         this.selNodes.push(node);
31481         this.selMap[node.id] = node;
31482         this.lastSelNode = node;
31483         node.ui.onSelectedChange(true);
31484         this.fireEvent("selectionchange", this, this.selNodes);
31485         return node;
31486     },
31487     
31488     /**
31489      * Deselect a node.
31490      * @param {TreeNode} node The node to unselect
31491      */
31492     unselect : function(node){
31493         if(this.selMap[node.id]){
31494             node.ui.onSelectedChange(false);
31495             var sn = this.selNodes;
31496             var index = -1;
31497             if(sn.indexOf){
31498                 index = sn.indexOf(node);
31499             }else{
31500                 for(var i = 0, len = sn.length; i < len; i++){
31501                     if(sn[i] == node){
31502                         index = i;
31503                         break;
31504                     }
31505                 }
31506             }
31507             if(index != -1){
31508                 this.selNodes.splice(index, 1);
31509             }
31510             delete this.selMap[node.id];
31511             this.fireEvent("selectionchange", this, this.selNodes);
31512         }
31513     },
31514     
31515     /**
31516      * Clear all selections
31517      */
31518     clearSelections : function(suppressEvent){
31519         var sn = this.selNodes;
31520         if(sn.length > 0){
31521             for(var i = 0, len = sn.length; i < len; i++){
31522                 sn[i].ui.onSelectedChange(false);
31523             }
31524             this.selNodes = [];
31525             this.selMap = {};
31526             if(suppressEvent !== true){
31527                 this.fireEvent("selectionchange", this, this.selNodes);
31528             }
31529         }
31530     },
31531     
31532     /**
31533      * Returns true if the node is selected
31534      * @param {TreeNode} node The node to check
31535      * @return {Boolean}
31536      */
31537     isSelected : function(node){
31538         return this.selMap[node.id] ? true : false;  
31539     },
31540     
31541     /**
31542      * Returns an array of the selected nodes
31543      * @return {Array}
31544      */
31545     getSelectedNodes : function(){
31546         return this.selNodes;    
31547     },
31548
31549     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31550
31551     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31552
31553     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31554 });/*
31555  * Based on:
31556  * Ext JS Library 1.1.1
31557  * Copyright(c) 2006-2007, Ext JS, LLC.
31558  *
31559  * Originally Released Under LGPL - original licence link has changed is not relivant.
31560  *
31561  * Fork - LGPL
31562  * <script type="text/javascript">
31563  */
31564  
31565 /**
31566  * @class Roo.tree.TreeNode
31567  * @extends Roo.data.Node
31568  * @cfg {String} text The text for this node
31569  * @cfg {Boolean} expanded true to start the node expanded
31570  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31571  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31572  * @cfg {Boolean} disabled true to start the node disabled
31573  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31574  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31575  * @cfg {String} cls A css class to be added to the node
31576  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31577  * @cfg {String} href URL of the link used for the node (defaults to #)
31578  * @cfg {String} hrefTarget target frame for the link
31579  * @cfg {String} qtip An Ext QuickTip for the node
31580  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31581  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31582  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31583  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31584  * (defaults to undefined with no checkbox rendered)
31585  * @constructor
31586  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31587  */
31588 Roo.tree.TreeNode = function(attributes){
31589     attributes = attributes || {};
31590     if(typeof attributes == "string"){
31591         attributes = {text: attributes};
31592     }
31593     this.childrenRendered = false;
31594     this.rendered = false;
31595     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31596     this.expanded = attributes.expanded === true;
31597     this.isTarget = attributes.isTarget !== false;
31598     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31599     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31600
31601     /**
31602      * Read-only. The text for this node. To change it use setText().
31603      * @type String
31604      */
31605     this.text = attributes.text;
31606     /**
31607      * True if this node is disabled.
31608      * @type Boolean
31609      */
31610     this.disabled = attributes.disabled === true;
31611
31612     this.addEvents({
31613         /**
31614         * @event textchange
31615         * Fires when the text for this node is changed
31616         * @param {Node} this This node
31617         * @param {String} text The new text
31618         * @param {String} oldText The old text
31619         */
31620         "textchange" : true,
31621         /**
31622         * @event beforeexpand
31623         * Fires before this node is expanded, return false to cancel.
31624         * @param {Node} this This node
31625         * @param {Boolean} deep
31626         * @param {Boolean} anim
31627         */
31628         "beforeexpand" : true,
31629         /**
31630         * @event beforecollapse
31631         * Fires before this node is collapsed, return false to cancel.
31632         * @param {Node} this This node
31633         * @param {Boolean} deep
31634         * @param {Boolean} anim
31635         */
31636         "beforecollapse" : true,
31637         /**
31638         * @event expand
31639         * Fires when this node is expanded
31640         * @param {Node} this This node
31641         */
31642         "expand" : true,
31643         /**
31644         * @event disabledchange
31645         * Fires when the disabled status of this node changes
31646         * @param {Node} this This node
31647         * @param {Boolean} disabled
31648         */
31649         "disabledchange" : true,
31650         /**
31651         * @event collapse
31652         * Fires when this node is collapsed
31653         * @param {Node} this This node
31654         */
31655         "collapse" : true,
31656         /**
31657         * @event beforeclick
31658         * Fires before click processing. Return false to cancel the default action.
31659         * @param {Node} this This node
31660         * @param {Roo.EventObject} e The event object
31661         */
31662         "beforeclick":true,
31663         /**
31664         * @event checkchange
31665         * Fires when a node with a checkbox's checked property changes
31666         * @param {Node} this This node
31667         * @param {Boolean} checked
31668         */
31669         "checkchange":true,
31670         /**
31671         * @event click
31672         * Fires when this node is clicked
31673         * @param {Node} this This node
31674         * @param {Roo.EventObject} e The event object
31675         */
31676         "click":true,
31677         /**
31678         * @event dblclick
31679         * Fires when this node is double clicked
31680         * @param {Node} this This node
31681         * @param {Roo.EventObject} e The event object
31682         */
31683         "dblclick":true,
31684         /**
31685         * @event contextmenu
31686         * Fires when this node is right clicked
31687         * @param {Node} this This node
31688         * @param {Roo.EventObject} e The event object
31689         */
31690         "contextmenu":true,
31691         /**
31692         * @event beforechildrenrendered
31693         * Fires right before the child nodes for this node are rendered
31694         * @param {Node} this This node
31695         */
31696         "beforechildrenrendered":true
31697     });
31698
31699     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31700
31701     /**
31702      * Read-only. The UI for this node
31703      * @type TreeNodeUI
31704      */
31705     this.ui = new uiClass(this);
31706 };
31707 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31708     preventHScroll: true,
31709     /**
31710      * Returns true if this node is expanded
31711      * @return {Boolean}
31712      */
31713     isExpanded : function(){
31714         return this.expanded;
31715     },
31716
31717     /**
31718      * Returns the UI object for this node
31719      * @return {TreeNodeUI}
31720      */
31721     getUI : function(){
31722         return this.ui;
31723     },
31724
31725     // private override
31726     setFirstChild : function(node){
31727         var of = this.firstChild;
31728         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31729         if(this.childrenRendered && of && node != of){
31730             of.renderIndent(true, true);
31731         }
31732         if(this.rendered){
31733             this.renderIndent(true, true);
31734         }
31735     },
31736
31737     // private override
31738     setLastChild : function(node){
31739         var ol = this.lastChild;
31740         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31741         if(this.childrenRendered && ol && node != ol){
31742             ol.renderIndent(true, true);
31743         }
31744         if(this.rendered){
31745             this.renderIndent(true, true);
31746         }
31747     },
31748
31749     // these methods are overridden to provide lazy rendering support
31750     // private override
31751     appendChild : function(){
31752         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31753         if(node && this.childrenRendered){
31754             node.render();
31755         }
31756         this.ui.updateExpandIcon();
31757         return node;
31758     },
31759
31760     // private override
31761     removeChild : function(node){
31762         this.ownerTree.getSelectionModel().unselect(node);
31763         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31764         // if it's been rendered remove dom node
31765         if(this.childrenRendered){
31766             node.ui.remove();
31767         }
31768         if(this.childNodes.length < 1){
31769             this.collapse(false, false);
31770         }else{
31771             this.ui.updateExpandIcon();
31772         }
31773         if(!this.firstChild) {
31774             this.childrenRendered = false;
31775         }
31776         return node;
31777     },
31778
31779     // private override
31780     insertBefore : function(node, refNode){
31781         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31782         if(newNode && refNode && this.childrenRendered){
31783             node.render();
31784         }
31785         this.ui.updateExpandIcon();
31786         return newNode;
31787     },
31788
31789     /**
31790      * Sets the text for this node
31791      * @param {String} text
31792      */
31793     setText : function(text){
31794         var oldText = this.text;
31795         this.text = text;
31796         this.attributes.text = text;
31797         if(this.rendered){ // event without subscribing
31798             this.ui.onTextChange(this, text, oldText);
31799         }
31800         this.fireEvent("textchange", this, text, oldText);
31801     },
31802
31803     /**
31804      * Triggers selection of this node
31805      */
31806     select : function(){
31807         this.getOwnerTree().getSelectionModel().select(this);
31808     },
31809
31810     /**
31811      * Triggers deselection of this node
31812      */
31813     unselect : function(){
31814         this.getOwnerTree().getSelectionModel().unselect(this);
31815     },
31816
31817     /**
31818      * Returns true if this node is selected
31819      * @return {Boolean}
31820      */
31821     isSelected : function(){
31822         return this.getOwnerTree().getSelectionModel().isSelected(this);
31823     },
31824
31825     /**
31826      * Expand this node.
31827      * @param {Boolean} deep (optional) True to expand all children as well
31828      * @param {Boolean} anim (optional) false to cancel the default animation
31829      * @param {Function} callback (optional) A callback to be called when
31830      * expanding this node completes (does not wait for deep expand to complete).
31831      * Called with 1 parameter, this node.
31832      */
31833     expand : function(deep, anim, callback){
31834         if(!this.expanded){
31835             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31836                 return;
31837             }
31838             if(!this.childrenRendered){
31839                 this.renderChildren();
31840             }
31841             this.expanded = true;
31842             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31843                 this.ui.animExpand(function(){
31844                     this.fireEvent("expand", this);
31845                     if(typeof callback == "function"){
31846                         callback(this);
31847                     }
31848                     if(deep === true){
31849                         this.expandChildNodes(true);
31850                     }
31851                 }.createDelegate(this));
31852                 return;
31853             }else{
31854                 this.ui.expand();
31855                 this.fireEvent("expand", this);
31856                 if(typeof callback == "function"){
31857                     callback(this);
31858                 }
31859             }
31860         }else{
31861            if(typeof callback == "function"){
31862                callback(this);
31863            }
31864         }
31865         if(deep === true){
31866             this.expandChildNodes(true);
31867         }
31868     },
31869
31870     isHiddenRoot : function(){
31871         return this.isRoot && !this.getOwnerTree().rootVisible;
31872     },
31873
31874     /**
31875      * Collapse this node.
31876      * @param {Boolean} deep (optional) True to collapse all children as well
31877      * @param {Boolean} anim (optional) false to cancel the default animation
31878      */
31879     collapse : function(deep, anim){
31880         if(this.expanded && !this.isHiddenRoot()){
31881             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31882                 return;
31883             }
31884             this.expanded = false;
31885             if((this.getOwnerTree().animate && anim !== false) || anim){
31886                 this.ui.animCollapse(function(){
31887                     this.fireEvent("collapse", this);
31888                     if(deep === true){
31889                         this.collapseChildNodes(true);
31890                     }
31891                 }.createDelegate(this));
31892                 return;
31893             }else{
31894                 this.ui.collapse();
31895                 this.fireEvent("collapse", this);
31896             }
31897         }
31898         if(deep === true){
31899             var cs = this.childNodes;
31900             for(var i = 0, len = cs.length; i < len; i++) {
31901                 cs[i].collapse(true, false);
31902             }
31903         }
31904     },
31905
31906     // private
31907     delayedExpand : function(delay){
31908         if(!this.expandProcId){
31909             this.expandProcId = this.expand.defer(delay, this);
31910         }
31911     },
31912
31913     // private
31914     cancelExpand : function(){
31915         if(this.expandProcId){
31916             clearTimeout(this.expandProcId);
31917         }
31918         this.expandProcId = false;
31919     },
31920
31921     /**
31922      * Toggles expanded/collapsed state of the node
31923      */
31924     toggle : function(){
31925         if(this.expanded){
31926             this.collapse();
31927         }else{
31928             this.expand();
31929         }
31930     },
31931
31932     /**
31933      * Ensures all parent nodes are expanded
31934      */
31935     ensureVisible : function(callback){
31936         var tree = this.getOwnerTree();
31937         tree.expandPath(this.parentNode.getPath(), false, function(){
31938             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31939             Roo.callback(callback);
31940         }.createDelegate(this));
31941     },
31942
31943     /**
31944      * Expand all child nodes
31945      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31946      */
31947     expandChildNodes : function(deep){
31948         var cs = this.childNodes;
31949         for(var i = 0, len = cs.length; i < len; i++) {
31950                 cs[i].expand(deep);
31951         }
31952     },
31953
31954     /**
31955      * Collapse all child nodes
31956      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31957      */
31958     collapseChildNodes : function(deep){
31959         var cs = this.childNodes;
31960         for(var i = 0, len = cs.length; i < len; i++) {
31961                 cs[i].collapse(deep);
31962         }
31963     },
31964
31965     /**
31966      * Disables this node
31967      */
31968     disable : function(){
31969         this.disabled = true;
31970         this.unselect();
31971         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31972             this.ui.onDisableChange(this, true);
31973         }
31974         this.fireEvent("disabledchange", this, true);
31975     },
31976
31977     /**
31978      * Enables this node
31979      */
31980     enable : function(){
31981         this.disabled = false;
31982         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31983             this.ui.onDisableChange(this, false);
31984         }
31985         this.fireEvent("disabledchange", this, false);
31986     },
31987
31988     // private
31989     renderChildren : function(suppressEvent){
31990         if(suppressEvent !== false){
31991             this.fireEvent("beforechildrenrendered", this);
31992         }
31993         var cs = this.childNodes;
31994         for(var i = 0, len = cs.length; i < len; i++){
31995             cs[i].render(true);
31996         }
31997         this.childrenRendered = true;
31998     },
31999
32000     // private
32001     sort : function(fn, scope){
32002         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32003         if(this.childrenRendered){
32004             var cs = this.childNodes;
32005             for(var i = 0, len = cs.length; i < len; i++){
32006                 cs[i].render(true);
32007             }
32008         }
32009     },
32010
32011     // private
32012     render : function(bulkRender){
32013         this.ui.render(bulkRender);
32014         if(!this.rendered){
32015             this.rendered = true;
32016             if(this.expanded){
32017                 this.expanded = false;
32018                 this.expand(false, false);
32019             }
32020         }
32021     },
32022
32023     // private
32024     renderIndent : function(deep, refresh){
32025         if(refresh){
32026             this.ui.childIndent = null;
32027         }
32028         this.ui.renderIndent();
32029         if(deep === true && this.childrenRendered){
32030             var cs = this.childNodes;
32031             for(var i = 0, len = cs.length; i < len; i++){
32032                 cs[i].renderIndent(true, refresh);
32033             }
32034         }
32035     }
32036 });/*
32037  * Based on:
32038  * Ext JS Library 1.1.1
32039  * Copyright(c) 2006-2007, Ext JS, LLC.
32040  *
32041  * Originally Released Under LGPL - original licence link has changed is not relivant.
32042  *
32043  * Fork - LGPL
32044  * <script type="text/javascript">
32045  */
32046  
32047 /**
32048  * @class Roo.tree.AsyncTreeNode
32049  * @extends Roo.tree.TreeNode
32050  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32051  * @constructor
32052  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32053  */
32054  Roo.tree.AsyncTreeNode = function(config){
32055     this.loaded = false;
32056     this.loading = false;
32057     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32058     /**
32059     * @event beforeload
32060     * Fires before this node is loaded, return false to cancel
32061     * @param {Node} this This node
32062     */
32063     this.addEvents({'beforeload':true, 'load': true});
32064     /**
32065     * @event load
32066     * Fires when this node is loaded
32067     * @param {Node} this This node
32068     */
32069     /**
32070      * The loader used by this node (defaults to using the tree's defined loader)
32071      * @type TreeLoader
32072      * @property loader
32073      */
32074 };
32075 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32076     expand : function(deep, anim, callback){
32077         if(this.loading){ // if an async load is already running, waiting til it's done
32078             var timer;
32079             var f = function(){
32080                 if(!this.loading){ // done loading
32081                     clearInterval(timer);
32082                     this.expand(deep, anim, callback);
32083                 }
32084             }.createDelegate(this);
32085             timer = setInterval(f, 200);
32086             return;
32087         }
32088         if(!this.loaded){
32089             if(this.fireEvent("beforeload", this) === false){
32090                 return;
32091             }
32092             this.loading = true;
32093             this.ui.beforeLoad(this);
32094             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32095             if(loader){
32096                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32097                 return;
32098             }
32099         }
32100         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32101     },
32102     
32103     /**
32104      * Returns true if this node is currently loading
32105      * @return {Boolean}
32106      */
32107     isLoading : function(){
32108         return this.loading;  
32109     },
32110     
32111     loadComplete : function(deep, anim, callback){
32112         this.loading = false;
32113         this.loaded = true;
32114         this.ui.afterLoad(this);
32115         this.fireEvent("load", this);
32116         this.expand(deep, anim, callback);
32117     },
32118     
32119     /**
32120      * Returns true if this node has been loaded
32121      * @return {Boolean}
32122      */
32123     isLoaded : function(){
32124         return this.loaded;
32125     },
32126     
32127     hasChildNodes : function(){
32128         if(!this.isLeaf() && !this.loaded){
32129             return true;
32130         }else{
32131             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32132         }
32133     },
32134
32135     /**
32136      * Trigger a reload for this node
32137      * @param {Function} callback
32138      */
32139     reload : function(callback){
32140         this.collapse(false, false);
32141         while(this.firstChild){
32142             this.removeChild(this.firstChild);
32143         }
32144         this.childrenRendered = false;
32145         this.loaded = false;
32146         if(this.isHiddenRoot()){
32147             this.expanded = false;
32148         }
32149         this.expand(false, false, callback);
32150     }
32151 });/*
32152  * Based on:
32153  * Ext JS Library 1.1.1
32154  * Copyright(c) 2006-2007, Ext JS, LLC.
32155  *
32156  * Originally Released Under LGPL - original licence link has changed is not relivant.
32157  *
32158  * Fork - LGPL
32159  * <script type="text/javascript">
32160  */
32161  
32162 /**
32163  * @class Roo.tree.TreeNodeUI
32164  * @constructor
32165  * @param {Object} node The node to render
32166  * The TreeNode UI implementation is separate from the
32167  * tree implementation. Unless you are customizing the tree UI,
32168  * you should never have to use this directly.
32169  */
32170 Roo.tree.TreeNodeUI = function(node){
32171     this.node = node;
32172     this.rendered = false;
32173     this.animating = false;
32174     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32175 };
32176
32177 Roo.tree.TreeNodeUI.prototype = {
32178     removeChild : function(node){
32179         if(this.rendered){
32180             this.ctNode.removeChild(node.ui.getEl());
32181         }
32182     },
32183
32184     beforeLoad : function(){
32185          this.addClass("x-tree-node-loading");
32186     },
32187
32188     afterLoad : function(){
32189          this.removeClass("x-tree-node-loading");
32190     },
32191
32192     onTextChange : function(node, text, oldText){
32193         if(this.rendered){
32194             this.textNode.innerHTML = text;
32195         }
32196     },
32197
32198     onDisableChange : function(node, state){
32199         this.disabled = state;
32200         if(state){
32201             this.addClass("x-tree-node-disabled");
32202         }else{
32203             this.removeClass("x-tree-node-disabled");
32204         }
32205     },
32206
32207     onSelectedChange : function(state){
32208         if(state){
32209             this.focus();
32210             this.addClass("x-tree-selected");
32211         }else{
32212             //this.blur();
32213             this.removeClass("x-tree-selected");
32214         }
32215     },
32216
32217     onMove : function(tree, node, oldParent, newParent, index, refNode){
32218         this.childIndent = null;
32219         if(this.rendered){
32220             var targetNode = newParent.ui.getContainer();
32221             if(!targetNode){//target not rendered
32222                 this.holder = document.createElement("div");
32223                 this.holder.appendChild(this.wrap);
32224                 return;
32225             }
32226             var insertBefore = refNode ? refNode.ui.getEl() : null;
32227             if(insertBefore){
32228                 targetNode.insertBefore(this.wrap, insertBefore);
32229             }else{
32230                 targetNode.appendChild(this.wrap);
32231             }
32232             this.node.renderIndent(true);
32233         }
32234     },
32235
32236     addClass : function(cls){
32237         if(this.elNode){
32238             Roo.fly(this.elNode).addClass(cls);
32239         }
32240     },
32241
32242     removeClass : function(cls){
32243         if(this.elNode){
32244             Roo.fly(this.elNode).removeClass(cls);
32245         }
32246     },
32247
32248     remove : function(){
32249         if(this.rendered){
32250             this.holder = document.createElement("div");
32251             this.holder.appendChild(this.wrap);
32252         }
32253     },
32254
32255     fireEvent : function(){
32256         return this.node.fireEvent.apply(this.node, arguments);
32257     },
32258
32259     initEvents : function(){
32260         this.node.on("move", this.onMove, this);
32261         var E = Roo.EventManager;
32262         var a = this.anchor;
32263
32264         var el = Roo.fly(a, '_treeui');
32265
32266         if(Roo.isOpera){ // opera render bug ignores the CSS
32267             el.setStyle("text-decoration", "none");
32268         }
32269
32270         el.on("click", this.onClick, this);
32271         el.on("dblclick", this.onDblClick, this);
32272
32273         if(this.checkbox){
32274             Roo.EventManager.on(this.checkbox,
32275                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32276         }
32277
32278         el.on("contextmenu", this.onContextMenu, this);
32279
32280         var icon = Roo.fly(this.iconNode);
32281         icon.on("click", this.onClick, this);
32282         icon.on("dblclick", this.onDblClick, this);
32283         icon.on("contextmenu", this.onContextMenu, this);
32284         E.on(this.ecNode, "click", this.ecClick, this, true);
32285
32286         if(this.node.disabled){
32287             this.addClass("x-tree-node-disabled");
32288         }
32289         if(this.node.hidden){
32290             this.addClass("x-tree-node-disabled");
32291         }
32292         var ot = this.node.getOwnerTree();
32293         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32294         if(dd && (!this.node.isRoot || ot.rootVisible)){
32295             Roo.dd.Registry.register(this.elNode, {
32296                 node: this.node,
32297                 handles: this.getDDHandles(),
32298                 isHandle: false
32299             });
32300         }
32301     },
32302
32303     getDDHandles : function(){
32304         return [this.iconNode, this.textNode];
32305     },
32306
32307     hide : function(){
32308         if(this.rendered){
32309             this.wrap.style.display = "none";
32310         }
32311     },
32312
32313     show : function(){
32314         if(this.rendered){
32315             this.wrap.style.display = "";
32316         }
32317     },
32318
32319     onContextMenu : function(e){
32320         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32321             e.preventDefault();
32322             this.focus();
32323             this.fireEvent("contextmenu", this.node, e);
32324         }
32325     },
32326
32327     onClick : function(e){
32328         if(this.dropping){
32329             e.stopEvent();
32330             return;
32331         }
32332         if(this.fireEvent("beforeclick", this.node, e) !== false){
32333             if(!this.disabled && this.node.attributes.href){
32334                 this.fireEvent("click", this.node, e);
32335                 return;
32336             }
32337             e.preventDefault();
32338             if(this.disabled){
32339                 return;
32340             }
32341
32342             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32343                 this.node.toggle();
32344             }
32345
32346             this.fireEvent("click", this.node, e);
32347         }else{
32348             e.stopEvent();
32349         }
32350     },
32351
32352     onDblClick : function(e){
32353         e.preventDefault();
32354         if(this.disabled){
32355             return;
32356         }
32357         if(this.checkbox){
32358             this.toggleCheck();
32359         }
32360         if(!this.animating && this.node.hasChildNodes()){
32361             this.node.toggle();
32362         }
32363         this.fireEvent("dblclick", this.node, e);
32364     },
32365
32366     onCheckChange : function(){
32367         var checked = this.checkbox.checked;
32368         this.node.attributes.checked = checked;
32369         this.fireEvent('checkchange', this.node, checked);
32370     },
32371
32372     ecClick : function(e){
32373         if(!this.animating && this.node.hasChildNodes()){
32374             this.node.toggle();
32375         }
32376     },
32377
32378     startDrop : function(){
32379         this.dropping = true;
32380     },
32381
32382     // delayed drop so the click event doesn't get fired on a drop
32383     endDrop : function(){
32384        setTimeout(function(){
32385            this.dropping = false;
32386        }.createDelegate(this), 50);
32387     },
32388
32389     expand : function(){
32390         this.updateExpandIcon();
32391         this.ctNode.style.display = "";
32392     },
32393
32394     focus : function(){
32395         if(!this.node.preventHScroll){
32396             try{this.anchor.focus();
32397             }catch(e){}
32398         }else if(!Roo.isIE){
32399             try{
32400                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32401                 var l = noscroll.scrollLeft;
32402                 this.anchor.focus();
32403                 noscroll.scrollLeft = l;
32404             }catch(e){}
32405         }
32406     },
32407
32408     toggleCheck : function(value){
32409         var cb = this.checkbox;
32410         if(cb){
32411             cb.checked = (value === undefined ? !cb.checked : value);
32412         }
32413     },
32414
32415     blur : function(){
32416         try{
32417             this.anchor.blur();
32418         }catch(e){}
32419     },
32420
32421     animExpand : function(callback){
32422         var ct = Roo.get(this.ctNode);
32423         ct.stopFx();
32424         if(!this.node.hasChildNodes()){
32425             this.updateExpandIcon();
32426             this.ctNode.style.display = "";
32427             Roo.callback(callback);
32428             return;
32429         }
32430         this.animating = true;
32431         this.updateExpandIcon();
32432
32433         ct.slideIn('t', {
32434            callback : function(){
32435                this.animating = false;
32436                Roo.callback(callback);
32437             },
32438             scope: this,
32439             duration: this.node.ownerTree.duration || .25
32440         });
32441     },
32442
32443     highlight : function(){
32444         var tree = this.node.getOwnerTree();
32445         Roo.fly(this.wrap).highlight(
32446             tree.hlColor || "C3DAF9",
32447             {endColor: tree.hlBaseColor}
32448         );
32449     },
32450
32451     collapse : function(){
32452         this.updateExpandIcon();
32453         this.ctNode.style.display = "none";
32454     },
32455
32456     animCollapse : function(callback){
32457         var ct = Roo.get(this.ctNode);
32458         ct.enableDisplayMode('block');
32459         ct.stopFx();
32460
32461         this.animating = true;
32462         this.updateExpandIcon();
32463
32464         ct.slideOut('t', {
32465             callback : function(){
32466                this.animating = false;
32467                Roo.callback(callback);
32468             },
32469             scope: this,
32470             duration: this.node.ownerTree.duration || .25
32471         });
32472     },
32473
32474     getContainer : function(){
32475         return this.ctNode;
32476     },
32477
32478     getEl : function(){
32479         return this.wrap;
32480     },
32481
32482     appendDDGhost : function(ghostNode){
32483         ghostNode.appendChild(this.elNode.cloneNode(true));
32484     },
32485
32486     getDDRepairXY : function(){
32487         return Roo.lib.Dom.getXY(this.iconNode);
32488     },
32489
32490     onRender : function(){
32491         this.render();
32492     },
32493
32494     render : function(bulkRender){
32495         var n = this.node, a = n.attributes;
32496         var targetNode = n.parentNode ?
32497               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32498
32499         if(!this.rendered){
32500             this.rendered = true;
32501
32502             this.renderElements(n, a, targetNode, bulkRender);
32503
32504             if(a.qtip){
32505                if(this.textNode.setAttributeNS){
32506                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32507                    if(a.qtipTitle){
32508                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32509                    }
32510                }else{
32511                    this.textNode.setAttribute("ext:qtip", a.qtip);
32512                    if(a.qtipTitle){
32513                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32514                    }
32515                }
32516             }else if(a.qtipCfg){
32517                 a.qtipCfg.target = Roo.id(this.textNode);
32518                 Roo.QuickTips.register(a.qtipCfg);
32519             }
32520             this.initEvents();
32521             if(!this.node.expanded){
32522                 this.updateExpandIcon();
32523             }
32524         }else{
32525             if(bulkRender === true) {
32526                 targetNode.appendChild(this.wrap);
32527             }
32528         }
32529     },
32530
32531     renderElements : function(n, a, targetNode, bulkRender){
32532         // add some indent caching, this helps performance when rendering a large tree
32533         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32534         var t = n.getOwnerTree();
32535         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32536         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32537         var cb = typeof a.checked == 'boolean';
32538         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32539         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32540             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32541             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32542             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32543             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32544             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32545              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32546                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32547             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32548             "</li>"];
32549
32550         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32551             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32552                                 n.nextSibling.ui.getEl(), buf.join(""));
32553         }else{
32554             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32555         }
32556
32557         this.elNode = this.wrap.childNodes[0];
32558         this.ctNode = this.wrap.childNodes[1];
32559         var cs = this.elNode.childNodes;
32560         this.indentNode = cs[0];
32561         this.ecNode = cs[1];
32562         this.iconNode = cs[2];
32563         var index = 3;
32564         if(cb){
32565             this.checkbox = cs[3];
32566             index++;
32567         }
32568         this.anchor = cs[index];
32569         this.textNode = cs[index].firstChild;
32570     },
32571
32572     getAnchor : function(){
32573         return this.anchor;
32574     },
32575
32576     getTextEl : function(){
32577         return this.textNode;
32578     },
32579
32580     getIconEl : function(){
32581         return this.iconNode;
32582     },
32583
32584     isChecked : function(){
32585         return this.checkbox ? this.checkbox.checked : false;
32586     },
32587
32588     updateExpandIcon : function(){
32589         if(this.rendered){
32590             var n = this.node, c1, c2;
32591             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32592             var hasChild = n.hasChildNodes();
32593             if(hasChild){
32594                 if(n.expanded){
32595                     cls += "-minus";
32596                     c1 = "x-tree-node-collapsed";
32597                     c2 = "x-tree-node-expanded";
32598                 }else{
32599                     cls += "-plus";
32600                     c1 = "x-tree-node-expanded";
32601                     c2 = "x-tree-node-collapsed";
32602                 }
32603                 if(this.wasLeaf){
32604                     this.removeClass("x-tree-node-leaf");
32605                     this.wasLeaf = false;
32606                 }
32607                 if(this.c1 != c1 || this.c2 != c2){
32608                     Roo.fly(this.elNode).replaceClass(c1, c2);
32609                     this.c1 = c1; this.c2 = c2;
32610                 }
32611             }else{
32612                 if(!this.wasLeaf){
32613                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32614                     delete this.c1;
32615                     delete this.c2;
32616                     this.wasLeaf = true;
32617                 }
32618             }
32619             var ecc = "x-tree-ec-icon "+cls;
32620             if(this.ecc != ecc){
32621                 this.ecNode.className = ecc;
32622                 this.ecc = ecc;
32623             }
32624         }
32625     },
32626
32627     getChildIndent : function(){
32628         if(!this.childIndent){
32629             var buf = [];
32630             var p = this.node;
32631             while(p){
32632                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32633                     if(!p.isLast()) {
32634                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32635                     } else {
32636                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32637                     }
32638                 }
32639                 p = p.parentNode;
32640             }
32641             this.childIndent = buf.join("");
32642         }
32643         return this.childIndent;
32644     },
32645
32646     renderIndent : function(){
32647         if(this.rendered){
32648             var indent = "";
32649             var p = this.node.parentNode;
32650             if(p){
32651                 indent = p.ui.getChildIndent();
32652             }
32653             if(this.indentMarkup != indent){ // don't rerender if not required
32654                 this.indentNode.innerHTML = indent;
32655                 this.indentMarkup = indent;
32656             }
32657             this.updateExpandIcon();
32658         }
32659     }
32660 };
32661
32662 Roo.tree.RootTreeNodeUI = function(){
32663     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32664 };
32665 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32666     render : function(){
32667         if(!this.rendered){
32668             var targetNode = this.node.ownerTree.innerCt.dom;
32669             this.node.expanded = true;
32670             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32671             this.wrap = this.ctNode = targetNode.firstChild;
32672         }
32673     },
32674     collapse : function(){
32675     },
32676     expand : function(){
32677     }
32678 });/*
32679  * Based on:
32680  * Ext JS Library 1.1.1
32681  * Copyright(c) 2006-2007, Ext JS, LLC.
32682  *
32683  * Originally Released Under LGPL - original licence link has changed is not relivant.
32684  *
32685  * Fork - LGPL
32686  * <script type="text/javascript">
32687  */
32688 /**
32689  * @class Roo.tree.TreeLoader
32690  * @extends Roo.util.Observable
32691  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32692  * nodes from a specified URL. The response must be a javascript Array definition
32693  * who's elements are node definition objects. eg:
32694  * <pre><code>
32695    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32696     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32697 </code></pre>
32698  * <br><br>
32699  * A server request is sent, and child nodes are loaded only when a node is expanded.
32700  * The loading node's id is passed to the server under the parameter name "node" to
32701  * enable the server to produce the correct child nodes.
32702  * <br><br>
32703  * To pass extra parameters, an event handler may be attached to the "beforeload"
32704  * event, and the parameters specified in the TreeLoader's baseParams property:
32705  * <pre><code>
32706     myTreeLoader.on("beforeload", function(treeLoader, node) {
32707         this.baseParams.category = node.attributes.category;
32708     }, this);
32709 </code></pre><
32710  * This would pass an HTTP parameter called "category" to the server containing
32711  * the value of the Node's "category" attribute.
32712  * @constructor
32713  * Creates a new Treeloader.
32714  * @param {Object} config A config object containing config properties.
32715  */
32716 Roo.tree.TreeLoader = function(config){
32717     this.baseParams = {};
32718     this.requestMethod = "POST";
32719     Roo.apply(this, config);
32720
32721     this.addEvents({
32722     
32723         /**
32724          * @event beforeload
32725          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32726          * @param {Object} This TreeLoader object.
32727          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32728          * @param {Object} callback The callback function specified in the {@link #load} call.
32729          */
32730         beforeload : true,
32731         /**
32732          * @event load
32733          * Fires when the node has been successfuly loaded.
32734          * @param {Object} This TreeLoader object.
32735          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32736          * @param {Object} response The response object containing the data from the server.
32737          */
32738         load : true,
32739         /**
32740          * @event loadexception
32741          * Fires if the network request failed.
32742          * @param {Object} This TreeLoader object.
32743          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32744          * @param {Object} response The response object containing the data from the server.
32745          */
32746         loadexception : true,
32747         /**
32748          * @event create
32749          * Fires before a node is created, enabling you to return custom Node types 
32750          * @param {Object} This TreeLoader object.
32751          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32752          */
32753         create : true
32754     });
32755
32756     Roo.tree.TreeLoader.superclass.constructor.call(this);
32757 };
32758
32759 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32760     /**
32761     * @cfg {String} dataUrl The URL from which to request a Json string which
32762     * specifies an array of node definition object representing the child nodes
32763     * to be loaded.
32764     */
32765     /**
32766     * @cfg {Object} baseParams (optional) An object containing properties which
32767     * specify HTTP parameters to be passed to each request for child nodes.
32768     */
32769     /**
32770     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32771     * created by this loader. If the attributes sent by the server have an attribute in this object,
32772     * they take priority.
32773     */
32774     /**
32775     * @cfg {Object} uiProviders (optional) An object containing properties which
32776     * 
32777     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32778     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32779     * <i>uiProvider</i> attribute of a returned child node is a string rather
32780     * than a reference to a TreeNodeUI implementation, this that string value
32781     * is used as a property name in the uiProviders object. You can define the provider named
32782     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32783     */
32784     uiProviders : {},
32785
32786     /**
32787     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32788     * child nodes before loading.
32789     */
32790     clearOnLoad : true,
32791
32792     /**
32793     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32794     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32795     * Grid query { data : [ .....] }
32796     */
32797     
32798     root : false,
32799      /**
32800     * @cfg {String} queryParam (optional) 
32801     * Name of the query as it will be passed on the querystring (defaults to 'node')
32802     * eg. the request will be ?node=[id]
32803     */
32804     
32805     
32806     queryParam: false,
32807     
32808     /**
32809      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32810      * This is called automatically when a node is expanded, but may be used to reload
32811      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32812      * @param {Roo.tree.TreeNode} node
32813      * @param {Function} callback
32814      */
32815     load : function(node, callback){
32816         if(this.clearOnLoad){
32817             while(node.firstChild){
32818                 node.removeChild(node.firstChild);
32819             }
32820         }
32821         if(node.attributes.children){ // preloaded json children
32822             var cs = node.attributes.children;
32823             for(var i = 0, len = cs.length; i < len; i++){
32824                 node.appendChild(this.createNode(cs[i]));
32825             }
32826             if(typeof callback == "function"){
32827                 callback();
32828             }
32829         }else if(this.dataUrl){
32830             this.requestData(node, callback);
32831         }
32832     },
32833
32834     getParams: function(node){
32835         var buf = [], bp = this.baseParams;
32836         for(var key in bp){
32837             if(typeof bp[key] != "function"){
32838                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32839             }
32840         }
32841         var n = this.queryParam === false ? 'node' : this.queryParam;
32842         buf.push(n + "=", encodeURIComponent(node.id));
32843         return buf.join("");
32844     },
32845
32846     requestData : function(node, callback){
32847         if(this.fireEvent("beforeload", this, node, callback) !== false){
32848             this.transId = Roo.Ajax.request({
32849                 method:this.requestMethod,
32850                 url: this.dataUrl||this.url,
32851                 success: this.handleResponse,
32852                 failure: this.handleFailure,
32853                 scope: this,
32854                 argument: {callback: callback, node: node},
32855                 params: this.getParams(node)
32856             });
32857         }else{
32858             // if the load is cancelled, make sure we notify
32859             // the node that we are done
32860             if(typeof callback == "function"){
32861                 callback();
32862             }
32863         }
32864     },
32865
32866     isLoading : function(){
32867         return this.transId ? true : false;
32868     },
32869
32870     abort : function(){
32871         if(this.isLoading()){
32872             Roo.Ajax.abort(this.transId);
32873         }
32874     },
32875
32876     // private
32877     createNode : function(attr){
32878         // apply baseAttrs, nice idea Corey!
32879         if(this.baseAttrs){
32880             Roo.applyIf(attr, this.baseAttrs);
32881         }
32882         if(this.applyLoader !== false){
32883             attr.loader = this;
32884         }
32885         // uiProvider = depreciated..
32886         
32887         if(typeof(attr.uiProvider) == 'string'){
32888            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32889                 /**  eval:var:attr */ eval(attr.uiProvider);
32890         }
32891         if(typeof(this.uiProviders['default']) != 'undefined') {
32892             attr.uiProvider = this.uiProviders['default'];
32893         }
32894         
32895         this.fireEvent('create', this, attr);
32896         
32897         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32898         return(attr.leaf ?
32899                         new Roo.tree.TreeNode(attr) :
32900                         new Roo.tree.AsyncTreeNode(attr));
32901     },
32902
32903     processResponse : function(response, node, callback){
32904         var json = response.responseText;
32905         try {
32906             
32907             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32908             if (this.root !== false) {
32909                 o = o[this.root];
32910             }
32911             
32912             for(var i = 0, len = o.length; i < len; i++){
32913                 var n = this.createNode(o[i]);
32914                 if(n){
32915                     node.appendChild(n);
32916                 }
32917             }
32918             if(typeof callback == "function"){
32919                 callback(this, node);
32920             }
32921         }catch(e){
32922             this.handleFailure(response);
32923         }
32924     },
32925
32926     handleResponse : function(response){
32927         this.transId = false;
32928         var a = response.argument;
32929         this.processResponse(response, a.node, a.callback);
32930         this.fireEvent("load", this, a.node, response);
32931     },
32932
32933     handleFailure : function(response){
32934         this.transId = false;
32935         var a = response.argument;
32936         this.fireEvent("loadexception", this, a.node, response);
32937         if(typeof a.callback == "function"){
32938             a.callback(this, a.node);
32939         }
32940     }
32941 });/*
32942  * Based on:
32943  * Ext JS Library 1.1.1
32944  * Copyright(c) 2006-2007, Ext JS, LLC.
32945  *
32946  * Originally Released Under LGPL - original licence link has changed is not relivant.
32947  *
32948  * Fork - LGPL
32949  * <script type="text/javascript">
32950  */
32951
32952 /**
32953 * @class Roo.tree.TreeFilter
32954 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32955 * @param {TreePanel} tree
32956 * @param {Object} config (optional)
32957  */
32958 Roo.tree.TreeFilter = function(tree, config){
32959     this.tree = tree;
32960     this.filtered = {};
32961     Roo.apply(this, config);
32962 };
32963
32964 Roo.tree.TreeFilter.prototype = {
32965     clearBlank:false,
32966     reverse:false,
32967     autoClear:false,
32968     remove:false,
32969
32970      /**
32971      * Filter the data by a specific attribute.
32972      * @param {String/RegExp} value Either string that the attribute value
32973      * should start with or a RegExp to test against the attribute
32974      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32975      * @param {TreeNode} startNode (optional) The node to start the filter at.
32976      */
32977     filter : function(value, attr, startNode){
32978         attr = attr || "text";
32979         var f;
32980         if(typeof value == "string"){
32981             var vlen = value.length;
32982             // auto clear empty filter
32983             if(vlen == 0 && this.clearBlank){
32984                 this.clear();
32985                 return;
32986             }
32987             value = value.toLowerCase();
32988             f = function(n){
32989                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32990             };
32991         }else if(value.exec){ // regex?
32992             f = function(n){
32993                 return value.test(n.attributes[attr]);
32994             };
32995         }else{
32996             throw 'Illegal filter type, must be string or regex';
32997         }
32998         this.filterBy(f, null, startNode);
32999         },
33000
33001     /**
33002      * Filter by a function. The passed function will be called with each
33003      * node in the tree (or from the startNode). If the function returns true, the node is kept
33004      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33005      * @param {Function} fn The filter function
33006      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33007      */
33008     filterBy : function(fn, scope, startNode){
33009         startNode = startNode || this.tree.root;
33010         if(this.autoClear){
33011             this.clear();
33012         }
33013         var af = this.filtered, rv = this.reverse;
33014         var f = function(n){
33015             if(n == startNode){
33016                 return true;
33017             }
33018             if(af[n.id]){
33019                 return false;
33020             }
33021             var m = fn.call(scope || n, n);
33022             if(!m || rv){
33023                 af[n.id] = n;
33024                 n.ui.hide();
33025                 return false;
33026             }
33027             return true;
33028         };
33029         startNode.cascade(f);
33030         if(this.remove){
33031            for(var id in af){
33032                if(typeof id != "function"){
33033                    var n = af[id];
33034                    if(n && n.parentNode){
33035                        n.parentNode.removeChild(n);
33036                    }
33037                }
33038            }
33039         }
33040     },
33041
33042     /**
33043      * Clears the current filter. Note: with the "remove" option
33044      * set a filter cannot be cleared.
33045      */
33046     clear : function(){
33047         var t = this.tree;
33048         var af = this.filtered;
33049         for(var id in af){
33050             if(typeof id != "function"){
33051                 var n = af[id];
33052                 if(n){
33053                     n.ui.show();
33054                 }
33055             }
33056         }
33057         this.filtered = {};
33058     }
33059 };
33060 /*
33061  * Based on:
33062  * Ext JS Library 1.1.1
33063  * Copyright(c) 2006-2007, Ext JS, LLC.
33064  *
33065  * Originally Released Under LGPL - original licence link has changed is not relivant.
33066  *
33067  * Fork - LGPL
33068  * <script type="text/javascript">
33069  */
33070  
33071
33072 /**
33073  * @class Roo.tree.TreeSorter
33074  * Provides sorting of nodes in a TreePanel
33075  * 
33076  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33077  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33078  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33079  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33080  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33081  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33082  * @constructor
33083  * @param {TreePanel} tree
33084  * @param {Object} config
33085  */
33086 Roo.tree.TreeSorter = function(tree, config){
33087     Roo.apply(this, config);
33088     tree.on("beforechildrenrendered", this.doSort, this);
33089     tree.on("append", this.updateSort, this);
33090     tree.on("insert", this.updateSort, this);
33091     
33092     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33093     var p = this.property || "text";
33094     var sortType = this.sortType;
33095     var fs = this.folderSort;
33096     var cs = this.caseSensitive === true;
33097     var leafAttr = this.leafAttr || 'leaf';
33098
33099     this.sortFn = function(n1, n2){
33100         if(fs){
33101             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33102                 return 1;
33103             }
33104             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33105                 return -1;
33106             }
33107         }
33108         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33109         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33110         if(v1 < v2){
33111                         return dsc ? +1 : -1;
33112                 }else if(v1 > v2){
33113                         return dsc ? -1 : +1;
33114         }else{
33115                 return 0;
33116         }
33117     };
33118 };
33119
33120 Roo.tree.TreeSorter.prototype = {
33121     doSort : function(node){
33122         node.sort(this.sortFn);
33123     },
33124     
33125     compareNodes : function(n1, n2){
33126         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33127     },
33128     
33129     updateSort : function(tree, node){
33130         if(node.childrenRendered){
33131             this.doSort.defer(1, this, [node]);
33132         }
33133     }
33134 };/*
33135  * Based on:
33136  * Ext JS Library 1.1.1
33137  * Copyright(c) 2006-2007, Ext JS, LLC.
33138  *
33139  * Originally Released Under LGPL - original licence link has changed is not relivant.
33140  *
33141  * Fork - LGPL
33142  * <script type="text/javascript">
33143  */
33144
33145 if(Roo.dd.DropZone){
33146     
33147 Roo.tree.TreeDropZone = function(tree, config){
33148     this.allowParentInsert = false;
33149     this.allowContainerDrop = false;
33150     this.appendOnly = false;
33151     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33152     this.tree = tree;
33153     this.lastInsertClass = "x-tree-no-status";
33154     this.dragOverData = {};
33155 };
33156
33157 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33158     ddGroup : "TreeDD",
33159     
33160     expandDelay : 1000,
33161     
33162     expandNode : function(node){
33163         if(node.hasChildNodes() && !node.isExpanded()){
33164             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33165         }
33166     },
33167     
33168     queueExpand : function(node){
33169         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33170     },
33171     
33172     cancelExpand : function(){
33173         if(this.expandProcId){
33174             clearTimeout(this.expandProcId);
33175             this.expandProcId = false;
33176         }
33177     },
33178     
33179     isValidDropPoint : function(n, pt, dd, e, data){
33180         if(!n || !data){ return false; }
33181         var targetNode = n.node;
33182         var dropNode = data.node;
33183         // default drop rules
33184         if(!(targetNode && targetNode.isTarget && pt)){
33185             return false;
33186         }
33187         if(pt == "append" && targetNode.allowChildren === false){
33188             return false;
33189         }
33190         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33191             return false;
33192         }
33193         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33194             return false;
33195         }
33196         // reuse the object
33197         var overEvent = this.dragOverData;
33198         overEvent.tree = this.tree;
33199         overEvent.target = targetNode;
33200         overEvent.data = data;
33201         overEvent.point = pt;
33202         overEvent.source = dd;
33203         overEvent.rawEvent = e;
33204         overEvent.dropNode = dropNode;
33205         overEvent.cancel = false;  
33206         var result = this.tree.fireEvent("nodedragover", overEvent);
33207         return overEvent.cancel === false && result !== false;
33208     },
33209     
33210     getDropPoint : function(e, n, dd){
33211         var tn = n.node;
33212         if(tn.isRoot){
33213             return tn.allowChildren !== false ? "append" : false; // always append for root
33214         }
33215         var dragEl = n.ddel;
33216         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33217         var y = Roo.lib.Event.getPageY(e);
33218         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33219         
33220         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33221         var noAppend = tn.allowChildren === false;
33222         if(this.appendOnly || tn.parentNode.allowChildren === false){
33223             return noAppend ? false : "append";
33224         }
33225         var noBelow = false;
33226         if(!this.allowParentInsert){
33227             noBelow = tn.hasChildNodes() && tn.isExpanded();
33228         }
33229         var q = (b - t) / (noAppend ? 2 : 3);
33230         if(y >= t && y < (t + q)){
33231             return "above";
33232         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33233             return "below";
33234         }else{
33235             return "append";
33236         }
33237     },
33238     
33239     onNodeEnter : function(n, dd, e, data){
33240         this.cancelExpand();
33241     },
33242     
33243     onNodeOver : function(n, dd, e, data){
33244         var pt = this.getDropPoint(e, n, dd);
33245         var node = n.node;
33246         
33247         // auto node expand check
33248         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33249             this.queueExpand(node);
33250         }else if(pt != "append"){
33251             this.cancelExpand();
33252         }
33253         
33254         // set the insert point style on the target node
33255         var returnCls = this.dropNotAllowed;
33256         if(this.isValidDropPoint(n, pt, dd, e, data)){
33257            if(pt){
33258                var el = n.ddel;
33259                var cls;
33260                if(pt == "above"){
33261                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33262                    cls = "x-tree-drag-insert-above";
33263                }else if(pt == "below"){
33264                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33265                    cls = "x-tree-drag-insert-below";
33266                }else{
33267                    returnCls = "x-tree-drop-ok-append";
33268                    cls = "x-tree-drag-append";
33269                }
33270                if(this.lastInsertClass != cls){
33271                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33272                    this.lastInsertClass = cls;
33273                }
33274            }
33275        }
33276        return returnCls;
33277     },
33278     
33279     onNodeOut : function(n, dd, e, data){
33280         this.cancelExpand();
33281         this.removeDropIndicators(n);
33282     },
33283     
33284     onNodeDrop : function(n, dd, e, data){
33285         var point = this.getDropPoint(e, n, dd);
33286         var targetNode = n.node;
33287         targetNode.ui.startDrop();
33288         if(!this.isValidDropPoint(n, point, dd, e, data)){
33289             targetNode.ui.endDrop();
33290             return false;
33291         }
33292         // first try to find the drop node
33293         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33294         var dropEvent = {
33295             tree : this.tree,
33296             target: targetNode,
33297             data: data,
33298             point: point,
33299             source: dd,
33300             rawEvent: e,
33301             dropNode: dropNode,
33302             cancel: !dropNode   
33303         };
33304         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33305         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33306             targetNode.ui.endDrop();
33307             return false;
33308         }
33309         // allow target changing
33310         targetNode = dropEvent.target;
33311         if(point == "append" && !targetNode.isExpanded()){
33312             targetNode.expand(false, null, function(){
33313                 this.completeDrop(dropEvent);
33314             }.createDelegate(this));
33315         }else{
33316             this.completeDrop(dropEvent);
33317         }
33318         return true;
33319     },
33320     
33321     completeDrop : function(de){
33322         var ns = de.dropNode, p = de.point, t = de.target;
33323         if(!(ns instanceof Array)){
33324             ns = [ns];
33325         }
33326         var n;
33327         for(var i = 0, len = ns.length; i < len; i++){
33328             n = ns[i];
33329             if(p == "above"){
33330                 t.parentNode.insertBefore(n, t);
33331             }else if(p == "below"){
33332                 t.parentNode.insertBefore(n, t.nextSibling);
33333             }else{
33334                 t.appendChild(n);
33335             }
33336         }
33337         n.ui.focus();
33338         if(this.tree.hlDrop){
33339             n.ui.highlight();
33340         }
33341         t.ui.endDrop();
33342         this.tree.fireEvent("nodedrop", de);
33343     },
33344     
33345     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33346         if(this.tree.hlDrop){
33347             dropNode.ui.focus();
33348             dropNode.ui.highlight();
33349         }
33350         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33351     },
33352     
33353     getTree : function(){
33354         return this.tree;
33355     },
33356     
33357     removeDropIndicators : function(n){
33358         if(n && n.ddel){
33359             var el = n.ddel;
33360             Roo.fly(el).removeClass([
33361                     "x-tree-drag-insert-above",
33362                     "x-tree-drag-insert-below",
33363                     "x-tree-drag-append"]);
33364             this.lastInsertClass = "_noclass";
33365         }
33366     },
33367     
33368     beforeDragDrop : function(target, e, id){
33369         this.cancelExpand();
33370         return true;
33371     },
33372     
33373     afterRepair : function(data){
33374         if(data && Roo.enableFx){
33375             data.node.ui.highlight();
33376         }
33377         this.hideProxy();
33378     }    
33379 });
33380
33381 }
33382 /*
33383  * Based on:
33384  * Ext JS Library 1.1.1
33385  * Copyright(c) 2006-2007, Ext JS, LLC.
33386  *
33387  * Originally Released Under LGPL - original licence link has changed is not relivant.
33388  *
33389  * Fork - LGPL
33390  * <script type="text/javascript">
33391  */
33392  
33393
33394 if(Roo.dd.DragZone){
33395 Roo.tree.TreeDragZone = function(tree, config){
33396     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33397     this.tree = tree;
33398 };
33399
33400 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33401     ddGroup : "TreeDD",
33402     
33403     onBeforeDrag : function(data, e){
33404         var n = data.node;
33405         return n && n.draggable && !n.disabled;
33406     },
33407     
33408     onInitDrag : function(e){
33409         var data = this.dragData;
33410         this.tree.getSelectionModel().select(data.node);
33411         this.proxy.update("");
33412         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33413         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33414     },
33415     
33416     getRepairXY : function(e, data){
33417         return data.node.ui.getDDRepairXY();
33418     },
33419     
33420     onEndDrag : function(data, e){
33421         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33422     },
33423     
33424     onValidDrop : function(dd, e, id){
33425         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33426         this.hideProxy();
33427     },
33428     
33429     beforeInvalidDrop : function(e, id){
33430         // this scrolls the original position back into view
33431         var sm = this.tree.getSelectionModel();
33432         sm.clearSelections();
33433         sm.select(this.dragData.node);
33434     }
33435 });
33436 }/*
33437  * Based on:
33438  * Ext JS Library 1.1.1
33439  * Copyright(c) 2006-2007, Ext JS, LLC.
33440  *
33441  * Originally Released Under LGPL - original licence link has changed is not relivant.
33442  *
33443  * Fork - LGPL
33444  * <script type="text/javascript">
33445  */
33446 /**
33447  * @class Roo.tree.TreeEditor
33448  * @extends Roo.Editor
33449  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33450  * as the editor field.
33451  * @constructor
33452  * @param {TreePanel} tree
33453  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33454  */
33455 Roo.tree.TreeEditor = function(tree, config){
33456     config = config || {};
33457     var field = config.events ? config : new Roo.form.TextField(config);
33458     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33459
33460     this.tree = tree;
33461
33462     tree.on('beforeclick', this.beforeNodeClick, this);
33463     tree.getTreeEl().on('mousedown', this.hide, this);
33464     this.on('complete', this.updateNode, this);
33465     this.on('beforestartedit', this.fitToTree, this);
33466     this.on('startedit', this.bindScroll, this, {delay:10});
33467     this.on('specialkey', this.onSpecialKey, this);
33468 };
33469
33470 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33471     /**
33472      * @cfg {String} alignment
33473      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33474      */
33475     alignment: "l-l",
33476     // inherit
33477     autoSize: false,
33478     /**
33479      * @cfg {Boolean} hideEl
33480      * True to hide the bound element while the editor is displayed (defaults to false)
33481      */
33482     hideEl : false,
33483     /**
33484      * @cfg {String} cls
33485      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33486      */
33487     cls: "x-small-editor x-tree-editor",
33488     /**
33489      * @cfg {Boolean} shim
33490      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33491      */
33492     shim:false,
33493     // inherit
33494     shadow:"frame",
33495     /**
33496      * @cfg {Number} maxWidth
33497      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33498      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33499      * scroll and client offsets into account prior to each edit.
33500      */
33501     maxWidth: 250,
33502
33503     editDelay : 350,
33504
33505     // private
33506     fitToTree : function(ed, el){
33507         var td = this.tree.getTreeEl().dom, nd = el.dom;
33508         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33509             td.scrollLeft = nd.offsetLeft;
33510         }
33511         var w = Math.min(
33512                 this.maxWidth,
33513                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33514         this.setSize(w, '');
33515     },
33516
33517     // private
33518     triggerEdit : function(node){
33519         this.completeEdit();
33520         this.editNode = node;
33521         this.startEdit(node.ui.textNode, node.text);
33522     },
33523
33524     // private
33525     bindScroll : function(){
33526         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33527     },
33528
33529     // private
33530     beforeNodeClick : function(node, e){
33531         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33532         this.lastClick = new Date();
33533         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33534             e.stopEvent();
33535             this.triggerEdit(node);
33536             return false;
33537         }
33538     },
33539
33540     // private
33541     updateNode : function(ed, value){
33542         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33543         this.editNode.setText(value);
33544     },
33545
33546     // private
33547     onHide : function(){
33548         Roo.tree.TreeEditor.superclass.onHide.call(this);
33549         if(this.editNode){
33550             this.editNode.ui.focus();
33551         }
33552     },
33553
33554     // private
33555     onSpecialKey : function(field, e){
33556         var k = e.getKey();
33557         if(k == e.ESC){
33558             e.stopEvent();
33559             this.cancelEdit();
33560         }else if(k == e.ENTER && !e.hasModifier()){
33561             e.stopEvent();
33562             this.completeEdit();
33563         }
33564     }
33565 });//<Script type="text/javascript">
33566 /*
33567  * Based on:
33568  * Ext JS Library 1.1.1
33569  * Copyright(c) 2006-2007, Ext JS, LLC.
33570  *
33571  * Originally Released Under LGPL - original licence link has changed is not relivant.
33572  *
33573  * Fork - LGPL
33574  * <script type="text/javascript">
33575  */
33576  
33577 /**
33578  * Not documented??? - probably should be...
33579  */
33580
33581 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33582     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33583     
33584     renderElements : function(n, a, targetNode, bulkRender){
33585         //consel.log("renderElements?");
33586         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33587
33588         var t = n.getOwnerTree();
33589         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33590         
33591         var cols = t.columns;
33592         var bw = t.borderWidth;
33593         var c = cols[0];
33594         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33595          var cb = typeof a.checked == "boolean";
33596         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33597         var colcls = 'x-t-' + tid + '-c0';
33598         var buf = [
33599             '<li class="x-tree-node">',
33600             
33601                 
33602                 '<div class="x-tree-node-el ', a.cls,'">',
33603                     // extran...
33604                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33605                 
33606                 
33607                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33608                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33609                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33610                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33611                            (a.iconCls ? ' '+a.iconCls : ''),
33612                            '" unselectable="on" />',
33613                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33614                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33615                              
33616                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33617                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33618                             '<span unselectable="on" qtip="' + tx + '">',
33619                              tx,
33620                              '</span></a>' ,
33621                     '</div>',
33622                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33623                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33624                  ];
33625         for(var i = 1, len = cols.length; i < len; i++){
33626             c = cols[i];
33627             colcls = 'x-t-' + tid + '-c' +i;
33628             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33629             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33630                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33631                       "</div>");
33632          }
33633          
33634          buf.push(
33635             '</a>',
33636             '<div class="x-clear"></div></div>',
33637             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33638             "</li>");
33639         
33640         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33641             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33642                                 n.nextSibling.ui.getEl(), buf.join(""));
33643         }else{
33644             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33645         }
33646         var el = this.wrap.firstChild;
33647         this.elRow = el;
33648         this.elNode = el.firstChild;
33649         this.ranchor = el.childNodes[1];
33650         this.ctNode = this.wrap.childNodes[1];
33651         var cs = el.firstChild.childNodes;
33652         this.indentNode = cs[0];
33653         this.ecNode = cs[1];
33654         this.iconNode = cs[2];
33655         var index = 3;
33656         if(cb){
33657             this.checkbox = cs[3];
33658             index++;
33659         }
33660         this.anchor = cs[index];
33661         
33662         this.textNode = cs[index].firstChild;
33663         
33664         //el.on("click", this.onClick, this);
33665         //el.on("dblclick", this.onDblClick, this);
33666         
33667         
33668        // console.log(this);
33669     },
33670     initEvents : function(){
33671         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33672         
33673             
33674         var a = this.ranchor;
33675
33676         var el = Roo.get(a);
33677
33678         if(Roo.isOpera){ // opera render bug ignores the CSS
33679             el.setStyle("text-decoration", "none");
33680         }
33681
33682         el.on("click", this.onClick, this);
33683         el.on("dblclick", this.onDblClick, this);
33684         el.on("contextmenu", this.onContextMenu, this);
33685         
33686     },
33687     
33688     /*onSelectedChange : function(state){
33689         if(state){
33690             this.focus();
33691             this.addClass("x-tree-selected");
33692         }else{
33693             //this.blur();
33694             this.removeClass("x-tree-selected");
33695         }
33696     },*/
33697     addClass : function(cls){
33698         if(this.elRow){
33699             Roo.fly(this.elRow).addClass(cls);
33700         }
33701         
33702     },
33703     
33704     
33705     removeClass : function(cls){
33706         if(this.elRow){
33707             Roo.fly(this.elRow).removeClass(cls);
33708         }
33709     }
33710
33711     
33712     
33713 });//<Script type="text/javascript">
33714
33715 /*
33716  * Based on:
33717  * Ext JS Library 1.1.1
33718  * Copyright(c) 2006-2007, Ext JS, LLC.
33719  *
33720  * Originally Released Under LGPL - original licence link has changed is not relivant.
33721  *
33722  * Fork - LGPL
33723  * <script type="text/javascript">
33724  */
33725  
33726
33727 /**
33728  * @class Roo.tree.ColumnTree
33729  * @extends Roo.data.TreePanel
33730  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33731  * @cfg {int} borderWidth  compined right/left border allowance
33732  * @constructor
33733  * @param {String/HTMLElement/Element} el The container element
33734  * @param {Object} config
33735  */
33736 Roo.tree.ColumnTree =  function(el, config)
33737 {
33738    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33739    this.addEvents({
33740         /**
33741         * @event resize
33742         * Fire this event on a container when it resizes
33743         * @param {int} w Width
33744         * @param {int} h Height
33745         */
33746        "resize" : true
33747     });
33748     this.on('resize', this.onResize, this);
33749 };
33750
33751 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33752     //lines:false,
33753     
33754     
33755     borderWidth: Roo.isBorderBox ? 0 : 2, 
33756     headEls : false,
33757     
33758     render : function(){
33759         // add the header.....
33760        
33761         Roo.tree.ColumnTree.superclass.render.apply(this);
33762         
33763         this.el.addClass('x-column-tree');
33764         
33765         this.headers = this.el.createChild(
33766             {cls:'x-tree-headers'},this.innerCt.dom);
33767    
33768         var cols = this.columns, c;
33769         var totalWidth = 0;
33770         this.headEls = [];
33771         var  len = cols.length;
33772         for(var i = 0; i < len; i++){
33773              c = cols[i];
33774              totalWidth += c.width;
33775             this.headEls.push(this.headers.createChild({
33776                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33777                  cn: {
33778                      cls:'x-tree-hd-text',
33779                      html: c.header
33780                  },
33781                  style:'width:'+(c.width-this.borderWidth)+'px;'
33782              }));
33783         }
33784         this.headers.createChild({cls:'x-clear'});
33785         // prevent floats from wrapping when clipped
33786         this.headers.setWidth(totalWidth);
33787         //this.innerCt.setWidth(totalWidth);
33788         this.innerCt.setStyle({ overflow: 'auto' });
33789         this.onResize(this.width, this.height);
33790              
33791         
33792     },
33793     onResize : function(w,h)
33794     {
33795         this.height = h;
33796         this.width = w;
33797         // resize cols..
33798         this.innerCt.setWidth(this.width);
33799         this.innerCt.setHeight(this.height-20);
33800         
33801         // headers...
33802         var cols = this.columns, c;
33803         var totalWidth = 0;
33804         var expEl = false;
33805         var len = cols.length;
33806         for(var i = 0; i < len; i++){
33807             c = cols[i];
33808             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33809                 // it's the expander..
33810                 expEl  = this.headEls[i];
33811                 continue;
33812             }
33813             totalWidth += c.width;
33814             
33815         }
33816         if (expEl) {
33817             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33818         }
33819         this.headers.setWidth(w-20);
33820
33821         
33822         
33823         
33824     }
33825 });
33826 /*
33827  * Based on:
33828  * Ext JS Library 1.1.1
33829  * Copyright(c) 2006-2007, Ext JS, LLC.
33830  *
33831  * Originally Released Under LGPL - original licence link has changed is not relivant.
33832  *
33833  * Fork - LGPL
33834  * <script type="text/javascript">
33835  */
33836  
33837 /**
33838  * @class Roo.menu.Menu
33839  * @extends Roo.util.Observable
33840  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33841  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33842  * @constructor
33843  * Creates a new Menu
33844  * @param {Object} config Configuration options
33845  */
33846 Roo.menu.Menu = function(config){
33847     Roo.apply(this, config);
33848     this.id = this.id || Roo.id();
33849     this.addEvents({
33850         /**
33851          * @event beforeshow
33852          * Fires before this menu is displayed
33853          * @param {Roo.menu.Menu} this
33854          */
33855         beforeshow : true,
33856         /**
33857          * @event beforehide
33858          * Fires before this menu is hidden
33859          * @param {Roo.menu.Menu} this
33860          */
33861         beforehide : true,
33862         /**
33863          * @event show
33864          * Fires after this menu is displayed
33865          * @param {Roo.menu.Menu} this
33866          */
33867         show : true,
33868         /**
33869          * @event hide
33870          * Fires after this menu is hidden
33871          * @param {Roo.menu.Menu} this
33872          */
33873         hide : true,
33874         /**
33875          * @event click
33876          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33877          * @param {Roo.menu.Menu} this
33878          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33879          * @param {Roo.EventObject} e
33880          */
33881         click : true,
33882         /**
33883          * @event mouseover
33884          * Fires when the mouse is hovering over this menu
33885          * @param {Roo.menu.Menu} this
33886          * @param {Roo.EventObject} e
33887          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33888          */
33889         mouseover : true,
33890         /**
33891          * @event mouseout
33892          * Fires when the mouse exits this menu
33893          * @param {Roo.menu.Menu} this
33894          * @param {Roo.EventObject} e
33895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33896          */
33897         mouseout : true,
33898         /**
33899          * @event itemclick
33900          * Fires when a menu item contained in this menu is clicked
33901          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33902          * @param {Roo.EventObject} e
33903          */
33904         itemclick: true
33905     });
33906     if (this.registerMenu) {
33907         Roo.menu.MenuMgr.register(this);
33908     }
33909     
33910     var mis = this.items;
33911     this.items = new Roo.util.MixedCollection();
33912     if(mis){
33913         this.add.apply(this, mis);
33914     }
33915 };
33916
33917 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33918     /**
33919      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33920      */
33921     minWidth : 120,
33922     /**
33923      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33924      * for bottom-right shadow (defaults to "sides")
33925      */
33926     shadow : "sides",
33927     /**
33928      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33929      * this menu (defaults to "tl-tr?")
33930      */
33931     subMenuAlign : "tl-tr?",
33932     /**
33933      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33934      * relative to its element of origin (defaults to "tl-bl?")
33935      */
33936     defaultAlign : "tl-bl?",
33937     /**
33938      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33939      */
33940     allowOtherMenus : false,
33941     /**
33942      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33943      */
33944     registerMenu : true,
33945
33946     hidden:true,
33947
33948     // private
33949     render : function(){
33950         if(this.el){
33951             return;
33952         }
33953         var el = this.el = new Roo.Layer({
33954             cls: "x-menu",
33955             shadow:this.shadow,
33956             constrain: false,
33957             parentEl: this.parentEl || document.body,
33958             zindex:15000
33959         });
33960
33961         this.keyNav = new Roo.menu.MenuNav(this);
33962
33963         if(this.plain){
33964             el.addClass("x-menu-plain");
33965         }
33966         if(this.cls){
33967             el.addClass(this.cls);
33968         }
33969         // generic focus element
33970         this.focusEl = el.createChild({
33971             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33972         });
33973         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33974         ul.on("click", this.onClick, this);
33975         ul.on("mouseover", this.onMouseOver, this);
33976         ul.on("mouseout", this.onMouseOut, this);
33977         this.items.each(function(item){
33978             var li = document.createElement("li");
33979             li.className = "x-menu-list-item";
33980             ul.dom.appendChild(li);
33981             item.render(li, this);
33982         }, this);
33983         this.ul = ul;
33984         this.autoWidth();
33985     },
33986
33987     // private
33988     autoWidth : function(){
33989         var el = this.el, ul = this.ul;
33990         if(!el){
33991             return;
33992         }
33993         var w = this.width;
33994         if(w){
33995             el.setWidth(w);
33996         }else if(Roo.isIE){
33997             el.setWidth(this.minWidth);
33998             var t = el.dom.offsetWidth; // force recalc
33999             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34000         }
34001     },
34002
34003     // private
34004     delayAutoWidth : function(){
34005         if(this.rendered){
34006             if(!this.awTask){
34007                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34008             }
34009             this.awTask.delay(20);
34010         }
34011     },
34012
34013     // private
34014     findTargetItem : function(e){
34015         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34016         if(t && t.menuItemId){
34017             return this.items.get(t.menuItemId);
34018         }
34019     },
34020
34021     // private
34022     onClick : function(e){
34023         var t;
34024         if(t = this.findTargetItem(e)){
34025             t.onClick(e);
34026             this.fireEvent("click", this, t, e);
34027         }
34028     },
34029
34030     // private
34031     setActiveItem : function(item, autoExpand){
34032         if(item != this.activeItem){
34033             if(this.activeItem){
34034                 this.activeItem.deactivate();
34035             }
34036             this.activeItem = item;
34037             item.activate(autoExpand);
34038         }else if(autoExpand){
34039             item.expandMenu();
34040         }
34041     },
34042
34043     // private
34044     tryActivate : function(start, step){
34045         var items = this.items;
34046         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34047             var item = items.get(i);
34048             if(!item.disabled && item.canActivate){
34049                 this.setActiveItem(item, false);
34050                 return item;
34051             }
34052         }
34053         return false;
34054     },
34055
34056     // private
34057     onMouseOver : function(e){
34058         var t;
34059         if(t = this.findTargetItem(e)){
34060             if(t.canActivate && !t.disabled){
34061                 this.setActiveItem(t, true);
34062             }
34063         }
34064         this.fireEvent("mouseover", this, e, t);
34065     },
34066
34067     // private
34068     onMouseOut : function(e){
34069         var t;
34070         if(t = this.findTargetItem(e)){
34071             if(t == this.activeItem && t.shouldDeactivate(e)){
34072                 this.activeItem.deactivate();
34073                 delete this.activeItem;
34074             }
34075         }
34076         this.fireEvent("mouseout", this, e, t);
34077     },
34078
34079     /**
34080      * Read-only.  Returns true if the menu is currently displayed, else false.
34081      * @type Boolean
34082      */
34083     isVisible : function(){
34084         return this.el && !this.hidden;
34085     },
34086
34087     /**
34088      * Displays this menu relative to another element
34089      * @param {String/HTMLElement/Roo.Element} element The element to align to
34090      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34091      * the element (defaults to this.defaultAlign)
34092      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34093      */
34094     show : function(el, pos, parentMenu){
34095         this.parentMenu = parentMenu;
34096         if(!this.el){
34097             this.render();
34098         }
34099         this.fireEvent("beforeshow", this);
34100         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34101     },
34102
34103     /**
34104      * Displays this menu at a specific xy position
34105      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34106      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34107      */
34108     showAt : function(xy, parentMenu, /* private: */_e){
34109         this.parentMenu = parentMenu;
34110         if(!this.el){
34111             this.render();
34112         }
34113         if(_e !== false){
34114             this.fireEvent("beforeshow", this);
34115             xy = this.el.adjustForConstraints(xy);
34116         }
34117         this.el.setXY(xy);
34118         this.el.show();
34119         this.hidden = false;
34120         this.focus();
34121         this.fireEvent("show", this);
34122     },
34123
34124     focus : function(){
34125         if(!this.hidden){
34126             this.doFocus.defer(50, this);
34127         }
34128     },
34129
34130     doFocus : function(){
34131         if(!this.hidden){
34132             this.focusEl.focus();
34133         }
34134     },
34135
34136     /**
34137      * Hides this menu and optionally all parent menus
34138      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34139      */
34140     hide : function(deep){
34141         if(this.el && this.isVisible()){
34142             this.fireEvent("beforehide", this);
34143             if(this.activeItem){
34144                 this.activeItem.deactivate();
34145                 this.activeItem = null;
34146             }
34147             this.el.hide();
34148             this.hidden = true;
34149             this.fireEvent("hide", this);
34150         }
34151         if(deep === true && this.parentMenu){
34152             this.parentMenu.hide(true);
34153         }
34154     },
34155
34156     /**
34157      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34158      * Any of the following are valid:
34159      * <ul>
34160      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34161      * <li>An HTMLElement object which will be converted to a menu item</li>
34162      * <li>A menu item config object that will be created as a new menu item</li>
34163      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34164      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34165      * </ul>
34166      * Usage:
34167      * <pre><code>
34168 // Create the menu
34169 var menu = new Roo.menu.Menu();
34170
34171 // Create a menu item to add by reference
34172 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34173
34174 // Add a bunch of items at once using different methods.
34175 // Only the last item added will be returned.
34176 var item = menu.add(
34177     menuItem,                // add existing item by ref
34178     'Dynamic Item',          // new TextItem
34179     '-',                     // new separator
34180     { text: 'Config Item' }  // new item by config
34181 );
34182 </code></pre>
34183      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34184      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34185      */
34186     add : function(){
34187         var a = arguments, l = a.length, item;
34188         for(var i = 0; i < l; i++){
34189             var el = a[i];
34190             if ((typeof(el) == "object") && el.xtype && el.xns) {
34191                 el = Roo.factory(el, Roo.menu);
34192             }
34193             
34194             if(el.render){ // some kind of Item
34195                 item = this.addItem(el);
34196             }else if(typeof el == "string"){ // string
34197                 if(el == "separator" || el == "-"){
34198                     item = this.addSeparator();
34199                 }else{
34200                     item = this.addText(el);
34201                 }
34202             }else if(el.tagName || el.el){ // element
34203                 item = this.addElement(el);
34204             }else if(typeof el == "object"){ // must be menu item config?
34205                 item = this.addMenuItem(el);
34206             }
34207         }
34208         return item;
34209     },
34210
34211     /**
34212      * Returns this menu's underlying {@link Roo.Element} object
34213      * @return {Roo.Element} The element
34214      */
34215     getEl : function(){
34216         if(!this.el){
34217             this.render();
34218         }
34219         return this.el;
34220     },
34221
34222     /**
34223      * Adds a separator bar to the menu
34224      * @return {Roo.menu.Item} The menu item that was added
34225      */
34226     addSeparator : function(){
34227         return this.addItem(new Roo.menu.Separator());
34228     },
34229
34230     /**
34231      * Adds an {@link Roo.Element} object to the menu
34232      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34233      * @return {Roo.menu.Item} The menu item that was added
34234      */
34235     addElement : function(el){
34236         return this.addItem(new Roo.menu.BaseItem(el));
34237     },
34238
34239     /**
34240      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34241      * @param {Roo.menu.Item} item The menu item to add
34242      * @return {Roo.menu.Item} The menu item that was added
34243      */
34244     addItem : function(item){
34245         this.items.add(item);
34246         if(this.ul){
34247             var li = document.createElement("li");
34248             li.className = "x-menu-list-item";
34249             this.ul.dom.appendChild(li);
34250             item.render(li, this);
34251             this.delayAutoWidth();
34252         }
34253         return item;
34254     },
34255
34256     /**
34257      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34258      * @param {Object} config A MenuItem config object
34259      * @return {Roo.menu.Item} The menu item that was added
34260      */
34261     addMenuItem : function(config){
34262         if(!(config instanceof Roo.menu.Item)){
34263             if(typeof config.checked == "boolean"){ // must be check menu item config?
34264                 config = new Roo.menu.CheckItem(config);
34265             }else{
34266                 config = new Roo.menu.Item(config);
34267             }
34268         }
34269         return this.addItem(config);
34270     },
34271
34272     /**
34273      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34274      * @param {String} text The text to display in the menu item
34275      * @return {Roo.menu.Item} The menu item that was added
34276      */
34277     addText : function(text){
34278         return this.addItem(new Roo.menu.TextItem({ text : text }));
34279     },
34280
34281     /**
34282      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34283      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34284      * @param {Roo.menu.Item} item The menu item to add
34285      * @return {Roo.menu.Item} The menu item that was added
34286      */
34287     insert : function(index, item){
34288         this.items.insert(index, item);
34289         if(this.ul){
34290             var li = document.createElement("li");
34291             li.className = "x-menu-list-item";
34292             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34293             item.render(li, this);
34294             this.delayAutoWidth();
34295         }
34296         return item;
34297     },
34298
34299     /**
34300      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34301      * @param {Roo.menu.Item} item The menu item to remove
34302      */
34303     remove : function(item){
34304         this.items.removeKey(item.id);
34305         item.destroy();
34306     },
34307
34308     /**
34309      * Removes and destroys all items in the menu
34310      */
34311     removeAll : function(){
34312         var f;
34313         while(f = this.items.first()){
34314             this.remove(f);
34315         }
34316     }
34317 });
34318
34319 // MenuNav is a private utility class used internally by the Menu
34320 Roo.menu.MenuNav = function(menu){
34321     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34322     this.scope = this.menu = menu;
34323 };
34324
34325 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34326     doRelay : function(e, h){
34327         var k = e.getKey();
34328         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34329             this.menu.tryActivate(0, 1);
34330             return false;
34331         }
34332         return h.call(this.scope || this, e, this.menu);
34333     },
34334
34335     up : function(e, m){
34336         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34337             m.tryActivate(m.items.length-1, -1);
34338         }
34339     },
34340
34341     down : function(e, m){
34342         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34343             m.tryActivate(0, 1);
34344         }
34345     },
34346
34347     right : function(e, m){
34348         if(m.activeItem){
34349             m.activeItem.expandMenu(true);
34350         }
34351     },
34352
34353     left : function(e, m){
34354         m.hide();
34355         if(m.parentMenu && m.parentMenu.activeItem){
34356             m.parentMenu.activeItem.activate();
34357         }
34358     },
34359
34360     enter : function(e, m){
34361         if(m.activeItem){
34362             e.stopPropagation();
34363             m.activeItem.onClick(e);
34364             m.fireEvent("click", this, m.activeItem);
34365             return true;
34366         }
34367     }
34368 });/*
34369  * Based on:
34370  * Ext JS Library 1.1.1
34371  * Copyright(c) 2006-2007, Ext JS, LLC.
34372  *
34373  * Originally Released Under LGPL - original licence link has changed is not relivant.
34374  *
34375  * Fork - LGPL
34376  * <script type="text/javascript">
34377  */
34378  
34379 /**
34380  * @class Roo.menu.MenuMgr
34381  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34382  * @singleton
34383  */
34384 Roo.menu.MenuMgr = function(){
34385    var menus, active, groups = {}, attached = false, lastShow = new Date();
34386
34387    // private - called when first menu is created
34388    function init(){
34389        menus = {};
34390        active = new Roo.util.MixedCollection();
34391        Roo.get(document).addKeyListener(27, function(){
34392            if(active.length > 0){
34393                hideAll();
34394            }
34395        });
34396    }
34397
34398    // private
34399    function hideAll(){
34400        if(active && active.length > 0){
34401            var c = active.clone();
34402            c.each(function(m){
34403                m.hide();
34404            });
34405        }
34406    }
34407
34408    // private
34409    function onHide(m){
34410        active.remove(m);
34411        if(active.length < 1){
34412            Roo.get(document).un("mousedown", onMouseDown);
34413            attached = false;
34414        }
34415    }
34416
34417    // private
34418    function onShow(m){
34419        var last = active.last();
34420        lastShow = new Date();
34421        active.add(m);
34422        if(!attached){
34423            Roo.get(document).on("mousedown", onMouseDown);
34424            attached = true;
34425        }
34426        if(m.parentMenu){
34427           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34428           m.parentMenu.activeChild = m;
34429        }else if(last && last.isVisible()){
34430           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34431        }
34432    }
34433
34434    // private
34435    function onBeforeHide(m){
34436        if(m.activeChild){
34437            m.activeChild.hide();
34438        }
34439        if(m.autoHideTimer){
34440            clearTimeout(m.autoHideTimer);
34441            delete m.autoHideTimer;
34442        }
34443    }
34444
34445    // private
34446    function onBeforeShow(m){
34447        var pm = m.parentMenu;
34448        if(!pm && !m.allowOtherMenus){
34449            hideAll();
34450        }else if(pm && pm.activeChild && active != m){
34451            pm.activeChild.hide();
34452        }
34453    }
34454
34455    // private
34456    function onMouseDown(e){
34457        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34458            hideAll();
34459        }
34460    }
34461
34462    // private
34463    function onBeforeCheck(mi, state){
34464        if(state){
34465            var g = groups[mi.group];
34466            for(var i = 0, l = g.length; i < l; i++){
34467                if(g[i] != mi){
34468                    g[i].setChecked(false);
34469                }
34470            }
34471        }
34472    }
34473
34474    return {
34475
34476        /**
34477         * Hides all menus that are currently visible
34478         */
34479        hideAll : function(){
34480             hideAll();  
34481        },
34482
34483        // private
34484        register : function(menu){
34485            if(!menus){
34486                init();
34487            }
34488            menus[menu.id] = menu;
34489            menu.on("beforehide", onBeforeHide);
34490            menu.on("hide", onHide);
34491            menu.on("beforeshow", onBeforeShow);
34492            menu.on("show", onShow);
34493            var g = menu.group;
34494            if(g && menu.events["checkchange"]){
34495                if(!groups[g]){
34496                    groups[g] = [];
34497                }
34498                groups[g].push(menu);
34499                menu.on("checkchange", onCheck);
34500            }
34501        },
34502
34503         /**
34504          * Returns a {@link Roo.menu.Menu} object
34505          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34506          * be used to generate and return a new Menu instance.
34507          */
34508        get : function(menu){
34509            if(typeof menu == "string"){ // menu id
34510                return menus[menu];
34511            }else if(menu.events){  // menu instance
34512                return menu;
34513            }else if(typeof menu.length == 'number'){ // array of menu items?
34514                return new Roo.menu.Menu({items:menu});
34515            }else{ // otherwise, must be a config
34516                return new Roo.menu.Menu(menu);
34517            }
34518        },
34519
34520        // private
34521        unregister : function(menu){
34522            delete menus[menu.id];
34523            menu.un("beforehide", onBeforeHide);
34524            menu.un("hide", onHide);
34525            menu.un("beforeshow", onBeforeShow);
34526            menu.un("show", onShow);
34527            var g = menu.group;
34528            if(g && menu.events["checkchange"]){
34529                groups[g].remove(menu);
34530                menu.un("checkchange", onCheck);
34531            }
34532        },
34533
34534        // private
34535        registerCheckable : function(menuItem){
34536            var g = menuItem.group;
34537            if(g){
34538                if(!groups[g]){
34539                    groups[g] = [];
34540                }
34541                groups[g].push(menuItem);
34542                menuItem.on("beforecheckchange", onBeforeCheck);
34543            }
34544        },
34545
34546        // private
34547        unregisterCheckable : function(menuItem){
34548            var g = menuItem.group;
34549            if(g){
34550                groups[g].remove(menuItem);
34551                menuItem.un("beforecheckchange", onBeforeCheck);
34552            }
34553        }
34554    };
34555 }();/*
34556  * Based on:
34557  * Ext JS Library 1.1.1
34558  * Copyright(c) 2006-2007, Ext JS, LLC.
34559  *
34560  * Originally Released Under LGPL - original licence link has changed is not relivant.
34561  *
34562  * Fork - LGPL
34563  * <script type="text/javascript">
34564  */
34565  
34566
34567 /**
34568  * @class Roo.menu.BaseItem
34569  * @extends Roo.Component
34570  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34571  * management and base configuration options shared by all menu components.
34572  * @constructor
34573  * Creates a new BaseItem
34574  * @param {Object} config Configuration options
34575  */
34576 Roo.menu.BaseItem = function(config){
34577     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34578
34579     this.addEvents({
34580         /**
34581          * @event click
34582          * Fires when this item is clicked
34583          * @param {Roo.menu.BaseItem} this
34584          * @param {Roo.EventObject} e
34585          */
34586         click: true,
34587         /**
34588          * @event activate
34589          * Fires when this item is activated
34590          * @param {Roo.menu.BaseItem} this
34591          */
34592         activate : true,
34593         /**
34594          * @event deactivate
34595          * Fires when this item is deactivated
34596          * @param {Roo.menu.BaseItem} this
34597          */
34598         deactivate : true
34599     });
34600
34601     if(this.handler){
34602         this.on("click", this.handler, this.scope, true);
34603     }
34604 };
34605
34606 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34607     /**
34608      * @cfg {Function} handler
34609      * A function that will handle the click event of this menu item (defaults to undefined)
34610      */
34611     /**
34612      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34613      */
34614     canActivate : false,
34615     /**
34616      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34617      */
34618     activeClass : "x-menu-item-active",
34619     /**
34620      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34621      */
34622     hideOnClick : true,
34623     /**
34624      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34625      */
34626     hideDelay : 100,
34627
34628     // private
34629     ctype: "Roo.menu.BaseItem",
34630
34631     // private
34632     actionMode : "container",
34633
34634     // private
34635     render : function(container, parentMenu){
34636         this.parentMenu = parentMenu;
34637         Roo.menu.BaseItem.superclass.render.call(this, container);
34638         this.container.menuItemId = this.id;
34639     },
34640
34641     // private
34642     onRender : function(container, position){
34643         this.el = Roo.get(this.el);
34644         container.dom.appendChild(this.el.dom);
34645     },
34646
34647     // private
34648     onClick : function(e){
34649         if(!this.disabled && this.fireEvent("click", this, e) !== false
34650                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34651             this.handleClick(e);
34652         }else{
34653             e.stopEvent();
34654         }
34655     },
34656
34657     // private
34658     activate : function(){
34659         if(this.disabled){
34660             return false;
34661         }
34662         var li = this.container;
34663         li.addClass(this.activeClass);
34664         this.region = li.getRegion().adjust(2, 2, -2, -2);
34665         this.fireEvent("activate", this);
34666         return true;
34667     },
34668
34669     // private
34670     deactivate : function(){
34671         this.container.removeClass(this.activeClass);
34672         this.fireEvent("deactivate", this);
34673     },
34674
34675     // private
34676     shouldDeactivate : function(e){
34677         return !this.region || !this.region.contains(e.getPoint());
34678     },
34679
34680     // private
34681     handleClick : function(e){
34682         if(this.hideOnClick){
34683             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34684         }
34685     },
34686
34687     // private
34688     expandMenu : function(autoActivate){
34689         // do nothing
34690     },
34691
34692     // private
34693     hideMenu : function(){
34694         // do nothing
34695     }
34696 });/*
34697  * Based on:
34698  * Ext JS Library 1.1.1
34699  * Copyright(c) 2006-2007, Ext JS, LLC.
34700  *
34701  * Originally Released Under LGPL - original licence link has changed is not relivant.
34702  *
34703  * Fork - LGPL
34704  * <script type="text/javascript">
34705  */
34706  
34707 /**
34708  * @class Roo.menu.Adapter
34709  * @extends Roo.menu.BaseItem
34710  * 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.
34711  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34712  * @constructor
34713  * Creates a new Adapter
34714  * @param {Object} config Configuration options
34715  */
34716 Roo.menu.Adapter = function(component, config){
34717     Roo.menu.Adapter.superclass.constructor.call(this, config);
34718     this.component = component;
34719 };
34720 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34721     // private
34722     canActivate : true,
34723
34724     // private
34725     onRender : function(container, position){
34726         this.component.render(container);
34727         this.el = this.component.getEl();
34728     },
34729
34730     // private
34731     activate : function(){
34732         if(this.disabled){
34733             return false;
34734         }
34735         this.component.focus();
34736         this.fireEvent("activate", this);
34737         return true;
34738     },
34739
34740     // private
34741     deactivate : function(){
34742         this.fireEvent("deactivate", this);
34743     },
34744
34745     // private
34746     disable : function(){
34747         this.component.disable();
34748         Roo.menu.Adapter.superclass.disable.call(this);
34749     },
34750
34751     // private
34752     enable : function(){
34753         this.component.enable();
34754         Roo.menu.Adapter.superclass.enable.call(this);
34755     }
34756 });/*
34757  * Based on:
34758  * Ext JS Library 1.1.1
34759  * Copyright(c) 2006-2007, Ext JS, LLC.
34760  *
34761  * Originally Released Under LGPL - original licence link has changed is not relivant.
34762  *
34763  * Fork - LGPL
34764  * <script type="text/javascript">
34765  */
34766
34767 /**
34768  * @class Roo.menu.TextItem
34769  * @extends Roo.menu.BaseItem
34770  * Adds a static text string to a menu, usually used as either a heading or group separator.
34771  * Note: old style constructor with text is still supported.
34772  * 
34773  * @constructor
34774  * Creates a new TextItem
34775  * @param {Object} cfg Configuration
34776  */
34777 Roo.menu.TextItem = function(cfg){
34778     if (typeof(cfg) == 'string') {
34779         this.text = cfg;
34780     } else {
34781         Roo.apply(this,cfg);
34782     }
34783     
34784     Roo.menu.TextItem.superclass.constructor.call(this);
34785 };
34786
34787 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34788     /**
34789      * @cfg {Boolean} text Text to show on item.
34790      */
34791     text : '',
34792     
34793     /**
34794      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34795      */
34796     hideOnClick : false,
34797     /**
34798      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34799      */
34800     itemCls : "x-menu-text",
34801
34802     // private
34803     onRender : function(){
34804         var s = document.createElement("span");
34805         s.className = this.itemCls;
34806         s.innerHTML = this.text;
34807         this.el = s;
34808         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34809     }
34810 });/*
34811  * Based on:
34812  * Ext JS Library 1.1.1
34813  * Copyright(c) 2006-2007, Ext JS, LLC.
34814  *
34815  * Originally Released Under LGPL - original licence link has changed is not relivant.
34816  *
34817  * Fork - LGPL
34818  * <script type="text/javascript">
34819  */
34820
34821 /**
34822  * @class Roo.menu.Separator
34823  * @extends Roo.menu.BaseItem
34824  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34825  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34826  * @constructor
34827  * @param {Object} config Configuration options
34828  */
34829 Roo.menu.Separator = function(config){
34830     Roo.menu.Separator.superclass.constructor.call(this, config);
34831 };
34832
34833 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34834     /**
34835      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34836      */
34837     itemCls : "x-menu-sep",
34838     /**
34839      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34840      */
34841     hideOnClick : false,
34842
34843     // private
34844     onRender : function(li){
34845         var s = document.createElement("span");
34846         s.className = this.itemCls;
34847         s.innerHTML = "&#160;";
34848         this.el = s;
34849         li.addClass("x-menu-sep-li");
34850         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34851     }
34852 });/*
34853  * Based on:
34854  * Ext JS Library 1.1.1
34855  * Copyright(c) 2006-2007, Ext JS, LLC.
34856  *
34857  * Originally Released Under LGPL - original licence link has changed is not relivant.
34858  *
34859  * Fork - LGPL
34860  * <script type="text/javascript">
34861  */
34862 /**
34863  * @class Roo.menu.Item
34864  * @extends Roo.menu.BaseItem
34865  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34866  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34867  * activation and click handling.
34868  * @constructor
34869  * Creates a new Item
34870  * @param {Object} config Configuration options
34871  */
34872 Roo.menu.Item = function(config){
34873     Roo.menu.Item.superclass.constructor.call(this, config);
34874     if(this.menu){
34875         this.menu = Roo.menu.MenuMgr.get(this.menu);
34876     }
34877 };
34878 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34879     
34880     /**
34881      * @cfg {String} text
34882      * The text to show on the menu item.
34883      */
34884     text: '',
34885      /**
34886      * @cfg {String} HTML to render in menu
34887      * The text to show on the menu item (HTML version).
34888      */
34889     html: '',
34890     /**
34891      * @cfg {String} icon
34892      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34893      */
34894     icon: undefined,
34895     /**
34896      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34897      */
34898     itemCls : "x-menu-item",
34899     /**
34900      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34901      */
34902     canActivate : true,
34903     /**
34904      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34905      */
34906     showDelay: 200,
34907     // doc'd in BaseItem
34908     hideDelay: 200,
34909
34910     // private
34911     ctype: "Roo.menu.Item",
34912     
34913     // private
34914     onRender : function(container, position){
34915         var el = document.createElement("a");
34916         el.hideFocus = true;
34917         el.unselectable = "on";
34918         el.href = this.href || "#";
34919         if(this.hrefTarget){
34920             el.target = this.hrefTarget;
34921         }
34922         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34923         
34924         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34925         
34926         el.innerHTML = String.format(
34927                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34928                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34929         this.el = el;
34930         Roo.menu.Item.superclass.onRender.call(this, container, position);
34931     },
34932
34933     /**
34934      * Sets the text to display in this menu item
34935      * @param {String} text The text to display
34936      * @param {Boolean} isHTML true to indicate text is pure html.
34937      */
34938     setText : function(text, isHTML){
34939         if (isHTML) {
34940             this.html = text;
34941         } else {
34942             this.text = text;
34943             this.html = '';
34944         }
34945         if(this.rendered){
34946             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34947      
34948             this.el.update(String.format(
34949                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34950                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34951             this.parentMenu.autoWidth();
34952         }
34953     },
34954
34955     // private
34956     handleClick : function(e){
34957         if(!this.href){ // if no link defined, stop the event automatically
34958             e.stopEvent();
34959         }
34960         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34961     },
34962
34963     // private
34964     activate : function(autoExpand){
34965         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34966             this.focus();
34967             if(autoExpand){
34968                 this.expandMenu();
34969             }
34970         }
34971         return true;
34972     },
34973
34974     // private
34975     shouldDeactivate : function(e){
34976         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34977             if(this.menu && this.menu.isVisible()){
34978                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34979             }
34980             return true;
34981         }
34982         return false;
34983     },
34984
34985     // private
34986     deactivate : function(){
34987         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34988         this.hideMenu();
34989     },
34990
34991     // private
34992     expandMenu : function(autoActivate){
34993         if(!this.disabled && this.menu){
34994             clearTimeout(this.hideTimer);
34995             delete this.hideTimer;
34996             if(!this.menu.isVisible() && !this.showTimer){
34997                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34998             }else if (this.menu.isVisible() && autoActivate){
34999                 this.menu.tryActivate(0, 1);
35000             }
35001         }
35002     },
35003
35004     // private
35005     deferExpand : function(autoActivate){
35006         delete this.showTimer;
35007         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35008         if(autoActivate){
35009             this.menu.tryActivate(0, 1);
35010         }
35011     },
35012
35013     // private
35014     hideMenu : function(){
35015         clearTimeout(this.showTimer);
35016         delete this.showTimer;
35017         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35018             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35019         }
35020     },
35021
35022     // private
35023     deferHide : function(){
35024         delete this.hideTimer;
35025         this.menu.hide();
35026     }
35027 });/*
35028  * Based on:
35029  * Ext JS Library 1.1.1
35030  * Copyright(c) 2006-2007, Ext JS, LLC.
35031  *
35032  * Originally Released Under LGPL - original licence link has changed is not relivant.
35033  *
35034  * Fork - LGPL
35035  * <script type="text/javascript">
35036  */
35037  
35038 /**
35039  * @class Roo.menu.CheckItem
35040  * @extends Roo.menu.Item
35041  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35042  * @constructor
35043  * Creates a new CheckItem
35044  * @param {Object} config Configuration options
35045  */
35046 Roo.menu.CheckItem = function(config){
35047     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35048     this.addEvents({
35049         /**
35050          * @event beforecheckchange
35051          * Fires before the checked value is set, providing an opportunity to cancel if needed
35052          * @param {Roo.menu.CheckItem} this
35053          * @param {Boolean} checked The new checked value that will be set
35054          */
35055         "beforecheckchange" : true,
35056         /**
35057          * @event checkchange
35058          * Fires after the checked value has been set
35059          * @param {Roo.menu.CheckItem} this
35060          * @param {Boolean} checked The checked value that was set
35061          */
35062         "checkchange" : true
35063     });
35064     if(this.checkHandler){
35065         this.on('checkchange', this.checkHandler, this.scope);
35066     }
35067 };
35068 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35069     /**
35070      * @cfg {String} group
35071      * All check items with the same group name will automatically be grouped into a single-select
35072      * radio button group (defaults to '')
35073      */
35074     /**
35075      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35076      */
35077     itemCls : "x-menu-item x-menu-check-item",
35078     /**
35079      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35080      */
35081     groupClass : "x-menu-group-item",
35082
35083     /**
35084      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35085      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35086      * initialized with checked = true will be rendered as checked.
35087      */
35088     checked: false,
35089
35090     // private
35091     ctype: "Roo.menu.CheckItem",
35092
35093     // private
35094     onRender : function(c){
35095         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35096         if(this.group){
35097             this.el.addClass(this.groupClass);
35098         }
35099         Roo.menu.MenuMgr.registerCheckable(this);
35100         if(this.checked){
35101             this.checked = false;
35102             this.setChecked(true, true);
35103         }
35104     },
35105
35106     // private
35107     destroy : function(){
35108         if(this.rendered){
35109             Roo.menu.MenuMgr.unregisterCheckable(this);
35110         }
35111         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35112     },
35113
35114     /**
35115      * Set the checked state of this item
35116      * @param {Boolean} checked The new checked value
35117      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35118      */
35119     setChecked : function(state, suppressEvent){
35120         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35121             if(this.container){
35122                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35123             }
35124             this.checked = state;
35125             if(suppressEvent !== true){
35126                 this.fireEvent("checkchange", this, state);
35127             }
35128         }
35129     },
35130
35131     // private
35132     handleClick : function(e){
35133        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35134            this.setChecked(!this.checked);
35135        }
35136        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35137     }
35138 });/*
35139  * Based on:
35140  * Ext JS Library 1.1.1
35141  * Copyright(c) 2006-2007, Ext JS, LLC.
35142  *
35143  * Originally Released Under LGPL - original licence link has changed is not relivant.
35144  *
35145  * Fork - LGPL
35146  * <script type="text/javascript">
35147  */
35148  
35149 /**
35150  * @class Roo.menu.DateItem
35151  * @extends Roo.menu.Adapter
35152  * A menu item that wraps the {@link Roo.DatPicker} component.
35153  * @constructor
35154  * Creates a new DateItem
35155  * @param {Object} config Configuration options
35156  */
35157 Roo.menu.DateItem = function(config){
35158     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35159     /** The Roo.DatePicker object @type Roo.DatePicker */
35160     this.picker = this.component;
35161     this.addEvents({select: true});
35162     
35163     this.picker.on("render", function(picker){
35164         picker.getEl().swallowEvent("click");
35165         picker.container.addClass("x-menu-date-item");
35166     });
35167
35168     this.picker.on("select", this.onSelect, this);
35169 };
35170
35171 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35172     // private
35173     onSelect : function(picker, date){
35174         this.fireEvent("select", this, date, picker);
35175         Roo.menu.DateItem.superclass.handleClick.call(this);
35176     }
35177 });/*
35178  * Based on:
35179  * Ext JS Library 1.1.1
35180  * Copyright(c) 2006-2007, Ext JS, LLC.
35181  *
35182  * Originally Released Under LGPL - original licence link has changed is not relivant.
35183  *
35184  * Fork - LGPL
35185  * <script type="text/javascript">
35186  */
35187  
35188 /**
35189  * @class Roo.menu.ColorItem
35190  * @extends Roo.menu.Adapter
35191  * A menu item that wraps the {@link Roo.ColorPalette} component.
35192  * @constructor
35193  * Creates a new ColorItem
35194  * @param {Object} config Configuration options
35195  */
35196 Roo.menu.ColorItem = function(config){
35197     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35198     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35199     this.palette = this.component;
35200     this.relayEvents(this.palette, ["select"]);
35201     if(this.selectHandler){
35202         this.on('select', this.selectHandler, this.scope);
35203     }
35204 };
35205 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35206  * Based on:
35207  * Ext JS Library 1.1.1
35208  * Copyright(c) 2006-2007, Ext JS, LLC.
35209  *
35210  * Originally Released Under LGPL - original licence link has changed is not relivant.
35211  *
35212  * Fork - LGPL
35213  * <script type="text/javascript">
35214  */
35215  
35216
35217 /**
35218  * @class Roo.menu.DateMenu
35219  * @extends Roo.menu.Menu
35220  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35221  * @constructor
35222  * Creates a new DateMenu
35223  * @param {Object} config Configuration options
35224  */
35225 Roo.menu.DateMenu = function(config){
35226     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35227     this.plain = true;
35228     var di = new Roo.menu.DateItem(config);
35229     this.add(di);
35230     /**
35231      * The {@link Roo.DatePicker} instance for this DateMenu
35232      * @type DatePicker
35233      */
35234     this.picker = di.picker;
35235     /**
35236      * @event select
35237      * @param {DatePicker} picker
35238      * @param {Date} date
35239      */
35240     this.relayEvents(di, ["select"]);
35241
35242     this.on('beforeshow', function(){
35243         if(this.picker){
35244             this.picker.hideMonthPicker(true);
35245         }
35246     }, this);
35247 };
35248 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35249     cls:'x-date-menu'
35250 });/*
35251  * Based on:
35252  * Ext JS Library 1.1.1
35253  * Copyright(c) 2006-2007, Ext JS, LLC.
35254  *
35255  * Originally Released Under LGPL - original licence link has changed is not relivant.
35256  *
35257  * Fork - LGPL
35258  * <script type="text/javascript">
35259  */
35260  
35261
35262 /**
35263  * @class Roo.menu.ColorMenu
35264  * @extends Roo.menu.Menu
35265  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35266  * @constructor
35267  * Creates a new ColorMenu
35268  * @param {Object} config Configuration options
35269  */
35270 Roo.menu.ColorMenu = function(config){
35271     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35272     this.plain = true;
35273     var ci = new Roo.menu.ColorItem(config);
35274     this.add(ci);
35275     /**
35276      * The {@link Roo.ColorPalette} instance for this ColorMenu
35277      * @type ColorPalette
35278      */
35279     this.palette = ci.palette;
35280     /**
35281      * @event select
35282      * @param {ColorPalette} palette
35283      * @param {String} color
35284      */
35285     this.relayEvents(ci, ["select"]);
35286 };
35287 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35288  * Based on:
35289  * Ext JS Library 1.1.1
35290  * Copyright(c) 2006-2007, Ext JS, LLC.
35291  *
35292  * Originally Released Under LGPL - original licence link has changed is not relivant.
35293  *
35294  * Fork - LGPL
35295  * <script type="text/javascript">
35296  */
35297  
35298 /**
35299  * @class Roo.form.Field
35300  * @extends Roo.BoxComponent
35301  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35302  * @constructor
35303  * Creates a new Field
35304  * @param {Object} config Configuration options
35305  */
35306 Roo.form.Field = function(config){
35307     Roo.form.Field.superclass.constructor.call(this, config);
35308 };
35309
35310 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35311     /**
35312      * @cfg {String} fieldLabel Label to use when rendering a form.
35313      */
35314        /**
35315      * @cfg {String} qtip Mouse over tip
35316      */
35317      
35318     /**
35319      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35320      */
35321     invalidClass : "x-form-invalid",
35322     /**
35323      * @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")
35324      */
35325     invalidText : "The value in this field is invalid",
35326     /**
35327      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35328      */
35329     focusClass : "x-form-focus",
35330     /**
35331      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35332       automatic validation (defaults to "keyup").
35333      */
35334     validationEvent : "keyup",
35335     /**
35336      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35337      */
35338     validateOnBlur : true,
35339     /**
35340      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35341      */
35342     validationDelay : 250,
35343     /**
35344      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35345      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35346      */
35347     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35348     /**
35349      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35350      */
35351     fieldClass : "x-form-field",
35352     /**
35353      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35354      *<pre>
35355 Value         Description
35356 -----------   ----------------------------------------------------------------------
35357 qtip          Display a quick tip when the user hovers over the field
35358 title         Display a default browser title attribute popup
35359 under         Add a block div beneath the field containing the error text
35360 side          Add an error icon to the right of the field with a popup on hover
35361 [element id]  Add the error text directly to the innerHTML of the specified element
35362 </pre>
35363      */
35364     msgTarget : 'qtip',
35365     /**
35366      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35367      */
35368     msgFx : 'normal',
35369
35370     /**
35371      * @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.
35372      */
35373     readOnly : false,
35374
35375     /**
35376      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35377      */
35378     disabled : false,
35379
35380     /**
35381      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35382      */
35383     inputType : undefined,
35384     
35385     /**
35386      * @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).
35387          */
35388         tabIndex : undefined,
35389         
35390     // private
35391     isFormField : true,
35392
35393     // private
35394     hasFocus : false,
35395     /**
35396      * @property {Roo.Element} fieldEl
35397      * Element Containing the rendered Field (with label etc.)
35398      */
35399     /**
35400      * @cfg {Mixed} value A value to initialize this field with.
35401      */
35402     value : undefined,
35403
35404     /**
35405      * @cfg {String} name The field's HTML name attribute.
35406      */
35407     /**
35408      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35409      */
35410
35411         // private ??
35412         initComponent : function(){
35413         Roo.form.Field.superclass.initComponent.call(this);
35414         this.addEvents({
35415             /**
35416              * @event focus
35417              * Fires when this field receives input focus.
35418              * @param {Roo.form.Field} this
35419              */
35420             focus : true,
35421             /**
35422              * @event blur
35423              * Fires when this field loses input focus.
35424              * @param {Roo.form.Field} this
35425              */
35426             blur : true,
35427             /**
35428              * @event specialkey
35429              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35430              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35431              * @param {Roo.form.Field} this
35432              * @param {Roo.EventObject} e The event object
35433              */
35434             specialkey : true,
35435             /**
35436              * @event change
35437              * Fires just before the field blurs if the field value has changed.
35438              * @param {Roo.form.Field} this
35439              * @param {Mixed} newValue The new value
35440              * @param {Mixed} oldValue The original value
35441              */
35442             change : true,
35443             /**
35444              * @event invalid
35445              * Fires after the field has been marked as invalid.
35446              * @param {Roo.form.Field} this
35447              * @param {String} msg The validation message
35448              */
35449             invalid : true,
35450             /**
35451              * @event valid
35452              * Fires after the field has been validated with no errors.
35453              * @param {Roo.form.Field} this
35454              */
35455             valid : true,
35456              /**
35457              * @event keyup
35458              * Fires after the key up
35459              * @param {Roo.form.Field} this
35460              * @param {Roo.EventObject}  e The event Object
35461              */
35462             keyup : true
35463         });
35464     },
35465
35466     /**
35467      * Returns the name attribute of the field if available
35468      * @return {String} name The field name
35469      */
35470     getName: function(){
35471          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35472     },
35473
35474     // private
35475     onRender : function(ct, position){
35476         Roo.form.Field.superclass.onRender.call(this, ct, position);
35477         if(!this.el){
35478             var cfg = this.getAutoCreate();
35479             if(!cfg.name){
35480                 cfg.name = this.name || this.id;
35481             }
35482             if(this.inputType){
35483                 cfg.type = this.inputType;
35484             }
35485             this.el = ct.createChild(cfg, position);
35486         }
35487         var type = this.el.dom.type;
35488         if(type){
35489             if(type == 'password'){
35490                 type = 'text';
35491             }
35492             this.el.addClass('x-form-'+type);
35493         }
35494         if(this.readOnly){
35495             this.el.dom.readOnly = true;
35496         }
35497         if(this.tabIndex !== undefined){
35498             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35499         }
35500
35501         this.el.addClass([this.fieldClass, this.cls]);
35502         this.initValue();
35503     },
35504
35505     /**
35506      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35507      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35508      * @return {Roo.form.Field} this
35509      */
35510     applyTo : function(target){
35511         this.allowDomMove = false;
35512         this.el = Roo.get(target);
35513         this.render(this.el.dom.parentNode);
35514         return this;
35515     },
35516
35517     // private
35518     initValue : function(){
35519         if(this.value !== undefined){
35520             this.setValue(this.value);
35521         }else if(this.el.dom.value.length > 0){
35522             this.setValue(this.el.dom.value);
35523         }
35524     },
35525
35526     /**
35527      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35528      */
35529     isDirty : function() {
35530         if(this.disabled) {
35531             return false;
35532         }
35533         return String(this.getValue()) !== String(this.originalValue);
35534     },
35535
35536     // private
35537     afterRender : function(){
35538         Roo.form.Field.superclass.afterRender.call(this);
35539         this.initEvents();
35540     },
35541
35542     // private
35543     fireKey : function(e){
35544         //Roo.log('field ' + e.getKey());
35545         if(e.isNavKeyPress()){
35546             this.fireEvent("specialkey", this, e);
35547         }
35548     },
35549
35550     /**
35551      * Resets the current field value to the originally loaded value and clears any validation messages
35552      */
35553     reset : function(){
35554         this.setValue(this.originalValue);
35555         this.clearInvalid();
35556     },
35557
35558     // private
35559     initEvents : function(){
35560         // safari killled keypress - so keydown is now used..
35561         this.el.on("keydown" , this.fireKey,  this);
35562         this.el.on("focus", this.onFocus,  this);
35563         this.el.on("blur", this.onBlur,  this);
35564         this.el.relayEvent('keyup', this);
35565
35566         // reference to original value for reset
35567         this.originalValue = this.getValue();
35568     },
35569
35570     // private
35571     onFocus : function(){
35572         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35573             this.el.addClass(this.focusClass);
35574         }
35575         if(!this.hasFocus){
35576             this.hasFocus = true;
35577             this.startValue = this.getValue();
35578             this.fireEvent("focus", this);
35579         }
35580     },
35581
35582     beforeBlur : Roo.emptyFn,
35583
35584     // private
35585     onBlur : function(){
35586         this.beforeBlur();
35587         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35588             this.el.removeClass(this.focusClass);
35589         }
35590         this.hasFocus = false;
35591         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35592             this.validate();
35593         }
35594         var v = this.getValue();
35595         if(String(v) !== String(this.startValue)){
35596             this.fireEvent('change', this, v, this.startValue);
35597         }
35598         this.fireEvent("blur", this);
35599     },
35600
35601     /**
35602      * Returns whether or not the field value is currently valid
35603      * @param {Boolean} preventMark True to disable marking the field invalid
35604      * @return {Boolean} True if the value is valid, else false
35605      */
35606     isValid : function(preventMark){
35607         if(this.disabled){
35608             return true;
35609         }
35610         var restore = this.preventMark;
35611         this.preventMark = preventMark === true;
35612         var v = this.validateValue(this.processValue(this.getRawValue()));
35613         this.preventMark = restore;
35614         return v;
35615     },
35616
35617     /**
35618      * Validates the field value
35619      * @return {Boolean} True if the value is valid, else false
35620      */
35621     validate : function(){
35622         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35623             this.clearInvalid();
35624             return true;
35625         }
35626         return false;
35627     },
35628
35629     processValue : function(value){
35630         return value;
35631     },
35632
35633     // private
35634     // Subclasses should provide the validation implementation by overriding this
35635     validateValue : function(value){
35636         return true;
35637     },
35638
35639     /**
35640      * Mark this field as invalid
35641      * @param {String} msg The validation message
35642      */
35643     markInvalid : function(msg){
35644         if(!this.rendered || this.preventMark){ // not rendered
35645             return;
35646         }
35647         this.el.addClass(this.invalidClass);
35648         msg = msg || this.invalidText;
35649         switch(this.msgTarget){
35650             case 'qtip':
35651                 this.el.dom.qtip = msg;
35652                 this.el.dom.qclass = 'x-form-invalid-tip';
35653                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35654                     Roo.QuickTips.enable();
35655                 }
35656                 break;
35657             case 'title':
35658                 this.el.dom.title = msg;
35659                 break;
35660             case 'under':
35661                 if(!this.errorEl){
35662                     var elp = this.el.findParent('.x-form-element', 5, true);
35663                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35664                     this.errorEl.setWidth(elp.getWidth(true)-20);
35665                 }
35666                 this.errorEl.update(msg);
35667                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35668                 break;
35669             case 'side':
35670                 if(!this.errorIcon){
35671                     var elp = this.el.findParent('.x-form-element', 5, true);
35672                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35673                 }
35674                 this.alignErrorIcon();
35675                 this.errorIcon.dom.qtip = msg;
35676                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35677                 this.errorIcon.show();
35678                 this.on('resize', this.alignErrorIcon, this);
35679                 break;
35680             default:
35681                 var t = Roo.getDom(this.msgTarget);
35682                 t.innerHTML = msg;
35683                 t.style.display = this.msgDisplay;
35684                 break;
35685         }
35686         this.fireEvent('invalid', this, msg);
35687     },
35688
35689     // private
35690     alignErrorIcon : function(){
35691         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35692     },
35693
35694     /**
35695      * Clear any invalid styles/messages for this field
35696      */
35697     clearInvalid : function(){
35698         if(!this.rendered || this.preventMark){ // not rendered
35699             return;
35700         }
35701         this.el.removeClass(this.invalidClass);
35702         switch(this.msgTarget){
35703             case 'qtip':
35704                 this.el.dom.qtip = '';
35705                 break;
35706             case 'title':
35707                 this.el.dom.title = '';
35708                 break;
35709             case 'under':
35710                 if(this.errorEl){
35711                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35712                 }
35713                 break;
35714             case 'side':
35715                 if(this.errorIcon){
35716                     this.errorIcon.dom.qtip = '';
35717                     this.errorIcon.hide();
35718                     this.un('resize', this.alignErrorIcon, this);
35719                 }
35720                 break;
35721             default:
35722                 var t = Roo.getDom(this.msgTarget);
35723                 t.innerHTML = '';
35724                 t.style.display = 'none';
35725                 break;
35726         }
35727         this.fireEvent('valid', this);
35728     },
35729
35730     /**
35731      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35732      * @return {Mixed} value The field value
35733      */
35734     getRawValue : function(){
35735         var v = this.el.getValue();
35736         if(v === this.emptyText){
35737             v = '';
35738         }
35739         return v;
35740     },
35741
35742     /**
35743      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35744      * @return {Mixed} value The field value
35745      */
35746     getValue : function(){
35747         var v = this.el.getValue();
35748         if(v === this.emptyText || v === undefined){
35749             v = '';
35750         }
35751         return v;
35752     },
35753
35754     /**
35755      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35756      * @param {Mixed} value The value to set
35757      */
35758     setRawValue : function(v){
35759         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35760     },
35761
35762     /**
35763      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35764      * @param {Mixed} value The value to set
35765      */
35766     setValue : function(v){
35767         this.value = v;
35768         if(this.rendered){
35769             this.el.dom.value = (v === null || v === undefined ? '' : v);
35770             this.validate();
35771         }
35772     },
35773
35774     adjustSize : function(w, h){
35775         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35776         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35777         return s;
35778     },
35779
35780     adjustWidth : function(tag, w){
35781         tag = tag.toLowerCase();
35782         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35783             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35784                 if(tag == 'input'){
35785                     return w + 2;
35786                 }
35787                 if(tag = 'textarea'){
35788                     return w-2;
35789                 }
35790             }else if(Roo.isOpera){
35791                 if(tag == 'input'){
35792                     return w + 2;
35793                 }
35794                 if(tag = 'textarea'){
35795                     return w-2;
35796                 }
35797             }
35798         }
35799         return w;
35800     }
35801 });
35802
35803
35804 // anything other than normal should be considered experimental
35805 Roo.form.Field.msgFx = {
35806     normal : {
35807         show: function(msgEl, f){
35808             msgEl.setDisplayed('block');
35809         },
35810
35811         hide : function(msgEl, f){
35812             msgEl.setDisplayed(false).update('');
35813         }
35814     },
35815
35816     slide : {
35817         show: function(msgEl, f){
35818             msgEl.slideIn('t', {stopFx:true});
35819         },
35820
35821         hide : function(msgEl, f){
35822             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35823         }
35824     },
35825
35826     slideRight : {
35827         show: function(msgEl, f){
35828             msgEl.fixDisplay();
35829             msgEl.alignTo(f.el, 'tl-tr');
35830             msgEl.slideIn('l', {stopFx:true});
35831         },
35832
35833         hide : function(msgEl, f){
35834             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35835         }
35836     }
35837 };/*
35838  * Based on:
35839  * Ext JS Library 1.1.1
35840  * Copyright(c) 2006-2007, Ext JS, LLC.
35841  *
35842  * Originally Released Under LGPL - original licence link has changed is not relivant.
35843  *
35844  * Fork - LGPL
35845  * <script type="text/javascript">
35846  */
35847  
35848
35849 /**
35850  * @class Roo.form.TextField
35851  * @extends Roo.form.Field
35852  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35853  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35854  * @constructor
35855  * Creates a new TextField
35856  * @param {Object} config Configuration options
35857  */
35858 Roo.form.TextField = function(config){
35859     Roo.form.TextField.superclass.constructor.call(this, config);
35860     this.addEvents({
35861         /**
35862          * @event autosize
35863          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35864          * according to the default logic, but this event provides a hook for the developer to apply additional
35865          * logic at runtime to resize the field if needed.
35866              * @param {Roo.form.Field} this This text field
35867              * @param {Number} width The new field width
35868              */
35869         autosize : true
35870     });
35871 };
35872
35873 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35874     /**
35875      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35876      */
35877     grow : false,
35878     /**
35879      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35880      */
35881     growMin : 30,
35882     /**
35883      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35884      */
35885     growMax : 800,
35886     /**
35887      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35888      */
35889     vtype : null,
35890     /**
35891      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35892      */
35893     maskRe : null,
35894     /**
35895      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35896      */
35897     disableKeyFilter : false,
35898     /**
35899      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35900      */
35901     allowBlank : true,
35902     /**
35903      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35904      */
35905     minLength : 0,
35906     /**
35907      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35908      */
35909     maxLength : Number.MAX_VALUE,
35910     /**
35911      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35912      */
35913     minLengthText : "The minimum length for this field is {0}",
35914     /**
35915      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35916      */
35917     maxLengthText : "The maximum length for this field is {0}",
35918     /**
35919      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35920      */
35921     selectOnFocus : false,
35922     /**
35923      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35924      */
35925     blankText : "This field is required",
35926     /**
35927      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35928      * If available, this function will be called only after the basic validators all return true, and will be passed the
35929      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35930      */
35931     validator : null,
35932     /**
35933      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35934      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35935      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35936      */
35937     regex : null,
35938     /**
35939      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35940      */
35941     regexText : "",
35942     /**
35943      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35944      */
35945     emptyText : null,
35946     /**
35947      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35948      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35949      */
35950     emptyClass : 'x-form-empty-field',
35951
35952     // private
35953     initEvents : function(){
35954         Roo.form.TextField.superclass.initEvents.call(this);
35955         if(this.validationEvent == 'keyup'){
35956             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35957             this.el.on('keyup', this.filterValidation, this);
35958         }
35959         else if(this.validationEvent !== false){
35960             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35961         }
35962         if(this.selectOnFocus || this.emptyText){
35963             this.on("focus", this.preFocus, this);
35964             if(this.emptyText){
35965                 this.on('blur', this.postBlur, this);
35966                 this.applyEmptyText();
35967             }
35968         }
35969         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35970             this.el.on("keypress", this.filterKeys, this);
35971         }
35972         if(this.grow){
35973             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35974             this.el.on("click", this.autoSize,  this);
35975         }
35976     },
35977
35978     processValue : function(value){
35979         if(this.stripCharsRe){
35980             var newValue = value.replace(this.stripCharsRe, '');
35981             if(newValue !== value){
35982                 this.setRawValue(newValue);
35983                 return newValue;
35984             }
35985         }
35986         return value;
35987     },
35988
35989     filterValidation : function(e){
35990         if(!e.isNavKeyPress()){
35991             this.validationTask.delay(this.validationDelay);
35992         }
35993     },
35994
35995     // private
35996     onKeyUp : function(e){
35997         if(!e.isNavKeyPress()){
35998             this.autoSize();
35999         }
36000     },
36001
36002     /**
36003      * Resets the current field value to the originally-loaded value and clears any validation messages.
36004      * Also adds emptyText and emptyClass if the original value was blank.
36005      */
36006     reset : function(){
36007         Roo.form.TextField.superclass.reset.call(this);
36008         this.applyEmptyText();
36009     },
36010
36011     applyEmptyText : function(){
36012         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36013             this.setRawValue(this.emptyText);
36014             this.el.addClass(this.emptyClass);
36015         }
36016     },
36017
36018     // private
36019     preFocus : function(){
36020         if(this.emptyText){
36021             if(this.el.dom.value == this.emptyText){
36022                 this.setRawValue('');
36023             }
36024             this.el.removeClass(this.emptyClass);
36025         }
36026         if(this.selectOnFocus){
36027             this.el.dom.select();
36028         }
36029     },
36030
36031     // private
36032     postBlur : function(){
36033         this.applyEmptyText();
36034     },
36035
36036     // private
36037     filterKeys : function(e){
36038         var k = e.getKey();
36039         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36040             return;
36041         }
36042         var c = e.getCharCode(), cc = String.fromCharCode(c);
36043         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36044             return;
36045         }
36046         if(!this.maskRe.test(cc)){
36047             e.stopEvent();
36048         }
36049     },
36050
36051     setValue : function(v){
36052         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36053             this.el.removeClass(this.emptyClass);
36054         }
36055         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36056         this.applyEmptyText();
36057         this.autoSize();
36058     },
36059
36060     /**
36061      * Validates a value according to the field's validation rules and marks the field as invalid
36062      * if the validation fails
36063      * @param {Mixed} value The value to validate
36064      * @return {Boolean} True if the value is valid, else false
36065      */
36066     validateValue : function(value){
36067         if(value.length < 1 || value === this.emptyText){ // if it's blank
36068              if(this.allowBlank){
36069                 this.clearInvalid();
36070                 return true;
36071              }else{
36072                 this.markInvalid(this.blankText);
36073                 return false;
36074              }
36075         }
36076         if(value.length < this.minLength){
36077             this.markInvalid(String.format(this.minLengthText, this.minLength));
36078             return false;
36079         }
36080         if(value.length > this.maxLength){
36081             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36082             return false;
36083         }
36084         if(this.vtype){
36085             var vt = Roo.form.VTypes;
36086             if(!vt[this.vtype](value, this)){
36087                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36088                 return false;
36089             }
36090         }
36091         if(typeof this.validator == "function"){
36092             var msg = this.validator(value);
36093             if(msg !== true){
36094                 this.markInvalid(msg);
36095                 return false;
36096             }
36097         }
36098         if(this.regex && !this.regex.test(value)){
36099             this.markInvalid(this.regexText);
36100             return false;
36101         }
36102         return true;
36103     },
36104
36105     /**
36106      * Selects text in this field
36107      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36108      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36109      */
36110     selectText : function(start, end){
36111         var v = this.getRawValue();
36112         if(v.length > 0){
36113             start = start === undefined ? 0 : start;
36114             end = end === undefined ? v.length : end;
36115             var d = this.el.dom;
36116             if(d.setSelectionRange){
36117                 d.setSelectionRange(start, end);
36118             }else if(d.createTextRange){
36119                 var range = d.createTextRange();
36120                 range.moveStart("character", start);
36121                 range.moveEnd("character", v.length-end);
36122                 range.select();
36123             }
36124         }
36125     },
36126
36127     /**
36128      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36129      * This only takes effect if grow = true, and fires the autosize event.
36130      */
36131     autoSize : function(){
36132         if(!this.grow || !this.rendered){
36133             return;
36134         }
36135         if(!this.metrics){
36136             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36137         }
36138         var el = this.el;
36139         var v = el.dom.value;
36140         var d = document.createElement('div');
36141         d.appendChild(document.createTextNode(v));
36142         v = d.innerHTML;
36143         d = null;
36144         v += "&#160;";
36145         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36146         this.el.setWidth(w);
36147         this.fireEvent("autosize", this, w);
36148     }
36149 });/*
36150  * Based on:
36151  * Ext JS Library 1.1.1
36152  * Copyright(c) 2006-2007, Ext JS, LLC.
36153  *
36154  * Originally Released Under LGPL - original licence link has changed is not relivant.
36155  *
36156  * Fork - LGPL
36157  * <script type="text/javascript">
36158  */
36159  
36160 /**
36161  * @class Roo.form.Hidden
36162  * @extends Roo.form.TextField
36163  * Simple Hidden element used on forms 
36164  * 
36165  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36166  * 
36167  * @constructor
36168  * Creates a new Hidden form element.
36169  * @param {Object} config Configuration options
36170  */
36171
36172
36173
36174 // easy hidden field...
36175 Roo.form.Hidden = function(config){
36176     Roo.form.Hidden.superclass.constructor.call(this, config);
36177 };
36178   
36179 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36180     fieldLabel:      '',
36181     inputType:      'hidden',
36182     width:          50,
36183     allowBlank:     true,
36184     labelSeparator: '',
36185     hidden:         true,
36186     itemCls :       'x-form-item-display-none'
36187
36188
36189 });
36190
36191
36192 /*
36193  * Based on:
36194  * Ext JS Library 1.1.1
36195  * Copyright(c) 2006-2007, Ext JS, LLC.
36196  *
36197  * Originally Released Under LGPL - original licence link has changed is not relivant.
36198  *
36199  * Fork - LGPL
36200  * <script type="text/javascript">
36201  */
36202  
36203 /**
36204  * @class Roo.form.TriggerField
36205  * @extends Roo.form.TextField
36206  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36207  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36208  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36209  * for which you can provide a custom implementation.  For example:
36210  * <pre><code>
36211 var trigger = new Roo.form.TriggerField();
36212 trigger.onTriggerClick = myTriggerFn;
36213 trigger.applyTo('my-field');
36214 </code></pre>
36215  *
36216  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36217  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36218  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36219  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36220  * @constructor
36221  * Create a new TriggerField.
36222  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36223  * to the base TextField)
36224  */
36225 Roo.form.TriggerField = function(config){
36226     this.mimicing = false;
36227     Roo.form.TriggerField.superclass.constructor.call(this, config);
36228 };
36229
36230 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36231     /**
36232      * @cfg {String} triggerClass A CSS class to apply to the trigger
36233      */
36234     /**
36235      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36236      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36237      */
36238     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36239     /**
36240      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36241      */
36242     hideTrigger:false,
36243
36244     /** @cfg {Boolean} grow @hide */
36245     /** @cfg {Number} growMin @hide */
36246     /** @cfg {Number} growMax @hide */
36247
36248     /**
36249      * @hide 
36250      * @method
36251      */
36252     autoSize: Roo.emptyFn,
36253     // private
36254     monitorTab : true,
36255     // private
36256     deferHeight : true,
36257
36258     
36259     actionMode : 'wrap',
36260     // private
36261     onResize : function(w, h){
36262         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36263         if(typeof w == 'number'){
36264             var x = w - this.trigger.getWidth();
36265             this.el.setWidth(this.adjustWidth('input', x));
36266             this.trigger.setStyle('left', x+'px');
36267         }
36268     },
36269
36270     // private
36271     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36272
36273     // private
36274     getResizeEl : function(){
36275         return this.wrap;
36276     },
36277
36278     // private
36279     getPositionEl : function(){
36280         return this.wrap;
36281     },
36282
36283     // private
36284     alignErrorIcon : function(){
36285         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36286     },
36287
36288     // private
36289     onRender : function(ct, position){
36290         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36291         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36292         this.trigger = this.wrap.createChild(this.triggerConfig ||
36293                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36294         if(this.hideTrigger){
36295             this.trigger.setDisplayed(false);
36296         }
36297         this.initTrigger();
36298         if(!this.width){
36299             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36300         }
36301     },
36302
36303     // private
36304     initTrigger : function(){
36305         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36306         this.trigger.addClassOnOver('x-form-trigger-over');
36307         this.trigger.addClassOnClick('x-form-trigger-click');
36308     },
36309
36310     // private
36311     onDestroy : function(){
36312         if(this.trigger){
36313             this.trigger.removeAllListeners();
36314             this.trigger.remove();
36315         }
36316         if(this.wrap){
36317             this.wrap.remove();
36318         }
36319         Roo.form.TriggerField.superclass.onDestroy.call(this);
36320     },
36321
36322     // private
36323     onFocus : function(){
36324         Roo.form.TriggerField.superclass.onFocus.call(this);
36325         if(!this.mimicing){
36326             this.wrap.addClass('x-trigger-wrap-focus');
36327             this.mimicing = true;
36328             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36329             if(this.monitorTab){
36330                 this.el.on("keydown", this.checkTab, this);
36331             }
36332         }
36333     },
36334
36335     // private
36336     checkTab : function(e){
36337         if(e.getKey() == e.TAB){
36338             this.triggerBlur();
36339         }
36340     },
36341
36342     // private
36343     onBlur : function(){
36344         // do nothing
36345     },
36346
36347     // private
36348     mimicBlur : function(e, t){
36349         if(!this.wrap.contains(t) && this.validateBlur()){
36350             this.triggerBlur();
36351         }
36352     },
36353
36354     // private
36355     triggerBlur : function(){
36356         this.mimicing = false;
36357         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36358         if(this.monitorTab){
36359             this.el.un("keydown", this.checkTab, this);
36360         }
36361         this.wrap.removeClass('x-trigger-wrap-focus');
36362         Roo.form.TriggerField.superclass.onBlur.call(this);
36363     },
36364
36365     // private
36366     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36367     validateBlur : function(e, t){
36368         return true;
36369     },
36370
36371     // private
36372     onDisable : function(){
36373         Roo.form.TriggerField.superclass.onDisable.call(this);
36374         if(this.wrap){
36375             this.wrap.addClass('x-item-disabled');
36376         }
36377     },
36378
36379     // private
36380     onEnable : function(){
36381         Roo.form.TriggerField.superclass.onEnable.call(this);
36382         if(this.wrap){
36383             this.wrap.removeClass('x-item-disabled');
36384         }
36385     },
36386
36387     // private
36388     onShow : function(){
36389         var ae = this.getActionEl();
36390         
36391         if(ae){
36392             ae.dom.style.display = '';
36393             ae.dom.style.visibility = 'visible';
36394         }
36395     },
36396
36397     // private
36398     
36399     onHide : function(){
36400         var ae = this.getActionEl();
36401         ae.dom.style.display = 'none';
36402     },
36403
36404     /**
36405      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36406      * by an implementing function.
36407      * @method
36408      * @param {EventObject} e
36409      */
36410     onTriggerClick : Roo.emptyFn
36411 });
36412
36413 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36414 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36415 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36416 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36417     initComponent : function(){
36418         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36419
36420         this.triggerConfig = {
36421             tag:'span', cls:'x-form-twin-triggers', cn:[
36422             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36423             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36424         ]};
36425     },
36426
36427     getTrigger : function(index){
36428         return this.triggers[index];
36429     },
36430
36431     initTrigger : function(){
36432         var ts = this.trigger.select('.x-form-trigger', true);
36433         this.wrap.setStyle('overflow', 'hidden');
36434         var triggerField = this;
36435         ts.each(function(t, all, index){
36436             t.hide = function(){
36437                 var w = triggerField.wrap.getWidth();
36438                 this.dom.style.display = 'none';
36439                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36440             };
36441             t.show = function(){
36442                 var w = triggerField.wrap.getWidth();
36443                 this.dom.style.display = '';
36444                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36445             };
36446             var triggerIndex = 'Trigger'+(index+1);
36447
36448             if(this['hide'+triggerIndex]){
36449                 t.dom.style.display = 'none';
36450             }
36451             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36452             t.addClassOnOver('x-form-trigger-over');
36453             t.addClassOnClick('x-form-trigger-click');
36454         }, this);
36455         this.triggers = ts.elements;
36456     },
36457
36458     onTrigger1Click : Roo.emptyFn,
36459     onTrigger2Click : Roo.emptyFn
36460 });/*
36461  * Based on:
36462  * Ext JS Library 1.1.1
36463  * Copyright(c) 2006-2007, Ext JS, LLC.
36464  *
36465  * Originally Released Under LGPL - original licence link has changed is not relivant.
36466  *
36467  * Fork - LGPL
36468  * <script type="text/javascript">
36469  */
36470  
36471 /**
36472  * @class Roo.form.TextArea
36473  * @extends Roo.form.TextField
36474  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36475  * support for auto-sizing.
36476  * @constructor
36477  * Creates a new TextArea
36478  * @param {Object} config Configuration options
36479  */
36480 Roo.form.TextArea = function(config){
36481     Roo.form.TextArea.superclass.constructor.call(this, config);
36482     // these are provided exchanges for backwards compat
36483     // minHeight/maxHeight were replaced by growMin/growMax to be
36484     // compatible with TextField growing config values
36485     if(this.minHeight !== undefined){
36486         this.growMin = this.minHeight;
36487     }
36488     if(this.maxHeight !== undefined){
36489         this.growMax = this.maxHeight;
36490     }
36491 };
36492
36493 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36494     /**
36495      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36496      */
36497     growMin : 60,
36498     /**
36499      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36500      */
36501     growMax: 1000,
36502     /**
36503      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36504      * in the field (equivalent to setting overflow: hidden, defaults to false)
36505      */
36506     preventScrollbars: false,
36507     /**
36508      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36509      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36510      */
36511
36512     // private
36513     onRender : function(ct, position){
36514         if(!this.el){
36515             this.defaultAutoCreate = {
36516                 tag: "textarea",
36517                 style:"width:300px;height:60px;",
36518                 autocomplete: "off"
36519             };
36520         }
36521         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36522         if(this.grow){
36523             this.textSizeEl = Roo.DomHelper.append(document.body, {
36524                 tag: "pre", cls: "x-form-grow-sizer"
36525             });
36526             if(this.preventScrollbars){
36527                 this.el.setStyle("overflow", "hidden");
36528             }
36529             this.el.setHeight(this.growMin);
36530         }
36531     },
36532
36533     onDestroy : function(){
36534         if(this.textSizeEl){
36535             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36536         }
36537         Roo.form.TextArea.superclass.onDestroy.call(this);
36538     },
36539
36540     // private
36541     onKeyUp : function(e){
36542         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36543             this.autoSize();
36544         }
36545     },
36546
36547     /**
36548      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36549      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36550      */
36551     autoSize : function(){
36552         if(!this.grow || !this.textSizeEl){
36553             return;
36554         }
36555         var el = this.el;
36556         var v = el.dom.value;
36557         var ts = this.textSizeEl;
36558
36559         ts.innerHTML = '';
36560         ts.appendChild(document.createTextNode(v));
36561         v = ts.innerHTML;
36562
36563         Roo.fly(ts).setWidth(this.el.getWidth());
36564         if(v.length < 1){
36565             v = "&#160;&#160;";
36566         }else{
36567             if(Roo.isIE){
36568                 v = v.replace(/\n/g, '<p>&#160;</p>');
36569             }
36570             v += "&#160;\n&#160;";
36571         }
36572         ts.innerHTML = v;
36573         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36574         if(h != this.lastHeight){
36575             this.lastHeight = h;
36576             this.el.setHeight(h);
36577             this.fireEvent("autosize", this, h);
36578         }
36579     }
36580 });/*
36581  * Based on:
36582  * Ext JS Library 1.1.1
36583  * Copyright(c) 2006-2007, Ext JS, LLC.
36584  *
36585  * Originally Released Under LGPL - original licence link has changed is not relivant.
36586  *
36587  * Fork - LGPL
36588  * <script type="text/javascript">
36589  */
36590  
36591
36592 /**
36593  * @class Roo.form.NumberField
36594  * @extends Roo.form.TextField
36595  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36596  * @constructor
36597  * Creates a new NumberField
36598  * @param {Object} config Configuration options
36599  */
36600 Roo.form.NumberField = function(config){
36601     Roo.form.NumberField.superclass.constructor.call(this, config);
36602 };
36603
36604 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36605     /**
36606      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36607      */
36608     fieldClass: "x-form-field x-form-num-field",
36609     /**
36610      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36611      */
36612     allowDecimals : true,
36613     /**
36614      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36615      */
36616     decimalSeparator : ".",
36617     /**
36618      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36619      */
36620     decimalPrecision : 2,
36621     /**
36622      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36623      */
36624     allowNegative : true,
36625     /**
36626      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36627      */
36628     minValue : Number.NEGATIVE_INFINITY,
36629     /**
36630      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36631      */
36632     maxValue : Number.MAX_VALUE,
36633     /**
36634      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36635      */
36636     minText : "The minimum value for this field is {0}",
36637     /**
36638      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36639      */
36640     maxText : "The maximum value for this field is {0}",
36641     /**
36642      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36643      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36644      */
36645     nanText : "{0} is not a valid number",
36646
36647     // private
36648     initEvents : function(){
36649         Roo.form.NumberField.superclass.initEvents.call(this);
36650         var allowed = "0123456789";
36651         if(this.allowDecimals){
36652             allowed += this.decimalSeparator;
36653         }
36654         if(this.allowNegative){
36655             allowed += "-";
36656         }
36657         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36658         var keyPress = function(e){
36659             var k = e.getKey();
36660             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36661                 return;
36662             }
36663             var c = e.getCharCode();
36664             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36665                 e.stopEvent();
36666             }
36667         };
36668         this.el.on("keypress", keyPress, this);
36669     },
36670
36671     // private
36672     validateValue : function(value){
36673         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36674             return false;
36675         }
36676         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36677              return true;
36678         }
36679         var num = this.parseValue(value);
36680         if(isNaN(num)){
36681             this.markInvalid(String.format(this.nanText, value));
36682             return false;
36683         }
36684         if(num < this.minValue){
36685             this.markInvalid(String.format(this.minText, this.minValue));
36686             return false;
36687         }
36688         if(num > this.maxValue){
36689             this.markInvalid(String.format(this.maxText, this.maxValue));
36690             return false;
36691         }
36692         return true;
36693     },
36694
36695     getValue : function(){
36696         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36697     },
36698
36699     // private
36700     parseValue : function(value){
36701         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36702         return isNaN(value) ? '' : value;
36703     },
36704
36705     // private
36706     fixPrecision : function(value){
36707         var nan = isNaN(value);
36708         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36709             return nan ? '' : value;
36710         }
36711         return parseFloat(value).toFixed(this.decimalPrecision);
36712     },
36713
36714     setValue : function(v){
36715         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36716     },
36717
36718     // private
36719     decimalPrecisionFcn : function(v){
36720         return Math.floor(v);
36721     },
36722
36723     beforeBlur : function(){
36724         var v = this.parseValue(this.getRawValue());
36725         if(v){
36726             this.setValue(this.fixPrecision(v));
36727         }
36728     }
36729 });/*
36730  * Based on:
36731  * Ext JS Library 1.1.1
36732  * Copyright(c) 2006-2007, Ext JS, LLC.
36733  *
36734  * Originally Released Under LGPL - original licence link has changed is not relivant.
36735  *
36736  * Fork - LGPL
36737  * <script type="text/javascript">
36738  */
36739  
36740 /**
36741  * @class Roo.form.DateField
36742  * @extends Roo.form.TriggerField
36743  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36744 * @constructor
36745 * Create a new DateField
36746 * @param {Object} config
36747  */
36748 Roo.form.DateField = function(config){
36749     Roo.form.DateField.superclass.constructor.call(this, config);
36750     
36751       this.addEvents({
36752          
36753         /**
36754          * @event select
36755          * Fires when a date is selected
36756              * @param {Roo.form.DateField} combo This combo box
36757              * @param {Date} date The date selected
36758              */
36759         'select' : true
36760          
36761     });
36762     
36763     
36764     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36765     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36766     this.ddMatch = null;
36767     if(this.disabledDates){
36768         var dd = this.disabledDates;
36769         var re = "(?:";
36770         for(var i = 0; i < dd.length; i++){
36771             re += dd[i];
36772             if(i != dd.length-1) re += "|";
36773         }
36774         this.ddMatch = new RegExp(re + ")");
36775     }
36776 };
36777
36778 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36779     /**
36780      * @cfg {String} format
36781      * The default date format string which can be overriden for localization support.  The format must be
36782      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36783      */
36784     format : "m/d/y",
36785     /**
36786      * @cfg {String} altFormats
36787      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36788      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36789      */
36790     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36791     /**
36792      * @cfg {Array} disabledDays
36793      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36794      */
36795     disabledDays : null,
36796     /**
36797      * @cfg {String} disabledDaysText
36798      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36799      */
36800     disabledDaysText : "Disabled",
36801     /**
36802      * @cfg {Array} disabledDates
36803      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36804      * expression so they are very powerful. Some examples:
36805      * <ul>
36806      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36807      * <li>["03/08", "09/16"] would disable those days for every year</li>
36808      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36809      * <li>["03/../2006"] would disable every day in March 2006</li>
36810      * <li>["^03"] would disable every day in every March</li>
36811      * </ul>
36812      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36813      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36814      */
36815     disabledDates : null,
36816     /**
36817      * @cfg {String} disabledDatesText
36818      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36819      */
36820     disabledDatesText : "Disabled",
36821     /**
36822      * @cfg {Date/String} minValue
36823      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36824      * valid format (defaults to null).
36825      */
36826     minValue : null,
36827     /**
36828      * @cfg {Date/String} maxValue
36829      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36830      * valid format (defaults to null).
36831      */
36832     maxValue : null,
36833     /**
36834      * @cfg {String} minText
36835      * The error text to display when the date in the cell is before minValue (defaults to
36836      * 'The date in this field must be after {minValue}').
36837      */
36838     minText : "The date in this field must be equal to or after {0}",
36839     /**
36840      * @cfg {String} maxText
36841      * The error text to display when the date in the cell is after maxValue (defaults to
36842      * 'The date in this field must be before {maxValue}').
36843      */
36844     maxText : "The date in this field must be equal to or before {0}",
36845     /**
36846      * @cfg {String} invalidText
36847      * The error text to display when the date in the field is invalid (defaults to
36848      * '{value} is not a valid date - it must be in the format {format}').
36849      */
36850     invalidText : "{0} is not a valid date - it must be in the format {1}",
36851     /**
36852      * @cfg {String} triggerClass
36853      * An additional CSS class used to style the trigger button.  The trigger will always get the
36854      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36855      * which displays a calendar icon).
36856      */
36857     triggerClass : 'x-form-date-trigger',
36858     
36859
36860     /**
36861      * @cfg {bool} useIso
36862      * if enabled, then the date field will use a hidden field to store the 
36863      * real value as iso formated date. default (false)
36864      */ 
36865     useIso : false,
36866     /**
36867      * @cfg {String/Object} autoCreate
36868      * A DomHelper element spec, or true for a default element spec (defaults to
36869      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36870      */ 
36871     // private
36872     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36873     
36874     // private
36875     hiddenField: false,
36876     
36877     onRender : function(ct, position)
36878     {
36879         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36880         if (this.useIso) {
36881             this.el.dom.removeAttribute('name'); 
36882             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36883                     'before', true);
36884             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36885             // prevent input submission
36886             this.hiddenName = this.name;
36887         }
36888             
36889             
36890     },
36891     
36892     // private
36893     validateValue : function(value)
36894     {
36895         value = this.formatDate(value);
36896         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36897             return false;
36898         }
36899         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36900              return true;
36901         }
36902         var svalue = value;
36903         value = this.parseDate(value);
36904         if(!value){
36905             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36906             return false;
36907         }
36908         var time = value.getTime();
36909         if(this.minValue && time < this.minValue.getTime()){
36910             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36911             return false;
36912         }
36913         if(this.maxValue && time > this.maxValue.getTime()){
36914             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36915             return false;
36916         }
36917         if(this.disabledDays){
36918             var day = value.getDay();
36919             for(var i = 0; i < this.disabledDays.length; i++) {
36920                 if(day === this.disabledDays[i]){
36921                     this.markInvalid(this.disabledDaysText);
36922                     return false;
36923                 }
36924             }
36925         }
36926         var fvalue = this.formatDate(value);
36927         if(this.ddMatch && this.ddMatch.test(fvalue)){
36928             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36929             return false;
36930         }
36931         return true;
36932     },
36933
36934     // private
36935     // Provides logic to override the default TriggerField.validateBlur which just returns true
36936     validateBlur : function(){
36937         return !this.menu || !this.menu.isVisible();
36938     },
36939
36940     /**
36941      * Returns the current date value of the date field.
36942      * @return {Date} The date value
36943      */
36944     getValue : function(){
36945         
36946         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36947     },
36948
36949     /**
36950      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36951      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36952      * (the default format used is "m/d/y").
36953      * <br />Usage:
36954      * <pre><code>
36955 //All of these calls set the same date value (May 4, 2006)
36956
36957 //Pass a date object:
36958 var dt = new Date('5/4/06');
36959 dateField.setValue(dt);
36960
36961 //Pass a date string (default format):
36962 dateField.setValue('5/4/06');
36963
36964 //Pass a date string (custom format):
36965 dateField.format = 'Y-m-d';
36966 dateField.setValue('2006-5-4');
36967 </code></pre>
36968      * @param {String/Date} date The date or valid date string
36969      */
36970     setValue : function(date){
36971         if (this.hiddenField) {
36972             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36973         }
36974         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36975     },
36976
36977     // private
36978     parseDate : function(value){
36979         if(!value || value instanceof Date){
36980             return value;
36981         }
36982         var v = Date.parseDate(value, this.format);
36983         if(!v && this.altFormats){
36984             if(!this.altFormatsArray){
36985                 this.altFormatsArray = this.altFormats.split("|");
36986             }
36987             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36988                 v = Date.parseDate(value, this.altFormatsArray[i]);
36989             }
36990         }
36991         return v;
36992     },
36993
36994     // private
36995     formatDate : function(date, fmt){
36996         return (!date || !(date instanceof Date)) ?
36997                date : date.dateFormat(fmt || this.format);
36998     },
36999
37000     // private
37001     menuListeners : {
37002         select: function(m, d){
37003             this.setValue(d);
37004             this.fireEvent('select', this, d);
37005         },
37006         show : function(){ // retain focus styling
37007             this.onFocus();
37008         },
37009         hide : function(){
37010             this.focus.defer(10, this);
37011             var ml = this.menuListeners;
37012             this.menu.un("select", ml.select,  this);
37013             this.menu.un("show", ml.show,  this);
37014             this.menu.un("hide", ml.hide,  this);
37015         }
37016     },
37017
37018     // private
37019     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37020     onTriggerClick : function(){
37021         if(this.disabled){
37022             return;
37023         }
37024         if(this.menu == null){
37025             this.menu = new Roo.menu.DateMenu();
37026         }
37027         Roo.apply(this.menu.picker,  {
37028             showClear: this.allowBlank,
37029             minDate : this.minValue,
37030             maxDate : this.maxValue,
37031             disabledDatesRE : this.ddMatch,
37032             disabledDatesText : this.disabledDatesText,
37033             disabledDays : this.disabledDays,
37034             disabledDaysText : this.disabledDaysText,
37035             format : this.format,
37036             minText : String.format(this.minText, this.formatDate(this.minValue)),
37037             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37038         });
37039         this.menu.on(Roo.apply({}, this.menuListeners, {
37040             scope:this
37041         }));
37042         this.menu.picker.setValue(this.getValue() || new Date());
37043         this.menu.show(this.el, "tl-bl?");
37044     },
37045
37046     beforeBlur : function(){
37047         var v = this.parseDate(this.getRawValue());
37048         if(v){
37049             this.setValue(v);
37050         }
37051     }
37052
37053     /** @cfg {Boolean} grow @hide */
37054     /** @cfg {Number} growMin @hide */
37055     /** @cfg {Number} growMax @hide */
37056     /**
37057      * @hide
37058      * @method autoSize
37059      */
37060 });/*
37061  * Based on:
37062  * Ext JS Library 1.1.1
37063  * Copyright(c) 2006-2007, Ext JS, LLC.
37064  *
37065  * Originally Released Under LGPL - original licence link has changed is not relivant.
37066  *
37067  * Fork - LGPL
37068  * <script type="text/javascript">
37069  */
37070  
37071
37072 /**
37073  * @class Roo.form.ComboBox
37074  * @extends Roo.form.TriggerField
37075  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37076  * @constructor
37077  * Create a new ComboBox.
37078  * @param {Object} config Configuration options
37079  */
37080 Roo.form.ComboBox = function(config){
37081     Roo.form.ComboBox.superclass.constructor.call(this, config);
37082     this.addEvents({
37083         /**
37084          * @event expand
37085          * Fires when the dropdown list is expanded
37086              * @param {Roo.form.ComboBox} combo This combo box
37087              */
37088         'expand' : true,
37089         /**
37090          * @event collapse
37091          * Fires when the dropdown list is collapsed
37092              * @param {Roo.form.ComboBox} combo This combo box
37093              */
37094         'collapse' : true,
37095         /**
37096          * @event beforeselect
37097          * Fires before a list item is selected. Return false to cancel the selection.
37098              * @param {Roo.form.ComboBox} combo This combo box
37099              * @param {Roo.data.Record} record The data record returned from the underlying store
37100              * @param {Number} index The index of the selected item in the dropdown list
37101              */
37102         'beforeselect' : true,
37103         /**
37104          * @event select
37105          * Fires when a list item is selected
37106              * @param {Roo.form.ComboBox} combo This combo box
37107              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37108              * @param {Number} index The index of the selected item in the dropdown list
37109              */
37110         'select' : true,
37111         /**
37112          * @event beforequery
37113          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37114          * The event object passed has these properties:
37115              * @param {Roo.form.ComboBox} combo This combo box
37116              * @param {String} query The query
37117              * @param {Boolean} forceAll true to force "all" query
37118              * @param {Boolean} cancel true to cancel the query
37119              * @param {Object} e The query event object
37120              */
37121         'beforequery': true,
37122          /**
37123          * @event add
37124          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37125              * @param {Roo.form.ComboBox} combo This combo box
37126              */
37127         'add' : true,
37128         /**
37129          * @event edit
37130          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37131              * @param {Roo.form.ComboBox} combo This combo box
37132              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37133              */
37134         'edit' : true
37135         
37136         
37137     });
37138     if(this.transform){
37139         this.allowDomMove = false;
37140         var s = Roo.getDom(this.transform);
37141         if(!this.hiddenName){
37142             this.hiddenName = s.name;
37143         }
37144         if(!this.store){
37145             this.mode = 'local';
37146             var d = [], opts = s.options;
37147             for(var i = 0, len = opts.length;i < len; i++){
37148                 var o = opts[i];
37149                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37150                 if(o.selected) {
37151                     this.value = value;
37152                 }
37153                 d.push([value, o.text]);
37154             }
37155             this.store = new Roo.data.SimpleStore({
37156                 'id': 0,
37157                 fields: ['value', 'text'],
37158                 data : d
37159             });
37160             this.valueField = 'value';
37161             this.displayField = 'text';
37162         }
37163         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37164         if(!this.lazyRender){
37165             this.target = true;
37166             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37167             s.parentNode.removeChild(s); // remove it
37168             this.render(this.el.parentNode);
37169         }else{
37170             s.parentNode.removeChild(s); // remove it
37171         }
37172
37173     }
37174     if (this.store) {
37175         this.store = Roo.factory(this.store, Roo.data);
37176     }
37177     
37178     this.selectedIndex = -1;
37179     if(this.mode == 'local'){
37180         if(config.queryDelay === undefined){
37181             this.queryDelay = 10;
37182         }
37183         if(config.minChars === undefined){
37184             this.minChars = 0;
37185         }
37186     }
37187 };
37188
37189 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37190     /**
37191      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37192      */
37193     /**
37194      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37195      * rendering into an Roo.Editor, defaults to false)
37196      */
37197     /**
37198      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37199      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37200      */
37201     /**
37202      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37203      */
37204     /**
37205      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37206      * the dropdown list (defaults to undefined, with no header element)
37207      */
37208
37209      /**
37210      * @cfg {String/Roo.Template} tpl The template to use to render the output
37211      */
37212      
37213     // private
37214     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37215     /**
37216      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37217      */
37218     listWidth: undefined,
37219     /**
37220      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37221      * mode = 'remote' or 'text' if mode = 'local')
37222      */
37223     displayField: undefined,
37224     /**
37225      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37226      * mode = 'remote' or 'value' if mode = 'local'). 
37227      * Note: use of a valueField requires the user make a selection
37228      * in order for a value to be mapped.
37229      */
37230     valueField: undefined,
37231     /**
37232      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37233      * field's data value (defaults to the underlying DOM element's name)
37234      */
37235     hiddenName: undefined,
37236     /**
37237      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37238      */
37239     listClass: '',
37240     /**
37241      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37242      */
37243     selectedClass: 'x-combo-selected',
37244     /**
37245      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37246      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37247      * which displays a downward arrow icon).
37248      */
37249     triggerClass : 'x-form-arrow-trigger',
37250     /**
37251      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37252      */
37253     shadow:'sides',
37254     /**
37255      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37256      * anchor positions (defaults to 'tl-bl')
37257      */
37258     listAlign: 'tl-bl?',
37259     /**
37260      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37261      */
37262     maxHeight: 300,
37263     /**
37264      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37265      * query specified by the allQuery config option (defaults to 'query')
37266      */
37267     triggerAction: 'query',
37268     /**
37269      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37270      * (defaults to 4, does not apply if editable = false)
37271      */
37272     minChars : 4,
37273     /**
37274      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37275      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37276      */
37277     typeAhead: false,
37278     /**
37279      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37280      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37281      */
37282     queryDelay: 500,
37283     /**
37284      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37285      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37286      */
37287     pageSize: 0,
37288     /**
37289      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37290      * when editable = true (defaults to false)
37291      */
37292     selectOnFocus:false,
37293     /**
37294      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37295      */
37296     queryParam: 'query',
37297     /**
37298      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37299      * when mode = 'remote' (defaults to 'Loading...')
37300      */
37301     loadingText: 'Loading...',
37302     /**
37303      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37304      */
37305     resizable: false,
37306     /**
37307      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37308      */
37309     handleHeight : 8,
37310     /**
37311      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37312      * traditional select (defaults to true)
37313      */
37314     editable: true,
37315     /**
37316      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37317      */
37318     allQuery: '',
37319     /**
37320      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37321      */
37322     mode: 'remote',
37323     /**
37324      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37325      * listWidth has a higher value)
37326      */
37327     minListWidth : 70,
37328     /**
37329      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37330      * allow the user to set arbitrary text into the field (defaults to false)
37331      */
37332     forceSelection:false,
37333     /**
37334      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37335      * if typeAhead = true (defaults to 250)
37336      */
37337     typeAheadDelay : 250,
37338     /**
37339      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37340      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37341      */
37342     valueNotFoundText : undefined,
37343     /**
37344      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37345      */
37346     blockFocus : false,
37347     
37348     /**
37349      * @cfg {Boolean} disableClear Disable showing of clear button.
37350      */
37351     disableClear : false,
37352     /**
37353      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37354      */
37355     alwaysQuery : false,
37356     
37357     //private
37358     addicon : false,
37359     editicon: false,
37360     
37361     
37362     // private
37363     onRender : function(ct, position){
37364         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37365         if(this.hiddenName){
37366             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37367                     'before', true);
37368             this.hiddenField.value =
37369                 this.hiddenValue !== undefined ? this.hiddenValue :
37370                 this.value !== undefined ? this.value : '';
37371
37372             // prevent input submission
37373             this.el.dom.removeAttribute('name');
37374         }
37375         if(Roo.isGecko){
37376             this.el.dom.setAttribute('autocomplete', 'off');
37377         }
37378
37379         var cls = 'x-combo-list';
37380
37381         this.list = new Roo.Layer({
37382             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37383         });
37384
37385         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37386         this.list.setWidth(lw);
37387         this.list.swallowEvent('mousewheel');
37388         this.assetHeight = 0;
37389
37390         if(this.title){
37391             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37392             this.assetHeight += this.header.getHeight();
37393         }
37394
37395         this.innerList = this.list.createChild({cls:cls+'-inner'});
37396         this.innerList.on('mouseover', this.onViewOver, this);
37397         this.innerList.on('mousemove', this.onViewMove, this);
37398         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37399         
37400         if(this.allowBlank && !this.pageSize && !this.disableClear){
37401             this.footer = this.list.createChild({cls:cls+'-ft'});
37402             this.pageTb = new Roo.Toolbar(this.footer);
37403            
37404         }
37405         if(this.pageSize){
37406             this.footer = this.list.createChild({cls:cls+'-ft'});
37407             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37408                     {pageSize: this.pageSize});
37409             
37410         }
37411         
37412         if (this.pageTb && this.allowBlank && !this.disableClear) {
37413             var _this = this;
37414             this.pageTb.add(new Roo.Toolbar.Fill(), {
37415                 cls: 'x-btn-icon x-btn-clear',
37416                 text: '&#160;',
37417                 handler: function()
37418                 {
37419                     _this.collapse();
37420                     _this.clearValue();
37421                     _this.onSelect(false, -1);
37422                 }
37423             });
37424         }
37425         if (this.footer) {
37426             this.assetHeight += this.footer.getHeight();
37427         }
37428         
37429
37430         if(!this.tpl){
37431             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37432         }
37433
37434         this.view = new Roo.View(this.innerList, this.tpl, {
37435             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37436         });
37437
37438         this.view.on('click', this.onViewClick, this);
37439
37440         this.store.on('beforeload', this.onBeforeLoad, this);
37441         this.store.on('load', this.onLoad, this);
37442         this.store.on('loadexception', this.collapse, this);
37443
37444         if(this.resizable){
37445             this.resizer = new Roo.Resizable(this.list,  {
37446                pinned:true, handles:'se'
37447             });
37448             this.resizer.on('resize', function(r, w, h){
37449                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37450                 this.listWidth = w;
37451                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37452                 this.restrictHeight();
37453             }, this);
37454             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37455         }
37456         if(!this.editable){
37457             this.editable = true;
37458             this.setEditable(false);
37459         }  
37460         
37461         
37462         if (typeof(this.events.add.listeners) != 'undefined') {
37463             
37464             this.addicon = this.wrap.createChild(
37465                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37466        
37467             this.addicon.on('click', function(e) {
37468                 this.fireEvent('add', this);
37469             }, this);
37470         }
37471         if (typeof(this.events.edit.listeners) != 'undefined') {
37472             
37473             this.editicon = this.wrap.createChild(
37474                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37475             if (this.addicon) {
37476                 this.editicon.setStyle('margin-left', '40px');
37477             }
37478             this.editicon.on('click', function(e) {
37479                 
37480                 // we fire even  if inothing is selected..
37481                 this.fireEvent('edit', this, this.lastData );
37482                 
37483             }, this);
37484         }
37485         
37486         
37487         
37488     },
37489
37490     // private
37491     initEvents : function(){
37492         Roo.form.ComboBox.superclass.initEvents.call(this);
37493
37494         this.keyNav = new Roo.KeyNav(this.el, {
37495             "up" : function(e){
37496                 this.inKeyMode = true;
37497                 this.selectPrev();
37498             },
37499
37500             "down" : function(e){
37501                 if(!this.isExpanded()){
37502                     this.onTriggerClick();
37503                 }else{
37504                     this.inKeyMode = true;
37505                     this.selectNext();
37506                 }
37507             },
37508
37509             "enter" : function(e){
37510                 this.onViewClick();
37511                 //return true;
37512             },
37513
37514             "esc" : function(e){
37515                 this.collapse();
37516             },
37517
37518             "tab" : function(e){
37519                 this.onViewClick(false);
37520                 return true;
37521             },
37522
37523             scope : this,
37524
37525             doRelay : function(foo, bar, hname){
37526                 if(hname == 'down' || this.scope.isExpanded()){
37527                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37528                 }
37529                 return true;
37530             },
37531
37532             forceKeyDown: true
37533         });
37534         this.queryDelay = Math.max(this.queryDelay || 10,
37535                 this.mode == 'local' ? 10 : 250);
37536         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37537         if(this.typeAhead){
37538             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37539         }
37540         if(this.editable !== false){
37541             this.el.on("keyup", this.onKeyUp, this);
37542         }
37543         if(this.forceSelection){
37544             this.on('blur', this.doForce, this);
37545         }
37546     },
37547
37548     onDestroy : function(){
37549         if(this.view){
37550             this.view.setStore(null);
37551             this.view.el.removeAllListeners();
37552             this.view.el.remove();
37553             this.view.purgeListeners();
37554         }
37555         if(this.list){
37556             this.list.destroy();
37557         }
37558         if(this.store){
37559             this.store.un('beforeload', this.onBeforeLoad, this);
37560             this.store.un('load', this.onLoad, this);
37561             this.store.un('loadexception', this.collapse, this);
37562         }
37563         Roo.form.ComboBox.superclass.onDestroy.call(this);
37564     },
37565
37566     // private
37567     fireKey : function(e){
37568         if(e.isNavKeyPress() && !this.list.isVisible()){
37569             this.fireEvent("specialkey", this, e);
37570         }
37571     },
37572
37573     // private
37574     onResize: function(w, h){
37575         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37576         
37577         if(typeof w != 'number'){
37578             // we do not handle it!?!?
37579             return;
37580         }
37581         var tw = this.trigger.getWidth();
37582         tw += this.addicon ? this.addicon.getWidth() : 0;
37583         tw += this.editicon ? this.editicon.getWidth() : 0;
37584         var x = w - tw;
37585         this.el.setWidth( this.adjustWidth('input', x));
37586             
37587         this.trigger.setStyle('left', x+'px');
37588         
37589         if(this.list && this.listWidth === undefined){
37590             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37591             this.list.setWidth(lw);
37592             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37593         }
37594         
37595     
37596         
37597     },
37598
37599     /**
37600      * Allow or prevent the user from directly editing the field text.  If false is passed,
37601      * the user will only be able to select from the items defined in the dropdown list.  This method
37602      * is the runtime equivalent of setting the 'editable' config option at config time.
37603      * @param {Boolean} value True to allow the user to directly edit the field text
37604      */
37605     setEditable : function(value){
37606         if(value == this.editable){
37607             return;
37608         }
37609         this.editable = value;
37610         if(!value){
37611             this.el.dom.setAttribute('readOnly', true);
37612             this.el.on('mousedown', this.onTriggerClick,  this);
37613             this.el.addClass('x-combo-noedit');
37614         }else{
37615             this.el.dom.setAttribute('readOnly', false);
37616             this.el.un('mousedown', this.onTriggerClick,  this);
37617             this.el.removeClass('x-combo-noedit');
37618         }
37619     },
37620
37621     // private
37622     onBeforeLoad : function(){
37623         if(!this.hasFocus){
37624             return;
37625         }
37626         this.innerList.update(this.loadingText ?
37627                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37628         this.restrictHeight();
37629         this.selectedIndex = -1;
37630     },
37631
37632     // private
37633     onLoad : function(){
37634         if(!this.hasFocus){
37635             return;
37636         }
37637         if(this.store.getCount() > 0){
37638             this.expand();
37639             this.restrictHeight();
37640             if(this.lastQuery == this.allQuery){
37641                 if(this.editable){
37642                     this.el.dom.select();
37643                 }
37644                 if(!this.selectByValue(this.value, true)){
37645                     this.select(0, true);
37646                 }
37647             }else{
37648                 this.selectNext();
37649                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37650                     this.taTask.delay(this.typeAheadDelay);
37651                 }
37652             }
37653         }else{
37654             this.onEmptyResults();
37655         }
37656         //this.el.focus();
37657     },
37658
37659     // private
37660     onTypeAhead : function(){
37661         if(this.store.getCount() > 0){
37662             var r = this.store.getAt(0);
37663             var newValue = r.data[this.displayField];
37664             var len = newValue.length;
37665             var selStart = this.getRawValue().length;
37666             if(selStart != len){
37667                 this.setRawValue(newValue);
37668                 this.selectText(selStart, newValue.length);
37669             }
37670         }
37671     },
37672
37673     // private
37674     onSelect : function(record, index){
37675         if(this.fireEvent('beforeselect', this, record, index) !== false){
37676             this.setFromData(index > -1 ? record.data : false);
37677             this.collapse();
37678             this.fireEvent('select', this, record, index);
37679         }
37680     },
37681
37682     /**
37683      * Returns the currently selected field value or empty string if no value is set.
37684      * @return {String} value The selected value
37685      */
37686     getValue : function(){
37687         if(this.valueField){
37688             return typeof this.value != 'undefined' ? this.value : '';
37689         }else{
37690             return Roo.form.ComboBox.superclass.getValue.call(this);
37691         }
37692     },
37693
37694     /**
37695      * Clears any text/value currently set in the field
37696      */
37697     clearValue : function(){
37698         if(this.hiddenField){
37699             this.hiddenField.value = '';
37700         }
37701         this.value = '';
37702         this.setRawValue('');
37703         this.lastSelectionText = '';
37704         this.applyEmptyText();
37705     },
37706
37707     /**
37708      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37709      * will be displayed in the field.  If the value does not match the data value of an existing item,
37710      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37711      * Otherwise the field will be blank (although the value will still be set).
37712      * @param {String} value The value to match
37713      */
37714     setValue : function(v){
37715         var text = v;
37716         if(this.valueField){
37717             var r = this.findRecord(this.valueField, v);
37718             if(r){
37719                 text = r.data[this.displayField];
37720             }else if(this.valueNotFoundText !== undefined){
37721                 text = this.valueNotFoundText;
37722             }
37723         }
37724         this.lastSelectionText = text;
37725         if(this.hiddenField){
37726             this.hiddenField.value = v;
37727         }
37728         Roo.form.ComboBox.superclass.setValue.call(this, text);
37729         this.value = v;
37730     },
37731     /**
37732      * @property {Object} the last set data for the element
37733      */
37734     
37735     lastData : false,
37736     /**
37737      * Sets the value of the field based on a object which is related to the record format for the store.
37738      * @param {Object} value the value to set as. or false on reset?
37739      */
37740     setFromData : function(o){
37741         var dv = ''; // display value
37742         var vv = ''; // value value..
37743         this.lastData = o;
37744         if (this.displayField) {
37745             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37746         } else {
37747             // this is an error condition!!!
37748             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37749         }
37750         
37751         if(this.valueField){
37752             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37753         }
37754         if(this.hiddenField){
37755             this.hiddenField.value = vv;
37756             
37757             this.lastSelectionText = dv;
37758             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37759             this.value = vv;
37760             return;
37761         }
37762         // no hidden field.. - we store the value in 'value', but still display
37763         // display field!!!!
37764         this.lastSelectionText = dv;
37765         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37766         this.value = vv;
37767         
37768         
37769     },
37770     // private
37771     reset : function(){
37772         // overridden so that last data is reset..
37773         this.setValue(this.originalValue);
37774         this.clearInvalid();
37775         this.lastData = false;
37776     },
37777     // private
37778     findRecord : function(prop, value){
37779         var record;
37780         if(this.store.getCount() > 0){
37781             this.store.each(function(r){
37782                 if(r.data[prop] == value){
37783                     record = r;
37784                     return false;
37785                 }
37786             });
37787         }
37788         return record;
37789     },
37790
37791     // private
37792     onViewMove : function(e, t){
37793         this.inKeyMode = false;
37794     },
37795
37796     // private
37797     onViewOver : function(e, t){
37798         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37799             return;
37800         }
37801         var item = this.view.findItemFromChild(t);
37802         if(item){
37803             var index = this.view.indexOf(item);
37804             this.select(index, false);
37805         }
37806     },
37807
37808     // private
37809     onViewClick : function(doFocus){
37810         var index = this.view.getSelectedIndexes()[0];
37811         var r = this.store.getAt(index);
37812         if(r){
37813             this.onSelect(r, index);
37814         }
37815         if(doFocus !== false && !this.blockFocus){
37816             this.el.focus();
37817         }
37818     },
37819
37820     // private
37821     restrictHeight : function(){
37822         this.innerList.dom.style.height = '';
37823         var inner = this.innerList.dom;
37824         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37825         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37826         this.list.beginUpdate();
37827         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37828         this.list.alignTo(this.el, this.listAlign);
37829         this.list.endUpdate();
37830     },
37831
37832     // private
37833     onEmptyResults : function(){
37834         this.collapse();
37835     },
37836
37837     /**
37838      * Returns true if the dropdown list is expanded, else false.
37839      */
37840     isExpanded : function(){
37841         return this.list.isVisible();
37842     },
37843
37844     /**
37845      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
37851      */
37852     selectByValue : function(v, scrollIntoView){
37853         if(v !== undefined && v !== null){
37854             var r = this.findRecord(this.valueField || this.displayField, v);
37855             if(r){
37856                 this.select(this.store.indexOf(r), scrollIntoView);
37857                 return true;
37858             }
37859         }
37860         return false;
37861     },
37862
37863     /**
37864      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37865      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37866      * @param {Number} index The zero-based index of the list item to select
37867      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37868      * selected item if it is not currently in view (defaults to true)
37869      */
37870     select : function(index, scrollIntoView){
37871         this.selectedIndex = index;
37872         this.view.select(index);
37873         if(scrollIntoView !== false){
37874             var el = this.view.getNode(index);
37875             if(el){
37876                 this.innerList.scrollChildIntoView(el, false);
37877             }
37878         }
37879     },
37880
37881     // private
37882     selectNext : function(){
37883         var ct = this.store.getCount();
37884         if(ct > 0){
37885             if(this.selectedIndex == -1){
37886                 this.select(0);
37887             }else if(this.selectedIndex < ct-1){
37888                 this.select(this.selectedIndex+1);
37889             }
37890         }
37891     },
37892
37893     // private
37894     selectPrev : function(){
37895         var ct = this.store.getCount();
37896         if(ct > 0){
37897             if(this.selectedIndex == -1){
37898                 this.select(0);
37899             }else if(this.selectedIndex != 0){
37900                 this.select(this.selectedIndex-1);
37901             }
37902         }
37903     },
37904
37905     // private
37906     onKeyUp : function(e){
37907         if(this.editable !== false && !e.isSpecialKey()){
37908             this.lastKey = e.getKey();
37909             this.dqTask.delay(this.queryDelay);
37910         }
37911     },
37912
37913     // private
37914     validateBlur : function(){
37915         return !this.list || !this.list.isVisible();   
37916     },
37917
37918     // private
37919     initQuery : function(){
37920         this.doQuery(this.getRawValue());
37921     },
37922
37923     // private
37924     doForce : function(){
37925         if(this.el.dom.value.length > 0){
37926             this.el.dom.value =
37927                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37928             this.applyEmptyText();
37929         }
37930     },
37931
37932     /**
37933      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37934      * query allowing the query action to be canceled if needed.
37935      * @param {String} query The SQL query to execute
37936      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37937      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37938      * saved in the current store (defaults to false)
37939      */
37940     doQuery : function(q, forceAll){
37941         if(q === undefined || q === null){
37942             q = '';
37943         }
37944         var qe = {
37945             query: q,
37946             forceAll: forceAll,
37947             combo: this,
37948             cancel:false
37949         };
37950         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37951             return false;
37952         }
37953         q = qe.query;
37954         forceAll = qe.forceAll;
37955         if(forceAll === true || (q.length >= this.minChars)){
37956             if(this.lastQuery != q || this.alwaysQuery){
37957                 this.lastQuery = q;
37958                 if(this.mode == 'local'){
37959                     this.selectedIndex = -1;
37960                     if(forceAll){
37961                         this.store.clearFilter();
37962                     }else{
37963                         this.store.filter(this.displayField, q);
37964                     }
37965                     this.onLoad();
37966                 }else{
37967                     this.store.baseParams[this.queryParam] = q;
37968                     this.store.load({
37969                         params: this.getParams(q)
37970                     });
37971                     this.expand();
37972                 }
37973             }else{
37974                 this.selectedIndex = -1;
37975                 this.onLoad();   
37976             }
37977         }
37978     },
37979
37980     // private
37981     getParams : function(q){
37982         var p = {};
37983         //p[this.queryParam] = q;
37984         if(this.pageSize){
37985             p.start = 0;
37986             p.limit = this.pageSize;
37987         }
37988         return p;
37989     },
37990
37991     /**
37992      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37993      */
37994     collapse : function(){
37995         if(!this.isExpanded()){
37996             return;
37997         }
37998         this.list.hide();
37999         Roo.get(document).un('mousedown', this.collapseIf, this);
38000         Roo.get(document).un('mousewheel', this.collapseIf, this);
38001         if (!this.editable) {
38002             Roo.get(document).un('keydown', this.listKeyPress, this);
38003         }
38004         this.fireEvent('collapse', this);
38005     },
38006
38007     // private
38008     collapseIf : function(e){
38009         if(!e.within(this.wrap) && !e.within(this.list)){
38010             this.collapse();
38011         }
38012     },
38013
38014     /**
38015      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38016      */
38017     expand : function(){
38018         if(this.isExpanded() || !this.hasFocus){
38019             return;
38020         }
38021         this.list.alignTo(this.el, this.listAlign);
38022         this.list.show();
38023         Roo.get(document).on('mousedown', this.collapseIf, this);
38024         Roo.get(document).on('mousewheel', this.collapseIf, this);
38025         if (!this.editable) {
38026             Roo.get(document).on('keydown', this.listKeyPress, this);
38027         }
38028         
38029         this.fireEvent('expand', this);
38030     },
38031
38032     // private
38033     // Implements the default empty TriggerField.onTriggerClick function
38034     onTriggerClick : function(){
38035         if(this.disabled){
38036             return;
38037         }
38038         if(this.isExpanded()){
38039             this.collapse();
38040             if (!this.blockFocus) {
38041                 this.el.focus();
38042             }
38043             
38044         }else {
38045             this.hasFocus = true;
38046             if(this.triggerAction == 'all') {
38047                 this.doQuery(this.allQuery, true);
38048             } else {
38049                 this.doQuery(this.getRawValue());
38050             }
38051             if (!this.blockFocus) {
38052                 this.el.focus();
38053             }
38054         }
38055     },
38056     listKeyPress : function(e)
38057     {
38058         //Roo.log('listkeypress');
38059         // scroll to first matching element based on key pres..
38060         if (e.isSpecialKey()) {
38061             return false;
38062         }
38063         var k = String.fromCharCode(e.getKey()).toUpperCase();
38064         //Roo.log(k);
38065         var match  = false;
38066         var csel = this.view.getSelectedNodes();
38067         var cselitem = false;
38068         if (csel.length) {
38069             var ix = this.view.indexOf(csel[0]);
38070             cselitem  = this.store.getAt(ix);
38071             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38072                 cselitem = false;
38073             }
38074             
38075         }
38076         
38077         this.store.each(function(v) { 
38078             if (cselitem) {
38079                 // start at existing selection.
38080                 if (cselitem.id == v.id) {
38081                     cselitem = false;
38082                 }
38083                 return;
38084             }
38085                 
38086             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38087                 match = this.store.indexOf(v);
38088                 return false;
38089             }
38090         }, this);
38091         
38092         if (match === false) {
38093             return true; // no more action?
38094         }
38095         // scroll to?
38096         this.view.select(match);
38097         var sn = Roo.get(this.view.getSelectedNodes()[0])
38098         sn.scrollIntoView(sn.dom.parentNode, false);
38099     }
38100
38101     /** 
38102     * @cfg {Boolean} grow 
38103     * @hide 
38104     */
38105     /** 
38106     * @cfg {Number} growMin 
38107     * @hide 
38108     */
38109     /** 
38110     * @cfg {Number} growMax 
38111     * @hide 
38112     */
38113     /**
38114      * @hide
38115      * @method autoSize
38116      */
38117 });/*
38118  * Based on:
38119  * Ext JS Library 1.1.1
38120  * Copyright(c) 2006-2007, Ext JS, LLC.
38121  *
38122  * Originally Released Under LGPL - original licence link has changed is not relivant.
38123  *
38124  * Fork - LGPL
38125  * <script type="text/javascript">
38126  */
38127 /**
38128  * @class Roo.form.Checkbox
38129  * @extends Roo.form.Field
38130  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38131  * @constructor
38132  * Creates a new Checkbox
38133  * @param {Object} config Configuration options
38134  */
38135 Roo.form.Checkbox = function(config){
38136     Roo.form.Checkbox.superclass.constructor.call(this, config);
38137     this.addEvents({
38138         /**
38139          * @event check
38140          * Fires when the checkbox is checked or unchecked.
38141              * @param {Roo.form.Checkbox} this This checkbox
38142              * @param {Boolean} checked The new checked value
38143              */
38144         check : true
38145     });
38146 };
38147
38148 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38149     /**
38150      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38151      */
38152     focusClass : undefined,
38153     /**
38154      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38155      */
38156     fieldClass: "x-form-field",
38157     /**
38158      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38159      */
38160     checked: false,
38161     /**
38162      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38163      * {tag: "input", type: "checkbox", autocomplete: "off"})
38164      */
38165     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38166     /**
38167      * @cfg {String} boxLabel The text that appears beside the checkbox
38168      */
38169     boxLabel : "",
38170     /**
38171      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38172      */  
38173     inputValue : '1',
38174     /**
38175      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38176      */
38177      valueOff: '0', // value when not checked..
38178
38179     actionMode : 'viewEl', 
38180     //
38181     // private
38182     itemCls : 'x-menu-check-item x-form-item',
38183     groupClass : 'x-menu-group-item',
38184     inputType : 'hidden',
38185     
38186     
38187     inSetChecked: false, // check that we are not calling self...
38188     
38189     inputElement: false, // real input element?
38190     basedOn: false, // ????
38191     
38192     isFormField: true, // not sure where this is needed!!!!
38193
38194     onResize : function(){
38195         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38196         if(!this.boxLabel){
38197             this.el.alignTo(this.wrap, 'c-c');
38198         }
38199     },
38200
38201     initEvents : function(){
38202         Roo.form.Checkbox.superclass.initEvents.call(this);
38203         this.el.on("click", this.onClick,  this);
38204         this.el.on("change", this.onClick,  this);
38205     },
38206
38207
38208     getResizeEl : function(){
38209         return this.wrap;
38210     },
38211
38212     getPositionEl : function(){
38213         return this.wrap;
38214     },
38215
38216     // private
38217     onRender : function(ct, position){
38218         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38219         /*
38220         if(this.inputValue !== undefined){
38221             this.el.dom.value = this.inputValue;
38222         }
38223         */
38224         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38225         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38226         var viewEl = this.wrap.createChild({ 
38227             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38228         this.viewEl = viewEl;   
38229         this.wrap.on('click', this.onClick,  this); 
38230         
38231         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38232         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38233         
38234         
38235         
38236         if(this.boxLabel){
38237             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38238         //    viewEl.on('click', this.onClick,  this); 
38239         }
38240         //if(this.checked){
38241             this.setChecked(this.checked);
38242         //}else{
38243             //this.checked = this.el.dom;
38244         //}
38245
38246     },
38247
38248     // private
38249     initValue : Roo.emptyFn,
38250
38251     /**
38252      * Returns the checked state of the checkbox.
38253      * @return {Boolean} True if checked, else false
38254      */
38255     getValue : function(){
38256         if(this.el){
38257             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38258         }
38259         return this.valueOff;
38260         
38261     },
38262
38263         // private
38264     onClick : function(){ 
38265         this.setChecked(!this.checked);
38266
38267         //if(this.el.dom.checked != this.checked){
38268         //    this.setValue(this.el.dom.checked);
38269        // }
38270     },
38271
38272     /**
38273      * Sets the checked state of the checkbox.
38274      * On is always based on a string comparison between inputValue and the param.
38275      * @param {Boolean/String} value - the value to set 
38276      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38277      */
38278     setValue : function(v,suppressEvent){
38279         
38280         
38281         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38282         //if(this.el && this.el.dom){
38283         //    this.el.dom.checked = this.checked;
38284         //    this.el.dom.defaultChecked = this.checked;
38285         //}
38286         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38287         //this.fireEvent("check", this, this.checked);
38288     },
38289     // private..
38290     setChecked : function(state,suppressEvent)
38291     {
38292         if (this.inSetChecked) {
38293             this.checked = state;
38294             return;
38295         }
38296         
38297     
38298         if(this.wrap){
38299             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38300         }
38301         this.checked = state;
38302         if(suppressEvent !== true){
38303             this.fireEvent('check', this, state);
38304         }
38305         this.inSetChecked = true;
38306         this.el.dom.value = state ? this.inputValue : this.valueOff;
38307         this.inSetChecked = false;
38308         
38309     },
38310     // handle setting of hidden value by some other method!!?!?
38311     setFromHidden: function()
38312     {
38313         if(!this.el){
38314             return;
38315         }
38316         //console.log("SET FROM HIDDEN");
38317         //alert('setFrom hidden');
38318         this.setValue(this.el.dom.value);
38319     },
38320     
38321     onDestroy : function()
38322     {
38323         if(this.viewEl){
38324             Roo.get(this.viewEl).remove();
38325         }
38326          
38327         Roo.form.Checkbox.superclass.onDestroy.call(this);
38328     }
38329
38330 });/*
38331  * Based on:
38332  * Ext JS Library 1.1.1
38333  * Copyright(c) 2006-2007, Ext JS, LLC.
38334  *
38335  * Originally Released Under LGPL - original licence link has changed is not relivant.
38336  *
38337  * Fork - LGPL
38338  * <script type="text/javascript">
38339  */
38340  
38341 /**
38342  * @class Roo.form.Radio
38343  * @extends Roo.form.Checkbox
38344  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38345  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38346  * @constructor
38347  * Creates a new Radio
38348  * @param {Object} config Configuration options
38349  */
38350 Roo.form.Radio = function(){
38351     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38352 };
38353 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38354     inputType: 'radio',
38355
38356     /**
38357      * If this radio is part of a group, it will return the selected value
38358      * @return {String}
38359      */
38360     getGroupValue : function(){
38361         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38362     }
38363 });//<script type="text/javascript">
38364
38365 /*
38366  * Ext JS Library 1.1.1
38367  * Copyright(c) 2006-2007, Ext JS, LLC.
38368  * licensing@extjs.com
38369  * 
38370  * http://www.extjs.com/license
38371  */
38372  
38373  /*
38374   * 
38375   * Known bugs:
38376   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38377   * - IE ? - no idea how much works there.
38378   * 
38379   * 
38380   * 
38381   */
38382  
38383
38384 /**
38385  * @class Ext.form.HtmlEditor
38386  * @extends Ext.form.Field
38387  * Provides a lightweight HTML Editor component.
38388  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38389  * 
38390  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38391  * supported by this editor.</b><br/><br/>
38392  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38393  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38394  */
38395 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38396       /**
38397      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38398      */
38399     toolbars : false,
38400     /**
38401      * @cfg {String} createLinkText The default text for the create link prompt
38402      */
38403     createLinkText : 'Please enter the URL for the link:',
38404     /**
38405      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38406      */
38407     defaultLinkValue : 'http:/'+'/',
38408    
38409     
38410     // id of frame..
38411     frameId: false,
38412     
38413     // private properties
38414     validationEvent : false,
38415     deferHeight: true,
38416     initialized : false,
38417     activated : false,
38418     sourceEditMode : false,
38419     onFocus : Roo.emptyFn,
38420     iframePad:3,
38421     hideMode:'offsets',
38422     defaultAutoCreate : {
38423         tag: "textarea",
38424         style:"width:500px;height:300px;",
38425         autocomplete: "off"
38426     },
38427
38428     // private
38429     initComponent : function(){
38430         this.addEvents({
38431             /**
38432              * @event initialize
38433              * Fires when the editor is fully initialized (including the iframe)
38434              * @param {HtmlEditor} this
38435              */
38436             initialize: true,
38437             /**
38438              * @event activate
38439              * Fires when the editor is first receives the focus. Any insertion must wait
38440              * until after this event.
38441              * @param {HtmlEditor} this
38442              */
38443             activate: true,
38444              /**
38445              * @event beforesync
38446              * Fires before the textarea is updated with content from the editor iframe. Return false
38447              * to cancel the sync.
38448              * @param {HtmlEditor} this
38449              * @param {String} html
38450              */
38451             beforesync: true,
38452              /**
38453              * @event beforepush
38454              * Fires before the iframe editor is updated with content from the textarea. Return false
38455              * to cancel the push.
38456              * @param {HtmlEditor} this
38457              * @param {String} html
38458              */
38459             beforepush: true,
38460              /**
38461              * @event sync
38462              * Fires when the textarea is updated with content from the editor iframe.
38463              * @param {HtmlEditor} this
38464              * @param {String} html
38465              */
38466             sync: true,
38467              /**
38468              * @event push
38469              * Fires when the iframe editor is updated with content from the textarea.
38470              * @param {HtmlEditor} this
38471              * @param {String} html
38472              */
38473             push: true,
38474              /**
38475              * @event editmodechange
38476              * Fires when the editor switches edit modes
38477              * @param {HtmlEditor} this
38478              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38479              */
38480             editmodechange: true,
38481             /**
38482              * @event editorevent
38483              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38484              * @param {HtmlEditor} this
38485              */
38486             editorevent: true
38487         })
38488     },
38489
38490     /**
38491      * Protected method that will not generally be called directly. It
38492      * is called when the editor creates its toolbar. Override this method if you need to
38493      * add custom toolbar buttons.
38494      * @param {HtmlEditor} editor
38495      */
38496     createToolbar : function(editor){
38497         if (!editor.toolbars || !editor.toolbars.length) {
38498             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38499         }
38500         
38501         for (var i =0 ; i < editor.toolbars.length;i++) {
38502             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38503             editor.toolbars[i].init(editor);
38504         }
38505          
38506         
38507     },
38508
38509     /**
38510      * Protected method that will not generally be called directly. It
38511      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38512      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38513      */
38514     getDocMarkup : function(){
38515         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38516     },
38517
38518     // private
38519     onRender : function(ct, position){
38520         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38521         this.el.dom.style.border = '0 none';
38522         this.el.dom.setAttribute('tabIndex', -1);
38523         this.el.addClass('x-hidden');
38524         if(Roo.isIE){ // fix IE 1px bogus margin
38525             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38526         }
38527         this.wrap = this.el.wrap({
38528             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38529         });
38530
38531         this.frameId = Roo.id();
38532         this.createToolbar(this);
38533         
38534         
38535         
38536         
38537       
38538         
38539         var iframe = this.wrap.createChild({
38540             tag: 'iframe',
38541             id: this.frameId,
38542             name: this.frameId,
38543             frameBorder : 'no',
38544             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38545         });
38546         
38547        // console.log(iframe);
38548         //this.wrap.dom.appendChild(iframe);
38549
38550         this.iframe = iframe.dom;
38551
38552          this.assignDocWin();
38553         
38554         this.doc.designMode = 'on';
38555        
38556         this.doc.open();
38557         this.doc.write(this.getDocMarkup());
38558         this.doc.close();
38559
38560         
38561         var task = { // must defer to wait for browser to be ready
38562             run : function(){
38563                 //console.log("run task?" + this.doc.readyState);
38564                 this.assignDocWin();
38565                 if(this.doc.body || this.doc.readyState == 'complete'){
38566                     try {
38567                         this.doc.designMode="on";
38568                     } catch (e) {
38569                         return;
38570                     }
38571                     Roo.TaskMgr.stop(task);
38572                     this.initEditor.defer(10, this);
38573                 }
38574             },
38575             interval : 10,
38576             duration:10000,
38577             scope: this
38578         };
38579         Roo.TaskMgr.start(task);
38580
38581         if(!this.width){
38582             this.setSize(this.el.getSize());
38583         }
38584     },
38585
38586     // private
38587     onResize : function(w, h){
38588         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38589         if(this.el && this.iframe){
38590             if(typeof w == 'number'){
38591                 var aw = w - this.wrap.getFrameWidth('lr');
38592                 this.el.setWidth(this.adjustWidth('textarea', aw));
38593                 this.iframe.style.width = aw + 'px';
38594             }
38595             if(typeof h == 'number'){
38596                 var tbh = 0;
38597                 for (var i =0; i < this.toolbars.length;i++) {
38598                     // fixme - ask toolbars for heights?
38599                     tbh += this.toolbars[i].tb.el.getHeight();
38600                 }
38601                 
38602                 
38603                 
38604                 
38605                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38606                 this.el.setHeight(this.adjustWidth('textarea', ah));
38607                 this.iframe.style.height = ah + 'px';
38608                 if(this.doc){
38609                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38610                 }
38611             }
38612         }
38613     },
38614
38615     /**
38616      * Toggles the editor between standard and source edit mode.
38617      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38618      */
38619     toggleSourceEdit : function(sourceEditMode){
38620         
38621         this.sourceEditMode = sourceEditMode === true;
38622         
38623         if(this.sourceEditMode){
38624           
38625             this.syncValue();
38626             this.iframe.className = 'x-hidden';
38627             this.el.removeClass('x-hidden');
38628             this.el.dom.removeAttribute('tabIndex');
38629             this.el.focus();
38630         }else{
38631              
38632             this.pushValue();
38633             this.iframe.className = '';
38634             this.el.addClass('x-hidden');
38635             this.el.dom.setAttribute('tabIndex', -1);
38636             this.deferFocus();
38637         }
38638         this.setSize(this.wrap.getSize());
38639         this.fireEvent('editmodechange', this, this.sourceEditMode);
38640     },
38641
38642     // private used internally
38643     createLink : function(){
38644         var url = prompt(this.createLinkText, this.defaultLinkValue);
38645         if(url && url != 'http:/'+'/'){
38646             this.relayCmd('createlink', url);
38647         }
38648     },
38649
38650     // private (for BoxComponent)
38651     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38652
38653     // private (for BoxComponent)
38654     getResizeEl : function(){
38655         return this.wrap;
38656     },
38657
38658     // private (for BoxComponent)
38659     getPositionEl : function(){
38660         return this.wrap;
38661     },
38662
38663     // private
38664     initEvents : function(){
38665         this.originalValue = this.getValue();
38666     },
38667
38668     /**
38669      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38670      * @method
38671      */
38672     markInvalid : Roo.emptyFn,
38673     /**
38674      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38675      * @method
38676      */
38677     clearInvalid : Roo.emptyFn,
38678
38679     setValue : function(v){
38680         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38681         this.pushValue();
38682     },
38683
38684     /**
38685      * Protected method that will not generally be called directly. If you need/want
38686      * custom HTML cleanup, this is the method you should override.
38687      * @param {String} html The HTML to be cleaned
38688      * return {String} The cleaned HTML
38689      */
38690     cleanHtml : function(html){
38691         html = String(html);
38692         if(html.length > 5){
38693             if(Roo.isSafari){ // strip safari nonsense
38694                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38695             }
38696         }
38697         if(html == '&nbsp;'){
38698             html = '';
38699         }
38700         return html;
38701     },
38702
38703     /**
38704      * Protected method that will not generally be called directly. Syncs the contents
38705      * of the editor iframe with the textarea.
38706      */
38707     syncValue : function(){
38708         if(this.initialized){
38709             var bd = (this.doc.body || this.doc.documentElement);
38710             this.cleanUpPaste();
38711             var html = bd.innerHTML;
38712             if(Roo.isSafari){
38713                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38714                 var m = bs.match(/text-align:(.*?);/i);
38715                 if(m && m[1]){
38716                     html = '<div style="'+m[0]+'">' + html + '</div>';
38717                 }
38718             }
38719             html = this.cleanHtml(html);
38720             if(this.fireEvent('beforesync', this, html) !== false){
38721                 this.el.dom.value = html;
38722                 this.fireEvent('sync', this, html);
38723             }
38724         }
38725     },
38726
38727     /**
38728      * Protected method that will not generally be called directly. Pushes the value of the textarea
38729      * into the iframe editor.
38730      */
38731     pushValue : function(){
38732         if(this.initialized){
38733             var v = this.el.dom.value;
38734             if(v.length < 1){
38735                 v = '&#160;';
38736             }
38737             
38738             if(this.fireEvent('beforepush', this, v) !== false){
38739                 var d = (this.doc.body || this.doc.documentElement);
38740                 d.innerHTML = v;
38741                 this.cleanUpPaste();
38742                 this.el.dom.value = d.innerHTML;
38743                 this.fireEvent('push', this, v);
38744             }
38745         }
38746     },
38747
38748     // private
38749     deferFocus : function(){
38750         this.focus.defer(10, this);
38751     },
38752
38753     // doc'ed in Field
38754     focus : function(){
38755         if(this.win && !this.sourceEditMode){
38756             this.win.focus();
38757         }else{
38758             this.el.focus();
38759         }
38760     },
38761     
38762     assignDocWin: function()
38763     {
38764         var iframe = this.iframe;
38765         
38766          if(Roo.isIE){
38767             this.doc = iframe.contentWindow.document;
38768             this.win = iframe.contentWindow;
38769         } else {
38770             if (!Roo.get(this.frameId)) {
38771                 return;
38772             }
38773             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38774             this.win = Roo.get(this.frameId).dom.contentWindow;
38775         }
38776     },
38777     
38778     // private
38779     initEditor : function(){
38780         //console.log("INIT EDITOR");
38781         this.assignDocWin();
38782         
38783         
38784         
38785         this.doc.designMode="on";
38786         this.doc.open();
38787         this.doc.write(this.getDocMarkup());
38788         this.doc.close();
38789         
38790         var dbody = (this.doc.body || this.doc.documentElement);
38791         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38792         // this copies styles from the containing element into thsi one..
38793         // not sure why we need all of this..
38794         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38795         ss['background-attachment'] = 'fixed'; // w3c
38796         dbody.bgProperties = 'fixed'; // ie
38797         Roo.DomHelper.applyStyles(dbody, ss);
38798         Roo.EventManager.on(this.doc, {
38799             'mousedown': this.onEditorEvent,
38800             'dblclick': this.onEditorEvent,
38801             'click': this.onEditorEvent,
38802             'keyup': this.onEditorEvent,
38803             buffer:100,
38804             scope: this
38805         });
38806         if(Roo.isGecko){
38807             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38808         }
38809         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38810             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38811         }
38812         this.initialized = true;
38813
38814         this.fireEvent('initialize', this);
38815         this.pushValue();
38816     },
38817
38818     // private
38819     onDestroy : function(){
38820         
38821         
38822         
38823         if(this.rendered){
38824             
38825             for (var i =0; i < this.toolbars.length;i++) {
38826                 // fixme - ask toolbars for heights?
38827                 this.toolbars[i].onDestroy();
38828             }
38829             
38830             this.wrap.dom.innerHTML = '';
38831             this.wrap.remove();
38832         }
38833     },
38834
38835     // private
38836     onFirstFocus : function(){
38837         
38838         this.assignDocWin();
38839         
38840         
38841         this.activated = true;
38842         for (var i =0; i < this.toolbars.length;i++) {
38843             this.toolbars[i].onFirstFocus();
38844         }
38845        
38846         if(Roo.isGecko){ // prevent silly gecko errors
38847             this.win.focus();
38848             var s = this.win.getSelection();
38849             if(!s.focusNode || s.focusNode.nodeType != 3){
38850                 var r = s.getRangeAt(0);
38851                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38852                 r.collapse(true);
38853                 this.deferFocus();
38854             }
38855             try{
38856                 this.execCmd('useCSS', true);
38857                 this.execCmd('styleWithCSS', false);
38858             }catch(e){}
38859         }
38860         this.fireEvent('activate', this);
38861     },
38862
38863     // private
38864     adjustFont: function(btn){
38865         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38866         //if(Roo.isSafari){ // safari
38867         //    adjust *= 2;
38868        // }
38869         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38870         if(Roo.isSafari){ // safari
38871             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38872             v =  (v < 10) ? 10 : v;
38873             v =  (v > 48) ? 48 : v;
38874             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38875             
38876         }
38877         
38878         
38879         v = Math.max(1, v+adjust);
38880         
38881         this.execCmd('FontSize', v  );
38882     },
38883
38884     onEditorEvent : function(e){
38885         this.fireEvent('editorevent', this, e);
38886       //  this.updateToolbar();
38887         this.syncValue();
38888     },
38889
38890     insertTag : function(tg)
38891     {
38892         // could be a bit smarter... -> wrap the current selected tRoo..
38893         
38894         this.execCmd("formatblock",   tg);
38895         
38896     },
38897     
38898     insertText : function(txt)
38899     {
38900         
38901         
38902         range = this.createRange();
38903         range.deleteContents();
38904                //alert(Sender.getAttribute('label'));
38905                
38906         range.insertNode(this.doc.createTextNode(txt));
38907     } ,
38908     
38909     // private
38910     relayBtnCmd : function(btn){
38911         this.relayCmd(btn.cmd);
38912     },
38913
38914     /**
38915      * Executes a Midas editor command on the editor document and performs necessary focus and
38916      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38917      * @param {String} cmd The Midas command
38918      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38919      */
38920     relayCmd : function(cmd, value){
38921         this.win.focus();
38922         this.execCmd(cmd, value);
38923         this.fireEvent('editorevent', this);
38924         //this.updateToolbar();
38925         this.deferFocus();
38926     },
38927
38928     /**
38929      * Executes a Midas editor command directly on the editor document.
38930      * For visual commands, you should use {@link #relayCmd} instead.
38931      * <b>This should only be called after the editor is initialized.</b>
38932      * @param {String} cmd The Midas command
38933      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38934      */
38935     execCmd : function(cmd, value){
38936         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38937         this.syncValue();
38938     },
38939
38940    
38941     /**
38942      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38943      * to insert tRoo.
38944      * @param {String} text
38945      */
38946     insertAtCursor : function(text){
38947         if(!this.activated){
38948             return;
38949         }
38950         if(Roo.isIE){
38951             this.win.focus();
38952             var r = this.doc.selection.createRange();
38953             if(r){
38954                 r.collapse(true);
38955                 r.pasteHTML(text);
38956                 this.syncValue();
38957                 this.deferFocus();
38958             }
38959         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38960             this.win.focus();
38961             this.execCmd('InsertHTML', text);
38962             this.deferFocus();
38963         }
38964     },
38965  // private
38966     mozKeyPress : function(e){
38967         if(e.ctrlKey){
38968             var c = e.getCharCode(), cmd;
38969           
38970             if(c > 0){
38971                 c = String.fromCharCode(c).toLowerCase();
38972                 switch(c){
38973                     case 'b':
38974                         cmd = 'bold';
38975                     break;
38976                     case 'i':
38977                         cmd = 'italic';
38978                     break;
38979                     case 'u':
38980                         cmd = 'underline';
38981                     case 'v':
38982                         this.cleanUpPaste.defer(100, this);
38983                         return;
38984                     break;
38985                 }
38986                 if(cmd){
38987                     this.win.focus();
38988                     this.execCmd(cmd);
38989                     this.deferFocus();
38990                     e.preventDefault();
38991                 }
38992                 
38993             }
38994         }
38995     },
38996
38997     // private
38998     fixKeys : function(){ // load time branching for fastest keydown performance
38999         if(Roo.isIE){
39000             return function(e){
39001                 var k = e.getKey(), r;
39002                 if(k == e.TAB){
39003                     e.stopEvent();
39004                     r = this.doc.selection.createRange();
39005                     if(r){
39006                         r.collapse(true);
39007                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39008                         this.deferFocus();
39009                     }
39010                     return;
39011                 }
39012                 
39013                 if(k == e.ENTER){
39014                     r = this.doc.selection.createRange();
39015                     if(r){
39016                         var target = r.parentElement();
39017                         if(!target || target.tagName.toLowerCase() != 'li'){
39018                             e.stopEvent();
39019                             r.pasteHTML('<br />');
39020                             r.collapse(false);
39021                             r.select();
39022                         }
39023                     }
39024                 }
39025                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39026                     this.cleanUpPaste.defer(100, this);
39027                     return;
39028                 }
39029                 
39030                 
39031             };
39032         }else if(Roo.isOpera){
39033             return function(e){
39034                 var k = e.getKey();
39035                 if(k == e.TAB){
39036                     e.stopEvent();
39037                     this.win.focus();
39038                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39039                     this.deferFocus();
39040                 }
39041                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39042                     this.cleanUpPaste.defer(100, this);
39043                     return;
39044                 }
39045                 
39046             };
39047         }else if(Roo.isSafari){
39048             return function(e){
39049                 var k = e.getKey();
39050                 
39051                 if(k == e.TAB){
39052                     e.stopEvent();
39053                     this.execCmd('InsertText','\t');
39054                     this.deferFocus();
39055                     return;
39056                 }
39057                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39058                     this.cleanUpPaste.defer(100, this);
39059                     return;
39060                 }
39061                 
39062              };
39063         }
39064     }(),
39065     
39066     getAllAncestors: function()
39067     {
39068         var p = this.getSelectedNode();
39069         var a = [];
39070         if (!p) {
39071             a.push(p); // push blank onto stack..
39072             p = this.getParentElement();
39073         }
39074         
39075         
39076         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39077             a.push(p);
39078             p = p.parentNode;
39079         }
39080         a.push(this.doc.body);
39081         return a;
39082     },
39083     lastSel : false,
39084     lastSelNode : false,
39085     
39086     
39087     getSelection : function() 
39088     {
39089         this.assignDocWin();
39090         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39091     },
39092     
39093     getSelectedNode: function() 
39094     {
39095         // this may only work on Gecko!!!
39096         
39097         // should we cache this!!!!
39098         
39099         
39100         
39101          
39102         var range = this.createRange(this.getSelection());
39103         
39104         if (Roo.isIE) {
39105             var parent = range.parentElement();
39106             while (true) {
39107                 var testRange = range.duplicate();
39108                 testRange.moveToElementText(parent);
39109                 if (testRange.inRange(range)) {
39110                     break;
39111                 }
39112                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39113                     break;
39114                 }
39115                 parent = parent.parentElement;
39116             }
39117             return parent;
39118         }
39119         
39120         
39121         var ar = range.endContainer.childNodes;
39122         if (!ar.length) {
39123             ar = range.commonAncestorContainer.childNodes;
39124             //alert(ar.length);
39125         }
39126         var nodes = [];
39127         var other_nodes = [];
39128         var has_other_nodes = false;
39129         for (var i=0;i<ar.length;i++) {
39130             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39131                 continue;
39132             }
39133             // fullly contained node.
39134             
39135             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39136                 nodes.push(ar[i]);
39137                 continue;
39138             }
39139             
39140             // probably selected..
39141             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39142                 other_nodes.push(ar[i]);
39143                 continue;
39144             }
39145             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39146                 continue;
39147             }
39148             
39149             
39150             has_other_nodes = true;
39151         }
39152         if (!nodes.length && other_nodes.length) {
39153             nodes= other_nodes;
39154         }
39155         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39156             return false;
39157         }
39158         
39159         return nodes[0];
39160     },
39161     createRange: function(sel)
39162     {
39163         // this has strange effects when using with 
39164         // top toolbar - not sure if it's a great idea.
39165         //this.editor.contentWindow.focus();
39166         if (typeof sel != "undefined") {
39167             try {
39168                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39169             } catch(e) {
39170                 return this.doc.createRange();
39171             }
39172         } else {
39173             return this.doc.createRange();
39174         }
39175     },
39176     getParentElement: function()
39177     {
39178         
39179         this.assignDocWin();
39180         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39181         
39182         var range = this.createRange(sel);
39183          
39184         try {
39185             var p = range.commonAncestorContainer;
39186             while (p.nodeType == 3) { // text node
39187                 p = p.parentNode;
39188             }
39189             return p;
39190         } catch (e) {
39191             return null;
39192         }
39193     
39194     },
39195     
39196     
39197     
39198     // BC Hacks - cause I cant work out what i was trying to do..
39199     rangeIntersectsNode : function(range, node)
39200     {
39201         var nodeRange = node.ownerDocument.createRange();
39202         try {
39203             nodeRange.selectNode(node);
39204         }
39205         catch (e) {
39206             nodeRange.selectNodeContents(node);
39207         }
39208
39209         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39210                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39211     },
39212     rangeCompareNode : function(range, node) {
39213         var nodeRange = node.ownerDocument.createRange();
39214         try {
39215             nodeRange.selectNode(node);
39216         } catch (e) {
39217             nodeRange.selectNodeContents(node);
39218         }
39219         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39220         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39221
39222         if (nodeIsBefore && !nodeIsAfter)
39223             return 0;
39224         if (!nodeIsBefore && nodeIsAfter)
39225             return 1;
39226         if (nodeIsBefore && nodeIsAfter)
39227             return 2;
39228
39229         return 3;
39230     },
39231
39232     // private? - in a new class?
39233     cleanUpPaste :  function()
39234     {
39235         // cleans up the whole document..
39236       //  console.log('cleanuppaste');
39237         this.cleanUpChildren(this.doc.body);
39238         
39239         
39240     },
39241     cleanUpChildren : function (n)
39242     {
39243         if (!n.childNodes.length) {
39244             return;
39245         }
39246         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39247            this.cleanUpChild(n.childNodes[i]);
39248         }
39249     },
39250     
39251     
39252         
39253     
39254     cleanUpChild : function (node)
39255     {
39256         //console.log(node);
39257         if (node.nodeName == "#text") {
39258             // clean up silly Windows -- stuff?
39259             return; 
39260         }
39261         if (node.nodeName == "#comment") {
39262             node.parentNode.removeChild(node);
39263             // clean up silly Windows -- stuff?
39264             return; 
39265         }
39266         
39267         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39268             // remove node.
39269             node.parentNode.removeChild(node);
39270             return;
39271             
39272         }
39273         if (!node.attributes || !node.attributes.length) {
39274             this.cleanUpChildren(node);
39275             return;
39276         }
39277         
39278         function cleanAttr(n,v)
39279         {
39280             
39281             if (v.match(/^\./) || v.match(/^\//)) {
39282                 return;
39283             }
39284             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39285                 return;
39286             }
39287             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39288             node.removeAttribute(n);
39289             
39290         }
39291         
39292         function cleanStyle(n,v)
39293         {
39294             if (v.match(/expression/)) { //XSS?? should we even bother..
39295                 node.removeAttribute(n);
39296                 return;
39297             }
39298             
39299             
39300             var parts = v.split(/;/);
39301             Roo.each(parts, function(p) {
39302                 p = p.replace(/\s+/g,'');
39303                 if (!p.length) {
39304                     return;
39305                 }
39306                 var l = p.split(':').shift().replace(/\s+/g,'');
39307                 
39308                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39309                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39310                     node.removeAttribute(n);
39311                     return false;
39312                 }
39313             });
39314             
39315             
39316         }
39317         
39318         
39319         for (var i = node.attributes.length-1; i > -1 ; i--) {
39320             var a = node.attributes[i];
39321             //console.log(a);
39322             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39323                 node.removeAttribute(a.name);
39324                 return;
39325             }
39326             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39327                 cleanAttr(a.name,a.value); // fixme..
39328                 return;
39329             }
39330             if (a.name == 'style') {
39331                 cleanStyle(a.name,a.value);
39332             }
39333             /// clean up MS crap..
39334             if (a.name == 'class') {
39335                 if (a.value.match(/^Mso/)) {
39336                     node.className = '';
39337                 }
39338             }
39339             
39340             // style cleanup!?
39341             // class cleanup?
39342             
39343         }
39344         
39345         
39346         this.cleanUpChildren(node);
39347         
39348         
39349     }
39350     
39351     
39352     // hide stuff that is not compatible
39353     /**
39354      * @event blur
39355      * @hide
39356      */
39357     /**
39358      * @event change
39359      * @hide
39360      */
39361     /**
39362      * @event focus
39363      * @hide
39364      */
39365     /**
39366      * @event specialkey
39367      * @hide
39368      */
39369     /**
39370      * @cfg {String} fieldClass @hide
39371      */
39372     /**
39373      * @cfg {String} focusClass @hide
39374      */
39375     /**
39376      * @cfg {String} autoCreate @hide
39377      */
39378     /**
39379      * @cfg {String} inputType @hide
39380      */
39381     /**
39382      * @cfg {String} invalidClass @hide
39383      */
39384     /**
39385      * @cfg {String} invalidText @hide
39386      */
39387     /**
39388      * @cfg {String} msgFx @hide
39389      */
39390     /**
39391      * @cfg {String} validateOnBlur @hide
39392      */
39393 });
39394
39395 Roo.form.HtmlEditor.white = [
39396         'area', 'br', 'img', 'input', 'hr', 'wbr',
39397         
39398        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39399        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39400        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39401        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39402        'table',   'ul',         'xmp', 
39403        
39404        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39405       'thead',   'tr', 
39406      
39407       'dir', 'menu', 'ol', 'ul', 'dl',
39408        
39409       'embed',  'object'
39410 ];
39411
39412
39413 Roo.form.HtmlEditor.black = [
39414     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39415         'applet', // 
39416         'base',   'basefont', 'bgsound', 'blink',  'body', 
39417         'frame',  'frameset', 'head',    'html',   'ilayer', 
39418         'iframe', 'layer',  'link',     'meta',    'object',   
39419         'script', 'style' ,'title',  'xml' // clean later..
39420 ];
39421 Roo.form.HtmlEditor.clean = [
39422     'script', 'style', 'title', 'xml'
39423 ];
39424
39425 // attributes..
39426
39427 Roo.form.HtmlEditor.ablack = [
39428     'on'
39429 ];
39430     
39431 Roo.form.HtmlEditor.aclean = [ 
39432     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39433 ];
39434
39435 // protocols..
39436 Roo.form.HtmlEditor.pwhite= [
39437         'http',  'https',  'mailto'
39438 ];
39439
39440 Roo.form.HtmlEditor.cwhite= [
39441         'text-align',
39442         'font-size'
39443 ];
39444
39445 // <script type="text/javascript">
39446 /*
39447  * Based on
39448  * Ext JS Library 1.1.1
39449  * Copyright(c) 2006-2007, Ext JS, LLC.
39450  *  
39451  
39452  */
39453
39454 /**
39455  * @class Roo.form.HtmlEditorToolbar1
39456  * Basic Toolbar
39457  * 
39458  * Usage:
39459  *
39460  new Roo.form.HtmlEditor({
39461     ....
39462     toolbars : [
39463         new Roo.form.HtmlEditorToolbar1({
39464             disable : { fonts: 1 , format: 1, ..., ... , ...],
39465             btns : [ .... ]
39466         })
39467     }
39468      
39469  * 
39470  * @cfg {Object} disable List of elements to disable..
39471  * @cfg {Array} btns List of additional buttons.
39472  * 
39473  * 
39474  * NEEDS Extra CSS? 
39475  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39476  */
39477  
39478 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39479 {
39480     
39481     Roo.apply(this, config);
39482     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39483     // dont call parent... till later.
39484 }
39485
39486 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39487     
39488     tb: false,
39489     
39490     rendered: false,
39491     
39492     editor : false,
39493     /**
39494      * @cfg {Object} disable  List of toolbar elements to disable
39495          
39496      */
39497     disable : false,
39498       /**
39499      * @cfg {Array} fontFamilies An array of available font families
39500      */
39501     fontFamilies : [
39502         'Arial',
39503         'Courier New',
39504         'Tahoma',
39505         'Times New Roman',
39506         'Verdana'
39507     ],
39508     
39509     specialChars : [
39510            "&#169;",
39511           "&#174;",     
39512           "&#8482;",    
39513           "&#163;" ,    
39514          // "&#8212;",    
39515           "&#8230;",    
39516           "&#247;" ,    
39517         //  "&#225;" ,     ?? a acute?
39518            "&#8364;"    , //Euro
39519        //   "&#8220;"    ,
39520         //  "&#8221;"    ,
39521         //  "&#8226;"    ,
39522           "&#176;"  //   , // degrees
39523
39524          // "&#233;"     , // e ecute
39525          // "&#250;"     , // u ecute?
39526     ],
39527     inputElements : [ 
39528             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39529             "input:submit", "input:button", "select", "textarea", "label" ],
39530     formats : [
39531         ["p"] ,  
39532         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39533         ["pre"],[ "code"], 
39534         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39535     ],
39536      /**
39537      * @cfg {String} defaultFont default font to use.
39538      */
39539     defaultFont: 'tahoma',
39540    
39541     fontSelect : false,
39542     
39543     
39544     formatCombo : false,
39545     
39546     init : function(editor)
39547     {
39548         this.editor = editor;
39549         
39550         
39551         var fid = editor.frameId;
39552         var etb = this;
39553         function btn(id, toggle, handler){
39554             var xid = fid + '-'+ id ;
39555             return {
39556                 id : xid,
39557                 cmd : id,
39558                 cls : 'x-btn-icon x-edit-'+id,
39559                 enableToggle:toggle !== false,
39560                 scope: editor, // was editor...
39561                 handler:handler||editor.relayBtnCmd,
39562                 clickEvent:'mousedown',
39563                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39564                 tabIndex:-1
39565             };
39566         }
39567         
39568         
39569         
39570         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39571         this.tb = tb;
39572          // stop form submits
39573         tb.el.on('click', function(e){
39574             e.preventDefault(); // what does this do?
39575         });
39576
39577         if(!this.disable.font && !Roo.isSafari){
39578             /* why no safari for fonts
39579             editor.fontSelect = tb.el.createChild({
39580                 tag:'select',
39581                 tabIndex: -1,
39582                 cls:'x-font-select',
39583                 html: editor.createFontOptions()
39584             });
39585             editor.fontSelect.on('change', function(){
39586                 var font = editor.fontSelect.dom.value;
39587                 editor.relayCmd('fontname', font);
39588                 editor.deferFocus();
39589             }, editor);
39590             tb.add(
39591                 editor.fontSelect.dom,
39592                 '-'
39593             );
39594             */
39595         };
39596         if(!this.disable.formats){
39597             this.formatCombo = new Roo.form.ComboBox({
39598                 store: new Roo.data.SimpleStore({
39599                     id : 'tag',
39600                     fields: ['tag'],
39601                     data : this.formats // from states.js
39602                 }),
39603                 blockFocus : true,
39604                 //autoCreate : {tag: "div",  size: "20"},
39605                 displayField:'tag',
39606                 typeAhead: false,
39607                 mode: 'local',
39608                 editable : false,
39609                 triggerAction: 'all',
39610                 emptyText:'Add tag',
39611                 selectOnFocus:true,
39612                 width:135,
39613                 listeners : {
39614                     'select': function(c, r, i) {
39615                         editor.insertTag(r.get('tag'));
39616                         editor.focus();
39617                     }
39618                 }
39619
39620             });
39621             tb.addField(this.formatCombo);
39622             
39623         }
39624         
39625         if(!this.disable.format){
39626             tb.add(
39627                 btn('bold'),
39628                 btn('italic'),
39629                 btn('underline')
39630             );
39631         };
39632         if(!this.disable.fontSize){
39633             tb.add(
39634                 '-',
39635                 
39636                 
39637                 btn('increasefontsize', false, editor.adjustFont),
39638                 btn('decreasefontsize', false, editor.adjustFont)
39639             );
39640         };
39641         
39642         
39643         if(this.disable.colors){
39644             tb.add(
39645                 '-', {
39646                     id:editor.frameId +'-forecolor',
39647                     cls:'x-btn-icon x-edit-forecolor',
39648                     clickEvent:'mousedown',
39649                     tooltip: this.buttonTips['forecolor'] || undefined,
39650                     tabIndex:-1,
39651                     menu : new Roo.menu.ColorMenu({
39652                         allowReselect: true,
39653                         focus: Roo.emptyFn,
39654                         value:'000000',
39655                         plain:true,
39656                         selectHandler: function(cp, color){
39657                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39658                             editor.deferFocus();
39659                         },
39660                         scope: editor,
39661                         clickEvent:'mousedown'
39662                     })
39663                 }, {
39664                     id:editor.frameId +'backcolor',
39665                     cls:'x-btn-icon x-edit-backcolor',
39666                     clickEvent:'mousedown',
39667                     tooltip: this.buttonTips['backcolor'] || undefined,
39668                     tabIndex:-1,
39669                     menu : new Roo.menu.ColorMenu({
39670                         focus: Roo.emptyFn,
39671                         value:'FFFFFF',
39672                         plain:true,
39673                         allowReselect: true,
39674                         selectHandler: function(cp, color){
39675                             if(Roo.isGecko){
39676                                 editor.execCmd('useCSS', false);
39677                                 editor.execCmd('hilitecolor', color);
39678                                 editor.execCmd('useCSS', true);
39679                                 editor.deferFocus();
39680                             }else{
39681                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39682                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39683                                 editor.deferFocus();
39684                             }
39685                         },
39686                         scope:editor,
39687                         clickEvent:'mousedown'
39688                     })
39689                 }
39690             );
39691         };
39692         // now add all the items...
39693         
39694
39695         if(!this.disable.alignments){
39696             tb.add(
39697                 '-',
39698                 btn('justifyleft'),
39699                 btn('justifycenter'),
39700                 btn('justifyright')
39701             );
39702         };
39703
39704         //if(!Roo.isSafari){
39705             if(!this.disable.links){
39706                 tb.add(
39707                     '-',
39708                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39709                 );
39710             };
39711
39712             if(!this.disable.lists){
39713                 tb.add(
39714                     '-',
39715                     btn('insertorderedlist'),
39716                     btn('insertunorderedlist')
39717                 );
39718             }
39719             if(!this.disable.sourceEdit){
39720                 tb.add(
39721                     '-',
39722                     btn('sourceedit', true, function(btn){
39723                         this.toggleSourceEdit(btn.pressed);
39724                     })
39725                 );
39726             }
39727         //}
39728         
39729         var smenu = { };
39730         // special menu.. - needs to be tidied up..
39731         if (!this.disable.special) {
39732             smenu = {
39733                 text: "&#169;",
39734                 cls: 'x-edit-none',
39735                 menu : {
39736                     items : []
39737                    }
39738             };
39739             for (var i =0; i < this.specialChars.length; i++) {
39740                 smenu.menu.items.push({
39741                     
39742                     html: this.specialChars[i],
39743                     handler: function(a,b) {
39744                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39745                         
39746                     },
39747                     tabIndex:-1
39748                 });
39749             }
39750             
39751             
39752             tb.add(smenu);
39753             
39754             
39755         }
39756         if (this.btns) {
39757             for(var i =0; i< this.btns.length;i++) {
39758                 var b = this.btns[i];
39759                 b.cls =  'x-edit-none';
39760                 b.scope = editor;
39761                 tb.add(b);
39762             }
39763         
39764         }
39765         
39766         
39767         
39768         // disable everything...
39769         
39770         this.tb.items.each(function(item){
39771            if(item.id != editor.frameId+ '-sourceedit'){
39772                 item.disable();
39773             }
39774         });
39775         this.rendered = true;
39776         
39777         // the all the btns;
39778         editor.on('editorevent', this.updateToolbar, this);
39779         // other toolbars need to implement this..
39780         //editor.on('editmodechange', this.updateToolbar, this);
39781     },
39782     
39783     
39784     
39785     /**
39786      * Protected method that will not generally be called directly. It triggers
39787      * a toolbar update by reading the markup state of the current selection in the editor.
39788      */
39789     updateToolbar: function(){
39790
39791         if(!this.editor.activated){
39792             this.editor.onFirstFocus();
39793             return;
39794         }
39795
39796         var btns = this.tb.items.map, 
39797             doc = this.editor.doc,
39798             frameId = this.editor.frameId;
39799
39800         if(!this.disable.font && !Roo.isSafari){
39801             /*
39802             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39803             if(name != this.fontSelect.dom.value){
39804                 this.fontSelect.dom.value = name;
39805             }
39806             */
39807         }
39808         if(!this.disable.format){
39809             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39810             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39811             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39812         }
39813         if(!this.disable.alignments){
39814             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39815             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39816             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39817         }
39818         if(!Roo.isSafari && !this.disable.lists){
39819             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39820             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39821         }
39822         
39823         var ans = this.editor.getAllAncestors();
39824         if (this.formatCombo) {
39825             
39826             
39827             var store = this.formatCombo.store;
39828             this.formatCombo.setValue("");
39829             for (var i =0; i < ans.length;i++) {
39830                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39831                     // select it..
39832                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39833                     break;
39834                 }
39835             }
39836         }
39837         
39838         
39839         
39840         // hides menus... - so this cant be on a menu...
39841         Roo.menu.MenuMgr.hideAll();
39842
39843         //this.editorsyncValue();
39844     },
39845    
39846     
39847     createFontOptions : function(){
39848         var buf = [], fs = this.fontFamilies, ff, lc;
39849         for(var i = 0, len = fs.length; i< len; i++){
39850             ff = fs[i];
39851             lc = ff.toLowerCase();
39852             buf.push(
39853                 '<option value="',lc,'" style="font-family:',ff,';"',
39854                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39855                     ff,
39856                 '</option>'
39857             );
39858         }
39859         return buf.join('');
39860     },
39861     
39862     toggleSourceEdit : function(sourceEditMode){
39863         if(sourceEditMode === undefined){
39864             sourceEditMode = !this.sourceEditMode;
39865         }
39866         this.sourceEditMode = sourceEditMode === true;
39867         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39868         // just toggle the button?
39869         if(btn.pressed !== this.editor.sourceEditMode){
39870             btn.toggle(this.editor.sourceEditMode);
39871             return;
39872         }
39873         
39874         if(this.sourceEditMode){
39875             this.tb.items.each(function(item){
39876                 if(item.cmd != 'sourceedit'){
39877                     item.disable();
39878                 }
39879             });
39880           
39881         }else{
39882             if(this.initialized){
39883                 this.tb.items.each(function(item){
39884                     item.enable();
39885                 });
39886             }
39887             
39888         }
39889         // tell the editor that it's been pressed..
39890         this.editor.toggleSourceEdit(sourceEditMode);
39891        
39892     },
39893      /**
39894      * Object collection of toolbar tooltips for the buttons in the editor. The key
39895      * is the command id associated with that button and the value is a valid QuickTips object.
39896      * For example:
39897 <pre><code>
39898 {
39899     bold : {
39900         title: 'Bold (Ctrl+B)',
39901         text: 'Make the selected text bold.',
39902         cls: 'x-html-editor-tip'
39903     },
39904     italic : {
39905         title: 'Italic (Ctrl+I)',
39906         text: 'Make the selected text italic.',
39907         cls: 'x-html-editor-tip'
39908     },
39909     ...
39910 </code></pre>
39911     * @type Object
39912      */
39913     buttonTips : {
39914         bold : {
39915             title: 'Bold (Ctrl+B)',
39916             text: 'Make the selected text bold.',
39917             cls: 'x-html-editor-tip'
39918         },
39919         italic : {
39920             title: 'Italic (Ctrl+I)',
39921             text: 'Make the selected text italic.',
39922             cls: 'x-html-editor-tip'
39923         },
39924         underline : {
39925             title: 'Underline (Ctrl+U)',
39926             text: 'Underline the selected text.',
39927             cls: 'x-html-editor-tip'
39928         },
39929         increasefontsize : {
39930             title: 'Grow Text',
39931             text: 'Increase the font size.',
39932             cls: 'x-html-editor-tip'
39933         },
39934         decreasefontsize : {
39935             title: 'Shrink Text',
39936             text: 'Decrease the font size.',
39937             cls: 'x-html-editor-tip'
39938         },
39939         backcolor : {
39940             title: 'Text Highlight Color',
39941             text: 'Change the background color of the selected text.',
39942             cls: 'x-html-editor-tip'
39943         },
39944         forecolor : {
39945             title: 'Font Color',
39946             text: 'Change the color of the selected text.',
39947             cls: 'x-html-editor-tip'
39948         },
39949         justifyleft : {
39950             title: 'Align Text Left',
39951             text: 'Align text to the left.',
39952             cls: 'x-html-editor-tip'
39953         },
39954         justifycenter : {
39955             title: 'Center Text',
39956             text: 'Center text in the editor.',
39957             cls: 'x-html-editor-tip'
39958         },
39959         justifyright : {
39960             title: 'Align Text Right',
39961             text: 'Align text to the right.',
39962             cls: 'x-html-editor-tip'
39963         },
39964         insertunorderedlist : {
39965             title: 'Bullet List',
39966             text: 'Start a bulleted list.',
39967             cls: 'x-html-editor-tip'
39968         },
39969         insertorderedlist : {
39970             title: 'Numbered List',
39971             text: 'Start a numbered list.',
39972             cls: 'x-html-editor-tip'
39973         },
39974         createlink : {
39975             title: 'Hyperlink',
39976             text: 'Make the selected text a hyperlink.',
39977             cls: 'x-html-editor-tip'
39978         },
39979         sourceedit : {
39980             title: 'Source Edit',
39981             text: 'Switch to source editing mode.',
39982             cls: 'x-html-editor-tip'
39983         }
39984     },
39985     // private
39986     onDestroy : function(){
39987         if(this.rendered){
39988             
39989             this.tb.items.each(function(item){
39990                 if(item.menu){
39991                     item.menu.removeAll();
39992                     if(item.menu.el){
39993                         item.menu.el.destroy();
39994                     }
39995                 }
39996                 item.destroy();
39997             });
39998              
39999         }
40000     },
40001     onFirstFocus: function() {
40002         this.tb.items.each(function(item){
40003            item.enable();
40004         });
40005     }
40006 });
40007
40008
40009
40010
40011 // <script type="text/javascript">
40012 /*
40013  * Based on
40014  * Ext JS Library 1.1.1
40015  * Copyright(c) 2006-2007, Ext JS, LLC.
40016  *  
40017  
40018  */
40019
40020  
40021 /**
40022  * @class Roo.form.HtmlEditor.ToolbarContext
40023  * Context Toolbar
40024  * 
40025  * Usage:
40026  *
40027  new Roo.form.HtmlEditor({
40028     ....
40029     toolbars : [
40030         new Roo.form.HtmlEditor.ToolbarStandard(),
40031         new Roo.form.HtmlEditor.ToolbarContext()
40032         })
40033     }
40034      
40035  * 
40036  * @config : {Object} disable List of elements to disable.. (not done yet.)
40037  * 
40038  * 
40039  */
40040
40041 Roo.form.HtmlEditor.ToolbarContext = function(config)
40042 {
40043     
40044     Roo.apply(this, config);
40045     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40046     // dont call parent... till later.
40047 }
40048 Roo.form.HtmlEditor.ToolbarContext.types = {
40049     'IMG' : {
40050         width : {
40051             title: "Width",
40052             width: 40
40053         },
40054         height:  {
40055             title: "Height",
40056             width: 40
40057         },
40058         align: {
40059             title: "Align",
40060             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40061             width : 80
40062             
40063         },
40064         border: {
40065             title: "Border",
40066             width: 40
40067         },
40068         alt: {
40069             title: "Alt",
40070             width: 120
40071         },
40072         src : {
40073             title: "Src",
40074             width: 220
40075         }
40076         
40077     },
40078     'A' : {
40079         name : {
40080             title: "Name",
40081             width: 50
40082         },
40083         href:  {
40084             title: "Href",
40085             width: 220
40086         } // border?
40087         
40088     },
40089     'TABLE' : {
40090         rows : {
40091             title: "Rows",
40092             width: 20
40093         },
40094         cols : {
40095             title: "Cols",
40096             width: 20
40097         },
40098         width : {
40099             title: "Width",
40100             width: 40
40101         },
40102         height : {
40103             title: "Height",
40104             width: 40
40105         },
40106         border : {
40107             title: "Border",
40108             width: 20
40109         }
40110     },
40111     'TD' : {
40112         width : {
40113             title: "Width",
40114             width: 40
40115         },
40116         height : {
40117             title: "Height",
40118             width: 40
40119         },   
40120         align: {
40121             title: "Align",
40122             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40123             width: 40
40124         },
40125         valign: {
40126             title: "Valign",
40127             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40128             width: 40
40129         },
40130         colspan: {
40131             title: "Colspan",
40132             width: 20
40133             
40134         }
40135     },
40136     'INPUT' : {
40137         name : {
40138             title: "name",
40139             width: 120
40140         },
40141         value : {
40142             title: "Value",
40143             width: 120
40144         },
40145         width : {
40146             title: "Width",
40147             width: 40
40148         }
40149     },
40150     'LABEL' : {
40151         'for' : {
40152             title: "For",
40153             width: 120
40154         }
40155     },
40156     'TEXTAREA' : {
40157           name : {
40158             title: "name",
40159             width: 120
40160         },
40161         rows : {
40162             title: "Rows",
40163             width: 20
40164         },
40165         cols : {
40166             title: "Cols",
40167             width: 20
40168         }
40169     },
40170     'SELECT' : {
40171         name : {
40172             title: "name",
40173             width: 120
40174         },
40175         selectoptions : {
40176             title: "Options",
40177             width: 200
40178         }
40179     },
40180     'BODY' : {
40181         title : {
40182             title: "title",
40183             width: 120,
40184             disabled : true
40185         }
40186     }
40187 };
40188
40189
40190
40191 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40192     
40193     tb: false,
40194     
40195     rendered: false,
40196     
40197     editor : false,
40198     /**
40199      * @cfg {Object} disable  List of toolbar elements to disable
40200          
40201      */
40202     disable : false,
40203     
40204     
40205     
40206     toolbars : false,
40207     
40208     init : function(editor)
40209     {
40210         this.editor = editor;
40211         
40212         
40213         var fid = editor.frameId;
40214         var etb = this;
40215         function btn(id, toggle, handler){
40216             var xid = fid + '-'+ id ;
40217             return {
40218                 id : xid,
40219                 cmd : id,
40220                 cls : 'x-btn-icon x-edit-'+id,
40221                 enableToggle:toggle !== false,
40222                 scope: editor, // was editor...
40223                 handler:handler||editor.relayBtnCmd,
40224                 clickEvent:'mousedown',
40225                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40226                 tabIndex:-1
40227             };
40228         }
40229         // create a new element.
40230         var wdiv = editor.wrap.createChild({
40231                 tag: 'div'
40232             }, editor.wrap.dom.firstChild.nextSibling, true);
40233         
40234         // can we do this more than once??
40235         
40236          // stop form submits
40237       
40238  
40239         // disable everything...
40240         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40241         this.toolbars = {};
40242            
40243         for (var i in  ty) {
40244           
40245             this.toolbars[i] = this.buildToolbar(ty[i],i);
40246         }
40247         this.tb = this.toolbars.BODY;
40248         this.tb.el.show();
40249         
40250          
40251         this.rendered = true;
40252         
40253         // the all the btns;
40254         editor.on('editorevent', this.updateToolbar, this);
40255         // other toolbars need to implement this..
40256         //editor.on('editmodechange', this.updateToolbar, this);
40257     },
40258     
40259     
40260     
40261     /**
40262      * Protected method that will not generally be called directly. It triggers
40263      * a toolbar update by reading the markup state of the current selection in the editor.
40264      */
40265     updateToolbar: function(){
40266
40267         if(!this.editor.activated){
40268             this.editor.onFirstFocus();
40269             return;
40270         }
40271
40272         
40273         var ans = this.editor.getAllAncestors();
40274         
40275         // pick
40276         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40277         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40278         sel = sel ? sel : this.editor.doc.body;
40279         sel = sel.tagName.length ? sel : this.editor.doc.body;
40280         var tn = sel.tagName.toUpperCase();
40281         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40282         tn = sel.tagName.toUpperCase();
40283         if (this.tb.name  == tn) {
40284             return; // no change
40285         }
40286         this.tb.el.hide();
40287         ///console.log("show: " + tn);
40288         this.tb =  this.toolbars[tn];
40289         this.tb.el.show();
40290         this.tb.fields.each(function(e) {
40291             e.setValue(sel.getAttribute(e.name));
40292         });
40293         this.tb.selectedNode = sel;
40294         
40295         
40296         Roo.menu.MenuMgr.hideAll();
40297
40298         //this.editorsyncValue();
40299     },
40300    
40301        
40302     // private
40303     onDestroy : function(){
40304         if(this.rendered){
40305             
40306             this.tb.items.each(function(item){
40307                 if(item.menu){
40308                     item.menu.removeAll();
40309                     if(item.menu.el){
40310                         item.menu.el.destroy();
40311                     }
40312                 }
40313                 item.destroy();
40314             });
40315              
40316         }
40317     },
40318     onFirstFocus: function() {
40319         // need to do this for all the toolbars..
40320         this.tb.items.each(function(item){
40321            item.enable();
40322         });
40323     },
40324     buildToolbar: function(tlist, nm)
40325     {
40326         var editor = this.editor;
40327          // create a new element.
40328         var wdiv = editor.wrap.createChild({
40329                 tag: 'div'
40330             }, editor.wrap.dom.firstChild.nextSibling, true);
40331         
40332        
40333         var tb = new Roo.Toolbar(wdiv);
40334         tb.add(nm+ ":&nbsp;");
40335         for (var i in tlist) {
40336             var item = tlist[i];
40337             tb.add(item.title + ":&nbsp;");
40338             if (item.opts) {
40339                 // fixme
40340                 
40341               
40342                 tb.addField( new Roo.form.ComboBox({
40343                     store: new Roo.data.SimpleStore({
40344                         id : 'val',
40345                         fields: ['val'],
40346                         data : item.opts // from states.js
40347                     }),
40348                     name : i,
40349                     displayField:'val',
40350                     typeAhead: false,
40351                     mode: 'local',
40352                     editable : false,
40353                     triggerAction: 'all',
40354                     emptyText:'Select',
40355                     selectOnFocus:true,
40356                     width: item.width ? item.width  : 130,
40357                     listeners : {
40358                         'select': function(c, r, i) {
40359                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40360                         }
40361                     }
40362
40363                 }));
40364                 continue;
40365                     
40366                 
40367                 
40368                 
40369                 
40370                 tb.addField( new Roo.form.TextField({
40371                     name: i,
40372                     width: 100,
40373                     //allowBlank:false,
40374                     value: ''
40375                 }));
40376                 continue;
40377             }
40378             tb.addField( new Roo.form.TextField({
40379                 name: i,
40380                 width: item.width,
40381                 //allowBlank:true,
40382                 value: '',
40383                 listeners: {
40384                     'change' : function(f, nv, ov) {
40385                         tb.selectedNode.setAttribute(f.name, nv);
40386                     }
40387                 }
40388             }));
40389              
40390         }
40391         tb.el.on('click', function(e){
40392             e.preventDefault(); // what does this do?
40393         });
40394         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40395         tb.el.hide();
40396         tb.name = nm;
40397         // dont need to disable them... as they will get hidden
40398         return tb;
40399          
40400         
40401     }
40402     
40403     
40404     
40405     
40406 });
40407
40408
40409
40410
40411
40412 /*
40413  * Based on:
40414  * Ext JS Library 1.1.1
40415  * Copyright(c) 2006-2007, Ext JS, LLC.
40416  *
40417  * Originally Released Under LGPL - original licence link has changed is not relivant.
40418  *
40419  * Fork - LGPL
40420  * <script type="text/javascript">
40421  */
40422  
40423 /**
40424  * @class Roo.form.BasicForm
40425  * @extends Roo.util.Observable
40426  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40427  * @constructor
40428  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40429  * @param {Object} config Configuration options
40430  */
40431 Roo.form.BasicForm = function(el, config){
40432     this.allItems = [];
40433     this.childForms = [];
40434     Roo.apply(this, config);
40435     /*
40436      * The Roo.form.Field items in this form.
40437      * @type MixedCollection
40438      */
40439      
40440      
40441     this.items = new Roo.util.MixedCollection(false, function(o){
40442         return o.id || (o.id = Roo.id());
40443     });
40444     this.addEvents({
40445         /**
40446          * @event beforeaction
40447          * Fires before any action is performed. Return false to cancel the action.
40448          * @param {Form} this
40449          * @param {Action} action The action to be performed
40450          */
40451         beforeaction: true,
40452         /**
40453          * @event actionfailed
40454          * Fires when an action fails.
40455          * @param {Form} this
40456          * @param {Action} action The action that failed
40457          */
40458         actionfailed : true,
40459         /**
40460          * @event actioncomplete
40461          * Fires when an action is completed.
40462          * @param {Form} this
40463          * @param {Action} action The action that completed
40464          */
40465         actioncomplete : true
40466     });
40467     if(el){
40468         this.initEl(el);
40469     }
40470     Roo.form.BasicForm.superclass.constructor.call(this);
40471 };
40472
40473 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40474     /**
40475      * @cfg {String} method
40476      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40477      */
40478     /**
40479      * @cfg {DataReader} reader
40480      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40481      * This is optional as there is built-in support for processing JSON.
40482      */
40483     /**
40484      * @cfg {DataReader} errorReader
40485      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40486      * This is completely optional as there is built-in support for processing JSON.
40487      */
40488     /**
40489      * @cfg {String} url
40490      * The URL to use for form actions if one isn't supplied in the action options.
40491      */
40492     /**
40493      * @cfg {Boolean} fileUpload
40494      * Set to true if this form is a file upload.
40495      */
40496      
40497     /**
40498      * @cfg {Object} baseParams
40499      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40500      */
40501      /**
40502      
40503     /**
40504      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40505      */
40506     timeout: 30,
40507
40508     // private
40509     activeAction : null,
40510
40511     /**
40512      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40513      * or setValues() data instead of when the form was first created.
40514      */
40515     trackResetOnLoad : false,
40516     
40517     
40518     /**
40519      * childForms - used for multi-tab forms
40520      * @type {Array}
40521      */
40522     childForms : false,
40523     
40524     /**
40525      * allItems - full list of fields.
40526      * @type {Array}
40527      */
40528     allItems : false,
40529     
40530     /**
40531      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40532      * element by passing it or its id or mask the form itself by passing in true.
40533      * @type Mixed
40534      */
40535     waitMsgTarget : false,
40536
40537     // private
40538     initEl : function(el){
40539         this.el = Roo.get(el);
40540         this.id = this.el.id || Roo.id();
40541         this.el.on('submit', this.onSubmit, this);
40542         this.el.addClass('x-form');
40543     },
40544
40545     // private
40546     onSubmit : function(e){
40547         e.stopEvent();
40548     },
40549
40550     /**
40551      * Returns true if client-side validation on the form is successful.
40552      * @return Boolean
40553      */
40554     isValid : function(){
40555         var valid = true;
40556         this.items.each(function(f){
40557            if(!f.validate()){
40558                valid = false;
40559            }
40560         });
40561         return valid;
40562     },
40563
40564     /**
40565      * Returns true if any fields in this form have changed since their original load.
40566      * @return Boolean
40567      */
40568     isDirty : function(){
40569         var dirty = false;
40570         this.items.each(function(f){
40571            if(f.isDirty()){
40572                dirty = true;
40573                return false;
40574            }
40575         });
40576         return dirty;
40577     },
40578
40579     /**
40580      * Performs a predefined action (submit or load) or custom actions you define on this form.
40581      * @param {String} actionName The name of the action type
40582      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40583      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40584      * accept other config options):
40585      * <pre>
40586 Property          Type             Description
40587 ----------------  ---------------  ----------------------------------------------------------------------------------
40588 url               String           The url for the action (defaults to the form's url)
40589 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40590 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40591 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40592                                    validate the form on the client (defaults to false)
40593      * </pre>
40594      * @return {BasicForm} this
40595      */
40596     doAction : function(action, options){
40597         if(typeof action == 'string'){
40598             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40599         }
40600         if(this.fireEvent('beforeaction', this, action) !== false){
40601             this.beforeAction(action);
40602             action.run.defer(100, action);
40603         }
40604         return this;
40605     },
40606
40607     /**
40608      * Shortcut to do a submit action.
40609      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40610      * @return {BasicForm} this
40611      */
40612     submit : function(options){
40613         this.doAction('submit', options);
40614         return this;
40615     },
40616
40617     /**
40618      * Shortcut to do a load action.
40619      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40620      * @return {BasicForm} this
40621      */
40622     load : function(options){
40623         this.doAction('load', options);
40624         return this;
40625     },
40626
40627     /**
40628      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40629      * @param {Record} record The record to edit
40630      * @return {BasicForm} this
40631      */
40632     updateRecord : function(record){
40633         record.beginEdit();
40634         var fs = record.fields;
40635         fs.each(function(f){
40636             var field = this.findField(f.name);
40637             if(field){
40638                 record.set(f.name, field.getValue());
40639             }
40640         }, this);
40641         record.endEdit();
40642         return this;
40643     },
40644
40645     /**
40646      * Loads an Roo.data.Record into this form.
40647      * @param {Record} record The record to load
40648      * @return {BasicForm} this
40649      */
40650     loadRecord : function(record){
40651         this.setValues(record.data);
40652         return this;
40653     },
40654
40655     // private
40656     beforeAction : function(action){
40657         var o = action.options;
40658         
40659        
40660         if(this.waitMsgTarget === true){
40661             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40662         }else if(this.waitMsgTarget){
40663             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40664             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40665         }else {
40666             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40667         }
40668          
40669     },
40670
40671     // private
40672     afterAction : function(action, success){
40673         this.activeAction = null;
40674         var o = action.options;
40675         
40676         if(this.waitMsgTarget === true){
40677             this.el.unmask();
40678         }else if(this.waitMsgTarget){
40679             this.waitMsgTarget.unmask();
40680         }else{
40681             Roo.MessageBox.updateProgress(1);
40682             Roo.MessageBox.hide();
40683         }
40684          
40685         if(success){
40686             if(o.reset){
40687                 this.reset();
40688             }
40689             Roo.callback(o.success, o.scope, [this, action]);
40690             this.fireEvent('actioncomplete', this, action);
40691             
40692         }else{
40693             Roo.callback(o.failure, o.scope, [this, action]);
40694             // show an error message if no failed handler is set..
40695             if (!this.hasListener('actionfailed')) {
40696                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40697             }
40698             
40699             this.fireEvent('actionfailed', this, action);
40700         }
40701         
40702     },
40703
40704     /**
40705      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40706      * @param {String} id The value to search for
40707      * @return Field
40708      */
40709     findField : function(id){
40710         var field = this.items.get(id);
40711         if(!field){
40712             this.items.each(function(f){
40713                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40714                     field = f;
40715                     return false;
40716                 }
40717             });
40718         }
40719         return field || null;
40720     },
40721
40722     /**
40723      * Add a secondary form to this one, 
40724      * Used to provide tabbed forms. One form is primary, with hidden values 
40725      * which mirror the elements from the other forms.
40726      * 
40727      * @param {Roo.form.Form} form to add.
40728      * 
40729      */
40730     addForm : function(form)
40731     {
40732        
40733         if (this.childForms.indexOf(form) > -1) {
40734             // already added..
40735             return;
40736         }
40737         this.childForms.push(form);
40738         var n = '';
40739         Roo.each(form.allItems, function (fe) {
40740             
40741             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40742             if (this.findField(n)) { // already added..
40743                 return;
40744             }
40745             var add = new Roo.form.Hidden({
40746                 name : n
40747             });
40748             add.render(this.el);
40749             
40750             this.add( add );
40751         }, this);
40752         
40753     },
40754     /**
40755      * Mark fields in this form invalid in bulk.
40756      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40757      * @return {BasicForm} this
40758      */
40759     markInvalid : function(errors){
40760         if(errors instanceof Array){
40761             for(var i = 0, len = errors.length; i < len; i++){
40762                 var fieldError = errors[i];
40763                 var f = this.findField(fieldError.id);
40764                 if(f){
40765                     f.markInvalid(fieldError.msg);
40766                 }
40767             }
40768         }else{
40769             var field, id;
40770             for(id in errors){
40771                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40772                     field.markInvalid(errors[id]);
40773                 }
40774             }
40775         }
40776         Roo.each(this.childForms || [], function (f) {
40777             f.markInvalid(errors);
40778         });
40779         
40780         return this;
40781     },
40782
40783     /**
40784      * Set values for fields in this form in bulk.
40785      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40786      * @return {BasicForm} this
40787      */
40788     setValues : function(values){
40789         if(values instanceof Array){ // array of objects
40790             for(var i = 0, len = values.length; i < len; i++){
40791                 var v = values[i];
40792                 var f = this.findField(v.id);
40793                 if(f){
40794                     f.setValue(v.value);
40795                     if(this.trackResetOnLoad){
40796                         f.originalValue = f.getValue();
40797                     }
40798                 }
40799             }
40800         }else{ // object hash
40801             var field, id;
40802             for(id in values){
40803                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40804                     
40805                     if (field.setFromData && 
40806                         field.valueField && 
40807                         field.displayField &&
40808                         // combos' with local stores can 
40809                         // be queried via setValue()
40810                         // to set their value..
40811                         (field.store && !field.store.isLocal)
40812                         ) {
40813                         // it's a combo
40814                         var sd = { };
40815                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40816                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40817                         field.setFromData(sd);
40818                         
40819                     } else {
40820                         field.setValue(values[id]);
40821                     }
40822                     
40823                     
40824                     if(this.trackResetOnLoad){
40825                         field.originalValue = field.getValue();
40826                     }
40827                 }
40828             }
40829         }
40830          
40831         Roo.each(this.childForms || [], function (f) {
40832             f.setValues(values);
40833         });
40834                 
40835         return this;
40836     },
40837
40838     /**
40839      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40840      * they are returned as an array.
40841      * @param {Boolean} asString
40842      * @return {Object}
40843      */
40844     getValues : function(asString){
40845         if (this.childForms) {
40846             // copy values from the child forms
40847             Roo.each(this.childForms, function (f) {
40848                 this.setValues(f.getValues());
40849             }, this);
40850         }
40851         
40852         
40853         
40854         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40855         if(asString === true){
40856             return fs;
40857         }
40858         return Roo.urlDecode(fs);
40859     },
40860     
40861     /**
40862      * Returns the fields in this form as an object with key/value pairs. 
40863      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40864      * @return {Object}
40865      */
40866     getFieldValues : function()
40867     {
40868         if (this.childForms) {
40869             // copy values from the child forms
40870             Roo.each(this.childForms, function (f) {
40871                 this.setValues(f.getValues());
40872             }, this);
40873         }
40874         
40875         var ret = {};
40876         this.items.each(function(f){
40877             if (!f.getName()) {
40878                 return;
40879             }
40880             var v = f.getValue();
40881             if ((typeof(v) == 'object') && f.getRawValue) {
40882                 v = f.getRawValue() ; // dates..
40883             }
40884             ret[f.getName()] = v;
40885         });
40886         
40887         return ret;
40888     },
40889
40890     /**
40891      * Clears all invalid messages in this form.
40892      * @return {BasicForm} this
40893      */
40894     clearInvalid : function(){
40895         this.items.each(function(f){
40896            f.clearInvalid();
40897         });
40898         
40899         Roo.each(this.childForms || [], function (f) {
40900             f.clearInvalid();
40901         });
40902         
40903         
40904         return this;
40905     },
40906
40907     /**
40908      * Resets this form.
40909      * @return {BasicForm} this
40910      */
40911     reset : function(){
40912         this.items.each(function(f){
40913             f.reset();
40914         });
40915         
40916         Roo.each(this.childForms || [], function (f) {
40917             f.reset();
40918         });
40919        
40920         
40921         return this;
40922     },
40923
40924     /**
40925      * Add Roo.form components to this form.
40926      * @param {Field} field1
40927      * @param {Field} field2 (optional)
40928      * @param {Field} etc (optional)
40929      * @return {BasicForm} this
40930      */
40931     add : function(){
40932         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40933         return this;
40934     },
40935
40936
40937     /**
40938      * Removes a field from the items collection (does NOT remove its markup).
40939      * @param {Field} field
40940      * @return {BasicForm} this
40941      */
40942     remove : function(field){
40943         this.items.remove(field);
40944         return this;
40945     },
40946
40947     /**
40948      * Looks at the fields in this form, checks them for an id attribute,
40949      * and calls applyTo on the existing dom element with that id.
40950      * @return {BasicForm} this
40951      */
40952     render : function(){
40953         this.items.each(function(f){
40954             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40955                 f.applyTo(f.id);
40956             }
40957         });
40958         return this;
40959     },
40960
40961     /**
40962      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40963      * @param {Object} values
40964      * @return {BasicForm} this
40965      */
40966     applyToFields : function(o){
40967         this.items.each(function(f){
40968            Roo.apply(f, o);
40969         });
40970         return this;
40971     },
40972
40973     /**
40974      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40975      * @param {Object} values
40976      * @return {BasicForm} this
40977      */
40978     applyIfToFields : function(o){
40979         this.items.each(function(f){
40980            Roo.applyIf(f, o);
40981         });
40982         return this;
40983     }
40984 });
40985
40986 // back compat
40987 Roo.BasicForm = Roo.form.BasicForm;/*
40988  * Based on:
40989  * Ext JS Library 1.1.1
40990  * Copyright(c) 2006-2007, Ext JS, LLC.
40991  *
40992  * Originally Released Under LGPL - original licence link has changed is not relivant.
40993  *
40994  * Fork - LGPL
40995  * <script type="text/javascript">
40996  */
40997
40998 /**
40999  * @class Roo.form.Form
41000  * @extends Roo.form.BasicForm
41001  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41002  * @constructor
41003  * @param {Object} config Configuration options
41004  */
41005 Roo.form.Form = function(config){
41006     var xitems =  [];
41007     if (config.items) {
41008         xitems = config.items;
41009         delete config.items;
41010     }
41011    
41012     
41013     Roo.form.Form.superclass.constructor.call(this, null, config);
41014     this.url = this.url || this.action;
41015     if(!this.root){
41016         this.root = new Roo.form.Layout(Roo.applyIf({
41017             id: Roo.id()
41018         }, config));
41019     }
41020     this.active = this.root;
41021     /**
41022      * Array of all the buttons that have been added to this form via {@link addButton}
41023      * @type Array
41024      */
41025     this.buttons = [];
41026     this.allItems = [];
41027     this.addEvents({
41028         /**
41029          * @event clientvalidation
41030          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41031          * @param {Form} this
41032          * @param {Boolean} valid true if the form has passed client-side validation
41033          */
41034         clientvalidation: true,
41035         /**
41036          * @event rendered
41037          * Fires when the form is rendered
41038          * @param {Roo.form.Form} form
41039          */
41040         rendered : true
41041     });
41042     
41043     if (this.progressUrl) {
41044             // push a hidden field onto the list of fields..
41045             this.addxtype( {
41046                     xns: Roo.form, 
41047                     xtype : 'Hidden', 
41048                     name : 'UPLOAD_IDENTIFIER' 
41049             });
41050         }
41051         
41052     
41053     Roo.each(xitems, this.addxtype, this);
41054     
41055     
41056     
41057 };
41058
41059 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41060     /**
41061      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41062      */
41063     /**
41064      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41065      */
41066     /**
41067      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41068      */
41069     buttonAlign:'center',
41070
41071     /**
41072      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41073      */
41074     minButtonWidth:75,
41075
41076     /**
41077      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41078      * This property cascades to child containers if not set.
41079      */
41080     labelAlign:'left',
41081
41082     /**
41083      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41084      * fires a looping event with that state. This is required to bind buttons to the valid
41085      * state using the config value formBind:true on the button.
41086      */
41087     monitorValid : false,
41088
41089     /**
41090      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41091      */
41092     monitorPoll : 200,
41093     
41094     /**
41095      * @cfg {String} progressUrl - Url to return progress data 
41096      */
41097     
41098     progressUrl : false,
41099   
41100     /**
41101      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41102      * fields are added and the column is closed. If no fields are passed the column remains open
41103      * until end() is called.
41104      * @param {Object} config The config to pass to the column
41105      * @param {Field} field1 (optional)
41106      * @param {Field} field2 (optional)
41107      * @param {Field} etc (optional)
41108      * @return Column The column container object
41109      */
41110     column : function(c){
41111         var col = new Roo.form.Column(c);
41112         this.start(col);
41113         if(arguments.length > 1){ // duplicate code required because of Opera
41114             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41115             this.end();
41116         }
41117         return col;
41118     },
41119
41120     /**
41121      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41122      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41123      * until end() is called.
41124      * @param {Object} config The config to pass to the fieldset
41125      * @param {Field} field1 (optional)
41126      * @param {Field} field2 (optional)
41127      * @param {Field} etc (optional)
41128      * @return FieldSet The fieldset container object
41129      */
41130     fieldset : function(c){
41131         var fs = new Roo.form.FieldSet(c);
41132         this.start(fs);
41133         if(arguments.length > 1){ // duplicate code required because of Opera
41134             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41135             this.end();
41136         }
41137         return fs;
41138     },
41139
41140     /**
41141      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41142      * fields are added and the container is closed. If no fields are passed the container remains open
41143      * until end() is called.
41144      * @param {Object} config The config to pass to the Layout
41145      * @param {Field} field1 (optional)
41146      * @param {Field} field2 (optional)
41147      * @param {Field} etc (optional)
41148      * @return Layout The container object
41149      */
41150     container : function(c){
41151         var l = new Roo.form.Layout(c);
41152         this.start(l);
41153         if(arguments.length > 1){ // duplicate code required because of Opera
41154             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41155             this.end();
41156         }
41157         return l;
41158     },
41159
41160     /**
41161      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41162      * @param {Object} container A Roo.form.Layout or subclass of Layout
41163      * @return {Form} this
41164      */
41165     start : function(c){
41166         // cascade label info
41167         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41168         this.active.stack.push(c);
41169         c.ownerCt = this.active;
41170         this.active = c;
41171         return this;
41172     },
41173
41174     /**
41175      * Closes the current open container
41176      * @return {Form} this
41177      */
41178     end : function(){
41179         if(this.active == this.root){
41180             return this;
41181         }
41182         this.active = this.active.ownerCt;
41183         return this;
41184     },
41185
41186     /**
41187      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41188      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41189      * as the label of the field.
41190      * @param {Field} field1
41191      * @param {Field} field2 (optional)
41192      * @param {Field} etc. (optional)
41193      * @return {Form} this
41194      */
41195     add : function(){
41196         this.active.stack.push.apply(this.active.stack, arguments);
41197         this.allItems.push.apply(this.allItems,arguments);
41198         var r = [];
41199         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41200             if(a[i].isFormField){
41201                 r.push(a[i]);
41202             }
41203         }
41204         if(r.length > 0){
41205             Roo.form.Form.superclass.add.apply(this, r);
41206         }
41207         return this;
41208     },
41209     
41210
41211     
41212     
41213     
41214      /**
41215      * Find any element that has been added to a form, using it's ID or name
41216      * This can include framesets, columns etc. along with regular fields..
41217      * @param {String} id - id or name to find.
41218      
41219      * @return {Element} e - or false if nothing found.
41220      */
41221     findbyId : function(id)
41222     {
41223         var ret = false;
41224         if (!id) {
41225             return ret;
41226         }
41227         Roo.each(this.allItems, function(f){
41228             if (f.id == id || f.name == id ){
41229                 ret = f;
41230                 return false;
41231             }
41232         });
41233         return ret;
41234     },
41235
41236     
41237     
41238     /**
41239      * Render this form into the passed container. This should only be called once!
41240      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41241      * @return {Form} this
41242      */
41243     render : function(ct)
41244     {
41245         
41246         
41247         
41248         ct = Roo.get(ct);
41249         var o = this.autoCreate || {
41250             tag: 'form',
41251             method : this.method || 'POST',
41252             id : this.id || Roo.id()
41253         };
41254         this.initEl(ct.createChild(o));
41255
41256         this.root.render(this.el);
41257         
41258        
41259              
41260         this.items.each(function(f){
41261             f.render('x-form-el-'+f.id);
41262         });
41263
41264         if(this.buttons.length > 0){
41265             // tables are required to maintain order and for correct IE layout
41266             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41267                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41268                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41269             }}, null, true);
41270             var tr = tb.getElementsByTagName('tr')[0];
41271             for(var i = 0, len = this.buttons.length; i < len; i++) {
41272                 var b = this.buttons[i];
41273                 var td = document.createElement('td');
41274                 td.className = 'x-form-btn-td';
41275                 b.render(tr.appendChild(td));
41276             }
41277         }
41278         if(this.monitorValid){ // initialize after render
41279             this.startMonitoring();
41280         }
41281         this.fireEvent('rendered', this);
41282         return this;
41283     },
41284
41285     /**
41286      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41287      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41288      * object or a valid Roo.DomHelper element config
41289      * @param {Function} handler The function called when the button is clicked
41290      * @param {Object} scope (optional) The scope of the handler function
41291      * @return {Roo.Button}
41292      */
41293     addButton : function(config, handler, scope){
41294         var bc = {
41295             handler: handler,
41296             scope: scope,
41297             minWidth: this.minButtonWidth,
41298             hideParent:true
41299         };
41300         if(typeof config == "string"){
41301             bc.text = config;
41302         }else{
41303             Roo.apply(bc, config);
41304         }
41305         var btn = new Roo.Button(null, bc);
41306         this.buttons.push(btn);
41307         return btn;
41308     },
41309
41310      /**
41311      * Adds a series of form elements (using the xtype property as the factory method.
41312      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41313      * @param {Object} config 
41314      */
41315     
41316     addxtype : function()
41317     {
41318         var ar = Array.prototype.slice.call(arguments, 0);
41319         var ret = false;
41320         for(var i = 0; i < ar.length; i++) {
41321             if (!ar[i]) {
41322                 continue; // skip -- if this happends something invalid got sent, we 
41323                 // should ignore it, as basically that interface element will not show up
41324                 // and that should be pretty obvious!!
41325             }
41326             
41327             if (Roo.form[ar[i].xtype]) {
41328                 ar[i].form = this;
41329                 var fe = Roo.factory(ar[i], Roo.form);
41330                 if (!ret) {
41331                     ret = fe;
41332                 }
41333                 fe.form = this;
41334                 if (fe.store) {
41335                     fe.store.form = this;
41336                 }
41337                 if (fe.isLayout) {  
41338                          
41339                     this.start(fe);
41340                     this.allItems.push(fe);
41341                     if (fe.items && fe.addxtype) {
41342                         fe.addxtype.apply(fe, fe.items);
41343                         delete fe.items;
41344                     }
41345                      this.end();
41346                     continue;
41347                 }
41348                 
41349                 
41350                  
41351                 this.add(fe);
41352               //  console.log('adding ' + ar[i].xtype);
41353             }
41354             if (ar[i].xtype == 'Button') {  
41355                 //console.log('adding button');
41356                 //console.log(ar[i]);
41357                 this.addButton(ar[i]);
41358                 this.allItems.push(fe);
41359                 continue;
41360             }
41361             
41362             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41363                 alert('end is not supported on xtype any more, use items');
41364             //    this.end();
41365             //    //console.log('adding end');
41366             }
41367             
41368         }
41369         return ret;
41370     },
41371     
41372     /**
41373      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41374      * option "monitorValid"
41375      */
41376     startMonitoring : function(){
41377         if(!this.bound){
41378             this.bound = true;
41379             Roo.TaskMgr.start({
41380                 run : this.bindHandler,
41381                 interval : this.monitorPoll || 200,
41382                 scope: this
41383             });
41384         }
41385     },
41386
41387     /**
41388      * Stops monitoring of the valid state of this form
41389      */
41390     stopMonitoring : function(){
41391         this.bound = false;
41392     },
41393
41394     // private
41395     bindHandler : function(){
41396         if(!this.bound){
41397             return false; // stops binding
41398         }
41399         var valid = true;
41400         this.items.each(function(f){
41401             if(!f.isValid(true)){
41402                 valid = false;
41403                 return false;
41404             }
41405         });
41406         for(var i = 0, len = this.buttons.length; i < len; i++){
41407             var btn = this.buttons[i];
41408             if(btn.formBind === true && btn.disabled === valid){
41409                 btn.setDisabled(!valid);
41410             }
41411         }
41412         this.fireEvent('clientvalidation', this, valid);
41413     }
41414     
41415     
41416     
41417     
41418     
41419     
41420     
41421     
41422 });
41423
41424
41425 // back compat
41426 Roo.Form = Roo.form.Form;
41427 /*
41428  * Based on:
41429  * Ext JS Library 1.1.1
41430  * Copyright(c) 2006-2007, Ext JS, LLC.
41431  *
41432  * Originally Released Under LGPL - original licence link has changed is not relivant.
41433  *
41434  * Fork - LGPL
41435  * <script type="text/javascript">
41436  */
41437  
41438  /**
41439  * @class Roo.form.Action
41440  * Internal Class used to handle form actions
41441  * @constructor
41442  * @param {Roo.form.BasicForm} el The form element or its id
41443  * @param {Object} config Configuration options
41444  */
41445  
41446  
41447 // define the action interface
41448 Roo.form.Action = function(form, options){
41449     this.form = form;
41450     this.options = options || {};
41451 };
41452 /**
41453  * Client Validation Failed
41454  * @const 
41455  */
41456 Roo.form.Action.CLIENT_INVALID = 'client';
41457 /**
41458  * Server Validation Failed
41459  * @const 
41460  */
41461  Roo.form.Action.SERVER_INVALID = 'server';
41462  /**
41463  * Connect to Server Failed
41464  * @const 
41465  */
41466 Roo.form.Action.CONNECT_FAILURE = 'connect';
41467 /**
41468  * Reading Data from Server Failed
41469  * @const 
41470  */
41471 Roo.form.Action.LOAD_FAILURE = 'load';
41472
41473 Roo.form.Action.prototype = {
41474     type : 'default',
41475     failureType : undefined,
41476     response : undefined,
41477     result : undefined,
41478
41479     // interface method
41480     run : function(options){
41481
41482     },
41483
41484     // interface method
41485     success : function(response){
41486
41487     },
41488
41489     // interface method
41490     handleResponse : function(response){
41491
41492     },
41493
41494     // default connection failure
41495     failure : function(response){
41496         
41497         this.response = response;
41498         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41499         this.form.afterAction(this, false);
41500     },
41501
41502     processResponse : function(response){
41503         this.response = response;
41504         if(!response.responseText){
41505             return true;
41506         }
41507         this.result = this.handleResponse(response);
41508         return this.result;
41509     },
41510
41511     // utility functions used internally
41512     getUrl : function(appendParams){
41513         var url = this.options.url || this.form.url || this.form.el.dom.action;
41514         if(appendParams){
41515             var p = this.getParams();
41516             if(p){
41517                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41518             }
41519         }
41520         return url;
41521     },
41522
41523     getMethod : function(){
41524         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41525     },
41526
41527     getParams : function(){
41528         var bp = this.form.baseParams;
41529         var p = this.options.params;
41530         if(p){
41531             if(typeof p == "object"){
41532                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41533             }else if(typeof p == 'string' && bp){
41534                 p += '&' + Roo.urlEncode(bp);
41535             }
41536         }else if(bp){
41537             p = Roo.urlEncode(bp);
41538         }
41539         return p;
41540     },
41541
41542     createCallback : function(){
41543         return {
41544             success: this.success,
41545             failure: this.failure,
41546             scope: this,
41547             timeout: (this.form.timeout*1000),
41548             upload: this.form.fileUpload ? this.success : undefined
41549         };
41550     }
41551 };
41552
41553 Roo.form.Action.Submit = function(form, options){
41554     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41555 };
41556
41557 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41558     type : 'submit',
41559
41560     haveProgress : false,
41561     uploadComplete : false,
41562     
41563     // uploadProgress indicator.
41564     uploadProgress : function()
41565     {
41566         if (!this.form.progressUrl) {
41567             return;
41568         }
41569         
41570         if (!this.haveProgress) {
41571             Roo.MessageBox.progress("Uploading", "Uploading");
41572         }
41573         if (this.uploadComplete) {
41574            Roo.MessageBox.hide();
41575            return;
41576         }
41577         
41578         this.haveProgress = true;
41579    
41580         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41581         
41582         var c = new Roo.data.Connection();
41583         c.request({
41584             url : this.form.progressUrl,
41585             params: {
41586                 id : uid
41587             },
41588             method: 'GET',
41589             success : function(req){
41590                //console.log(data);
41591                 var rdata = false;
41592                 var edata;
41593                 try  {
41594                    rdata = Roo.decode(req.responseText)
41595                 } catch (e) {
41596                     Roo.log("Invalid data from server..");
41597                     Roo.log(edata);
41598                     return;
41599                 }
41600                 if (!rdata || !rdata.success) {
41601                     Roo.log(rdata);
41602                     return;
41603                 }
41604                 var data = rdata.data;
41605                 
41606                 if (this.uploadComplete) {
41607                    Roo.MessageBox.hide();
41608                    return;
41609                 }
41610                    
41611                 if (data){
41612                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41613                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41614                     );
41615                 }
41616                 this.uploadProgress.defer(2000,this);
41617             },
41618        
41619             failure: function(data) {
41620                 Roo.log('progress url failed ');
41621                 Roo.log(data);
41622             },
41623             scope : this
41624         });
41625            
41626     },
41627     
41628     
41629     run : function()
41630     {
41631         // run get Values on the form, so it syncs any secondary forms.
41632         this.form.getValues();
41633         
41634         var o = this.options;
41635         var method = this.getMethod();
41636         var isPost = method == 'POST';
41637         if(o.clientValidation === false || this.form.isValid()){
41638             
41639             if (this.form.progressUrl) {
41640                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41641                     (new Date() * 1) + '' + Math.random());
41642                     
41643             } 
41644             
41645             
41646             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41647                 form:this.form.el.dom,
41648                 url:this.getUrl(!isPost),
41649                 method: method,
41650                 params:isPost ? this.getParams() : null,
41651                 isUpload: this.form.fileUpload
41652             }));
41653             
41654             this.uploadProgress();
41655
41656         }else if (o.clientValidation !== false){ // client validation failed
41657             this.failureType = Roo.form.Action.CLIENT_INVALID;
41658             this.form.afterAction(this, false);
41659         }
41660     },
41661
41662     success : function(response)
41663     {
41664         this.uploadComplete= true;
41665         if (this.haveProgress) {
41666             Roo.MessageBox.hide();
41667         }
41668         
41669         
41670         var result = this.processResponse(response);
41671         if(result === true || result.success){
41672             this.form.afterAction(this, true);
41673             return;
41674         }
41675         if(result.errors){
41676             this.form.markInvalid(result.errors);
41677             this.failureType = Roo.form.Action.SERVER_INVALID;
41678         }
41679         this.form.afterAction(this, false);
41680     },
41681     failure : function(response)
41682     {
41683         this.uploadComplete= true;
41684         if (this.haveProgress) {
41685             Roo.MessageBox.hide();
41686         }
41687         
41688         
41689         this.response = response;
41690         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41691         this.form.afterAction(this, false);
41692     },
41693     
41694     handleResponse : function(response){
41695         if(this.form.errorReader){
41696             var rs = this.form.errorReader.read(response);
41697             var errors = [];
41698             if(rs.records){
41699                 for(var i = 0, len = rs.records.length; i < len; i++) {
41700                     var r = rs.records[i];
41701                     errors[i] = r.data;
41702                 }
41703             }
41704             if(errors.length < 1){
41705                 errors = null;
41706             }
41707             return {
41708                 success : rs.success,
41709                 errors : errors
41710             };
41711         }
41712         var ret = false;
41713         try {
41714             ret = Roo.decode(response.responseText);
41715         } catch (e) {
41716             ret = {
41717                 success: false,
41718                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41719                 errors : []
41720             };
41721         }
41722         return ret;
41723         
41724     }
41725 });
41726
41727
41728 Roo.form.Action.Load = function(form, options){
41729     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41730     this.reader = this.form.reader;
41731 };
41732
41733 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41734     type : 'load',
41735
41736     run : function(){
41737         
41738         Roo.Ajax.request(Roo.apply(
41739                 this.createCallback(), {
41740                     method:this.getMethod(),
41741                     url:this.getUrl(false),
41742                     params:this.getParams()
41743         }));
41744     },
41745
41746     success : function(response){
41747         
41748         var result = this.processResponse(response);
41749         if(result === true || !result.success || !result.data){
41750             this.failureType = Roo.form.Action.LOAD_FAILURE;
41751             this.form.afterAction(this, false);
41752             return;
41753         }
41754         this.form.clearInvalid();
41755         this.form.setValues(result.data);
41756         this.form.afterAction(this, true);
41757     },
41758
41759     handleResponse : function(response){
41760         if(this.form.reader){
41761             var rs = this.form.reader.read(response);
41762             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41763             return {
41764                 success : rs.success,
41765                 data : data
41766             };
41767         }
41768         return Roo.decode(response.responseText);
41769     }
41770 });
41771
41772 Roo.form.Action.ACTION_TYPES = {
41773     'load' : Roo.form.Action.Load,
41774     'submit' : Roo.form.Action.Submit
41775 };/*
41776  * Based on:
41777  * Ext JS Library 1.1.1
41778  * Copyright(c) 2006-2007, Ext JS, LLC.
41779  *
41780  * Originally Released Under LGPL - original licence link has changed is not relivant.
41781  *
41782  * Fork - LGPL
41783  * <script type="text/javascript">
41784  */
41785  
41786 /**
41787  * @class Roo.form.Layout
41788  * @extends Roo.Component
41789  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41790  * @constructor
41791  * @param {Object} config Configuration options
41792  */
41793 Roo.form.Layout = function(config){
41794     var xitems = [];
41795     if (config.items) {
41796         xitems = config.items;
41797         delete config.items;
41798     }
41799     Roo.form.Layout.superclass.constructor.call(this, config);
41800     this.stack = [];
41801     Roo.each(xitems, this.addxtype, this);
41802      
41803 };
41804
41805 Roo.extend(Roo.form.Layout, Roo.Component, {
41806     /**
41807      * @cfg {String/Object} autoCreate
41808      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41809      */
41810     /**
41811      * @cfg {String/Object/Function} style
41812      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41813      * a function which returns such a specification.
41814      */
41815     /**
41816      * @cfg {String} labelAlign
41817      * Valid values are "left," "top" and "right" (defaults to "left")
41818      */
41819     /**
41820      * @cfg {Number} labelWidth
41821      * Fixed width in pixels of all field labels (defaults to undefined)
41822      */
41823     /**
41824      * @cfg {Boolean} clear
41825      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41826      */
41827     clear : true,
41828     /**
41829      * @cfg {String} labelSeparator
41830      * The separator to use after field labels (defaults to ':')
41831      */
41832     labelSeparator : ':',
41833     /**
41834      * @cfg {Boolean} hideLabels
41835      * True to suppress the display of field labels in this layout (defaults to false)
41836      */
41837     hideLabels : false,
41838
41839     // private
41840     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41841     
41842     isLayout : true,
41843     
41844     // private
41845     onRender : function(ct, position){
41846         if(this.el){ // from markup
41847             this.el = Roo.get(this.el);
41848         }else {  // generate
41849             var cfg = this.getAutoCreate();
41850             this.el = ct.createChild(cfg, position);
41851         }
41852         if(this.style){
41853             this.el.applyStyles(this.style);
41854         }
41855         if(this.labelAlign){
41856             this.el.addClass('x-form-label-'+this.labelAlign);
41857         }
41858         if(this.hideLabels){
41859             this.labelStyle = "display:none";
41860             this.elementStyle = "padding-left:0;";
41861         }else{
41862             if(typeof this.labelWidth == 'number'){
41863                 this.labelStyle = "width:"+this.labelWidth+"px;";
41864                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41865             }
41866             if(this.labelAlign == 'top'){
41867                 this.labelStyle = "width:auto;";
41868                 this.elementStyle = "padding-left:0;";
41869             }
41870         }
41871         var stack = this.stack;
41872         var slen = stack.length;
41873         if(slen > 0){
41874             if(!this.fieldTpl){
41875                 var t = new Roo.Template(
41876                     '<div class="x-form-item {5}">',
41877                         '<label for="{0}" style="{2}">{1}{4}</label>',
41878                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41879                         '</div>',
41880                     '</div><div class="x-form-clear-left"></div>'
41881                 );
41882                 t.disableFormats = true;
41883                 t.compile();
41884                 Roo.form.Layout.prototype.fieldTpl = t;
41885             }
41886             for(var i = 0; i < slen; i++) {
41887                 if(stack[i].isFormField){
41888                     this.renderField(stack[i]);
41889                 }else{
41890                     this.renderComponent(stack[i]);
41891                 }
41892             }
41893         }
41894         if(this.clear){
41895             this.el.createChild({cls:'x-form-clear'});
41896         }
41897     },
41898
41899     // private
41900     renderField : function(f){
41901         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41902                f.id, //0
41903                f.fieldLabel, //1
41904                f.labelStyle||this.labelStyle||'', //2
41905                this.elementStyle||'', //3
41906                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41907                f.itemCls||this.itemCls||''  //5
41908        ], true).getPrevSibling());
41909     },
41910
41911     // private
41912     renderComponent : function(c){
41913         c.render(c.isLayout ? this.el : this.el.createChild());    
41914     },
41915     /**
41916      * Adds a object form elements (using the xtype property as the factory method.)
41917      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41918      * @param {Object} config 
41919      */
41920     addxtype : function(o)
41921     {
41922         // create the lement.
41923         o.form = this.form;
41924         var fe = Roo.factory(o, Roo.form);
41925         this.form.allItems.push(fe);
41926         this.stack.push(fe);
41927         
41928         if (fe.isFormField) {
41929             this.form.items.add(fe);
41930         }
41931          
41932         return fe;
41933     }
41934 });
41935
41936 /**
41937  * @class Roo.form.Column
41938  * @extends Roo.form.Layout
41939  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41940  * @constructor
41941  * @param {Object} config Configuration options
41942  */
41943 Roo.form.Column = function(config){
41944     Roo.form.Column.superclass.constructor.call(this, config);
41945 };
41946
41947 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41948     /**
41949      * @cfg {Number/String} width
41950      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41951      */
41952     /**
41953      * @cfg {String/Object} autoCreate
41954      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41955      */
41956
41957     // private
41958     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41959
41960     // private
41961     onRender : function(ct, position){
41962         Roo.form.Column.superclass.onRender.call(this, ct, position);
41963         if(this.width){
41964             this.el.setWidth(this.width);
41965         }
41966     }
41967 });
41968
41969
41970 /**
41971  * @class Roo.form.Row
41972  * @extends Roo.form.Layout
41973  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41974  * @constructor
41975  * @param {Object} config Configuration options
41976  */
41977
41978  
41979 Roo.form.Row = function(config){
41980     Roo.form.Row.superclass.constructor.call(this, config);
41981 };
41982  
41983 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41984       /**
41985      * @cfg {Number/String} width
41986      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41987      */
41988     /**
41989      * @cfg {Number/String} height
41990      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41991      */
41992     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41993     
41994     padWidth : 20,
41995     // private
41996     onRender : function(ct, position){
41997         //console.log('row render');
41998         if(!this.rowTpl){
41999             var t = new Roo.Template(
42000                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42001                     '<label for="{0}" style="{2}">{1}{4}</label>',
42002                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42003                     '</div>',
42004                 '</div>'
42005             );
42006             t.disableFormats = true;
42007             t.compile();
42008             Roo.form.Layout.prototype.rowTpl = t;
42009         }
42010         this.fieldTpl = this.rowTpl;
42011         
42012         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42013         var labelWidth = 100;
42014         
42015         if ((this.labelAlign != 'top')) {
42016             if (typeof this.labelWidth == 'number') {
42017                 labelWidth = this.labelWidth
42018             }
42019             this.padWidth =  20 + labelWidth;
42020             
42021         }
42022         
42023         Roo.form.Column.superclass.onRender.call(this, ct, position);
42024         if(this.width){
42025             this.el.setWidth(this.width);
42026         }
42027         if(this.height){
42028             this.el.setHeight(this.height);
42029         }
42030     },
42031     
42032     // private
42033     renderField : function(f){
42034         f.fieldEl = this.fieldTpl.append(this.el, [
42035                f.id, f.fieldLabel,
42036                f.labelStyle||this.labelStyle||'',
42037                this.elementStyle||'',
42038                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42039                f.itemCls||this.itemCls||'',
42040                f.width ? f.width + this.padWidth : 160 + this.padWidth
42041        ],true);
42042     }
42043 });
42044  
42045
42046 /**
42047  * @class Roo.form.FieldSet
42048  * @extends Roo.form.Layout
42049  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42050  * @constructor
42051  * @param {Object} config Configuration options
42052  */
42053 Roo.form.FieldSet = function(config){
42054     Roo.form.FieldSet.superclass.constructor.call(this, config);
42055 };
42056
42057 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42058     /**
42059      * @cfg {String} legend
42060      * The text to display as the legend for the FieldSet (defaults to '')
42061      */
42062     /**
42063      * @cfg {String/Object} autoCreate
42064      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42065      */
42066
42067     // private
42068     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42069
42070     // private
42071     onRender : function(ct, position){
42072         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42073         if(this.legend){
42074             this.setLegend(this.legend);
42075         }
42076     },
42077
42078     // private
42079     setLegend : function(text){
42080         if(this.rendered){
42081             this.el.child('legend').update(text);
42082         }
42083     }
42084 });/*
42085  * Based on:
42086  * Ext JS Library 1.1.1
42087  * Copyright(c) 2006-2007, Ext JS, LLC.
42088  *
42089  * Originally Released Under LGPL - original licence link has changed is not relivant.
42090  *
42091  * Fork - LGPL
42092  * <script type="text/javascript">
42093  */
42094 /**
42095  * @class Roo.form.VTypes
42096  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42097  * @singleton
42098  */
42099 Roo.form.VTypes = function(){
42100     // closure these in so they are only created once.
42101     var alpha = /^[a-zA-Z_]+$/;
42102     var alphanum = /^[a-zA-Z0-9_]+$/;
42103     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42104     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42105
42106     // All these messages and functions are configurable
42107     return {
42108         /**
42109          * The function used to validate email addresses
42110          * @param {String} value The email address
42111          */
42112         'email' : function(v){
42113             return email.test(v);
42114         },
42115         /**
42116          * The error text to display when the email validation function returns false
42117          * @type String
42118          */
42119         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42120         /**
42121          * The keystroke filter mask to be applied on email input
42122          * @type RegExp
42123          */
42124         'emailMask' : /[a-z0-9_\.\-@]/i,
42125
42126         /**
42127          * The function used to validate URLs
42128          * @param {String} value The URL
42129          */
42130         'url' : function(v){
42131             return url.test(v);
42132         },
42133         /**
42134          * The error text to display when the url validation function returns false
42135          * @type String
42136          */
42137         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42138         
42139         /**
42140          * The function used to validate alpha values
42141          * @param {String} value The value
42142          */
42143         'alpha' : function(v){
42144             return alpha.test(v);
42145         },
42146         /**
42147          * The error text to display when the alpha validation function returns false
42148          * @type String
42149          */
42150         'alphaText' : 'This field should only contain letters and _',
42151         /**
42152          * The keystroke filter mask to be applied on alpha input
42153          * @type RegExp
42154          */
42155         'alphaMask' : /[a-z_]/i,
42156
42157         /**
42158          * The function used to validate alphanumeric values
42159          * @param {String} value The value
42160          */
42161         'alphanum' : function(v){
42162             return alphanum.test(v);
42163         },
42164         /**
42165          * The error text to display when the alphanumeric validation function returns false
42166          * @type String
42167          */
42168         'alphanumText' : 'This field should only contain letters, numbers and _',
42169         /**
42170          * The keystroke filter mask to be applied on alphanumeric input
42171          * @type RegExp
42172          */
42173         'alphanumMask' : /[a-z0-9_]/i
42174     };
42175 }();//<script type="text/javascript">
42176
42177 /**
42178  * @class Roo.form.FCKeditor
42179  * @extends Roo.form.TextArea
42180  * Wrapper around the FCKEditor http://www.fckeditor.net
42181  * @constructor
42182  * Creates a new FCKeditor
42183  * @param {Object} config Configuration options
42184  */
42185 Roo.form.FCKeditor = function(config){
42186     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42187     this.addEvents({
42188          /**
42189          * @event editorinit
42190          * Fired when the editor is initialized - you can add extra handlers here..
42191          * @param {FCKeditor} this
42192          * @param {Object} the FCK object.
42193          */
42194         editorinit : true
42195     });
42196     
42197     
42198 };
42199 Roo.form.FCKeditor.editors = { };
42200 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42201 {
42202     //defaultAutoCreate : {
42203     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42204     //},
42205     // private
42206     /**
42207      * @cfg {Object} fck options - see fck manual for details.
42208      */
42209     fckconfig : false,
42210     
42211     /**
42212      * @cfg {Object} fck toolbar set (Basic or Default)
42213      */
42214     toolbarSet : 'Basic',
42215     /**
42216      * @cfg {Object} fck BasePath
42217      */ 
42218     basePath : '/fckeditor/',
42219     
42220     
42221     frame : false,
42222     
42223     value : '',
42224     
42225    
42226     onRender : function(ct, position)
42227     {
42228         if(!this.el){
42229             this.defaultAutoCreate = {
42230                 tag: "textarea",
42231                 style:"width:300px;height:60px;",
42232                 autocomplete: "off"
42233             };
42234         }
42235         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42236         /*
42237         if(this.grow){
42238             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42239             if(this.preventScrollbars){
42240                 this.el.setStyle("overflow", "hidden");
42241             }
42242             this.el.setHeight(this.growMin);
42243         }
42244         */
42245         //console.log('onrender' + this.getId() );
42246         Roo.form.FCKeditor.editors[this.getId()] = this;
42247          
42248
42249         this.replaceTextarea() ;
42250         
42251     },
42252     
42253     getEditor : function() {
42254         return this.fckEditor;
42255     },
42256     /**
42257      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42258      * @param {Mixed} value The value to set
42259      */
42260     
42261     
42262     setValue : function(value)
42263     {
42264         //console.log('setValue: ' + value);
42265         
42266         if(typeof(value) == 'undefined') { // not sure why this is happending...
42267             return;
42268         }
42269         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42270         
42271         //if(!this.el || !this.getEditor()) {
42272         //    this.value = value;
42273             //this.setValue.defer(100,this,[value]);    
42274         //    return;
42275         //} 
42276         
42277         if(!this.getEditor()) {
42278             return;
42279         }
42280         
42281         this.getEditor().SetData(value);
42282         
42283         //
42284
42285     },
42286
42287     /**
42288      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42289      * @return {Mixed} value The field value
42290      */
42291     getValue : function()
42292     {
42293         
42294         if (this.frame && this.frame.dom.style.display == 'none') {
42295             return Roo.form.FCKeditor.superclass.getValue.call(this);
42296         }
42297         
42298         if(!this.el || !this.getEditor()) {
42299            
42300            // this.getValue.defer(100,this); 
42301             return this.value;
42302         }
42303        
42304         
42305         var value=this.getEditor().GetData();
42306         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42307         return Roo.form.FCKeditor.superclass.getValue.call(this);
42308         
42309
42310     },
42311
42312     /**
42313      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42314      * @return {Mixed} value The field value
42315      */
42316     getRawValue : function()
42317     {
42318         if (this.frame && this.frame.dom.style.display == 'none') {
42319             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42320         }
42321         
42322         if(!this.el || !this.getEditor()) {
42323             //this.getRawValue.defer(100,this); 
42324             return this.value;
42325             return;
42326         }
42327         
42328         
42329         
42330         var value=this.getEditor().GetData();
42331         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42332         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42333          
42334     },
42335     
42336     setSize : function(w,h) {
42337         
42338         
42339         
42340         //if (this.frame && this.frame.dom.style.display == 'none') {
42341         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42342         //    return;
42343         //}
42344         //if(!this.el || !this.getEditor()) {
42345         //    this.setSize.defer(100,this, [w,h]); 
42346         //    return;
42347         //}
42348         
42349         
42350         
42351         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42352         
42353         this.frame.dom.setAttribute('width', w);
42354         this.frame.dom.setAttribute('height', h);
42355         this.frame.setSize(w,h);
42356         
42357     },
42358     
42359     toggleSourceEdit : function(value) {
42360         
42361       
42362          
42363         this.el.dom.style.display = value ? '' : 'none';
42364         this.frame.dom.style.display = value ?  'none' : '';
42365         
42366     },
42367     
42368     
42369     focus: function(tag)
42370     {
42371         if (this.frame.dom.style.display == 'none') {
42372             return Roo.form.FCKeditor.superclass.focus.call(this);
42373         }
42374         if(!this.el || !this.getEditor()) {
42375             this.focus.defer(100,this, [tag]); 
42376             return;
42377         }
42378         
42379         
42380         
42381         
42382         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42383         this.getEditor().Focus();
42384         if (tgs.length) {
42385             if (!this.getEditor().Selection.GetSelection()) {
42386                 this.focus.defer(100,this, [tag]); 
42387                 return;
42388             }
42389             
42390             
42391             var r = this.getEditor().EditorDocument.createRange();
42392             r.setStart(tgs[0],0);
42393             r.setEnd(tgs[0],0);
42394             this.getEditor().Selection.GetSelection().removeAllRanges();
42395             this.getEditor().Selection.GetSelection().addRange(r);
42396             this.getEditor().Focus();
42397         }
42398         
42399     },
42400     
42401     
42402     
42403     replaceTextarea : function()
42404     {
42405         if ( document.getElementById( this.getId() + '___Frame' ) )
42406             return ;
42407         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42408         //{
42409             // We must check the elements firstly using the Id and then the name.
42410         var oTextarea = document.getElementById( this.getId() );
42411         
42412         var colElementsByName = document.getElementsByName( this.getId() ) ;
42413          
42414         oTextarea.style.display = 'none' ;
42415
42416         if ( oTextarea.tabIndex ) {            
42417             this.TabIndex = oTextarea.tabIndex ;
42418         }
42419         
42420         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42421         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42422         this.frame = Roo.get(this.getId() + '___Frame')
42423     },
42424     
42425     _getConfigHtml : function()
42426     {
42427         var sConfig = '' ;
42428
42429         for ( var o in this.fckconfig ) {
42430             sConfig += sConfig.length > 0  ? '&amp;' : '';
42431             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42432         }
42433
42434         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42435     },
42436     
42437     
42438     _getIFrameHtml : function()
42439     {
42440         var sFile = 'fckeditor.html' ;
42441         /* no idea what this is about..
42442         try
42443         {
42444             if ( (/fcksource=true/i).test( window.top.location.search ) )
42445                 sFile = 'fckeditor.original.html' ;
42446         }
42447         catch (e) { 
42448         */
42449
42450         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42451         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42452         
42453         
42454         var html = '<iframe id="' + this.getId() +
42455             '___Frame" src="' + sLink +
42456             '" width="' + this.width +
42457             '" height="' + this.height + '"' +
42458             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42459             ' frameborder="0" scrolling="no"></iframe>' ;
42460
42461         return html ;
42462     },
42463     
42464     _insertHtmlBefore : function( html, element )
42465     {
42466         if ( element.insertAdjacentHTML )       {
42467             // IE
42468             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42469         } else { // Gecko
42470             var oRange = document.createRange() ;
42471             oRange.setStartBefore( element ) ;
42472             var oFragment = oRange.createContextualFragment( html );
42473             element.parentNode.insertBefore( oFragment, element ) ;
42474         }
42475     }
42476     
42477     
42478   
42479     
42480     
42481     
42482     
42483
42484 });
42485
42486 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42487
42488 function FCKeditor_OnComplete(editorInstance){
42489     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42490     f.fckEditor = editorInstance;
42491     //console.log("loaded");
42492     f.fireEvent('editorinit', f, editorInstance);
42493
42494   
42495
42496  
42497
42498
42499
42500
42501
42502
42503
42504
42505
42506
42507
42508
42509
42510
42511
42512 //<script type="text/javascript">
42513 /**
42514  * @class Roo.form.GridField
42515  * @extends Roo.form.Field
42516  * Embed a grid (or editable grid into a form)
42517  * STATUS ALPHA
42518  * 
42519  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42520  * it needs 
42521  * xgrid.store = Roo.data.Store
42522  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42523  * xgrid.store.reader = Roo.data.JsonReader 
42524  * 
42525  * 
42526  * @constructor
42527  * Creates a new GridField
42528  * @param {Object} config Configuration options
42529  */
42530 Roo.form.GridField = function(config){
42531     Roo.form.GridField.superclass.constructor.call(this, config);
42532      
42533 };
42534
42535 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42536     /**
42537      * @cfg {Number} width  - used to restrict width of grid..
42538      */
42539     width : 100,
42540     /**
42541      * @cfg {Number} height - used to restrict height of grid..
42542      */
42543     height : 50,
42544      /**
42545      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42546          * 
42547          *}
42548      */
42549     xgrid : false, 
42550     /**
42551      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42552      * {tag: "input", type: "checkbox", autocomplete: "off"})
42553      */
42554    // defaultAutoCreate : { tag: 'div' },
42555     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42556     /**
42557      * @cfg {String} addTitle Text to include for adding a title.
42558      */
42559     addTitle : false,
42560     //
42561     onResize : function(){
42562         Roo.form.Field.superclass.onResize.apply(this, arguments);
42563     },
42564
42565     initEvents : function(){
42566         // Roo.form.Checkbox.superclass.initEvents.call(this);
42567         // has no events...
42568        
42569     },
42570
42571
42572     getResizeEl : function(){
42573         return this.wrap;
42574     },
42575
42576     getPositionEl : function(){
42577         return this.wrap;
42578     },
42579
42580     // private
42581     onRender : function(ct, position){
42582         
42583         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42584         var style = this.style;
42585         delete this.style;
42586         
42587         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42588         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42589         this.viewEl = this.wrap.createChild({ tag: 'div' });
42590         if (style) {
42591             this.viewEl.applyStyles(style);
42592         }
42593         if (this.width) {
42594             this.viewEl.setWidth(this.width);
42595         }
42596         if (this.height) {
42597             this.viewEl.setHeight(this.height);
42598         }
42599         //if(this.inputValue !== undefined){
42600         //this.setValue(this.value);
42601         
42602         
42603         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42604         
42605         
42606         this.grid.render();
42607         this.grid.getDataSource().on('remove', this.refreshValue, this);
42608         this.grid.getDataSource().on('update', this.refreshValue, this);
42609         this.grid.on('afteredit', this.refreshValue, this);
42610  
42611     },
42612      
42613     
42614     /**
42615      * Sets the value of the item. 
42616      * @param {String} either an object  or a string..
42617      */
42618     setValue : function(v){
42619         //this.value = v;
42620         v = v || []; // empty set..
42621         // this does not seem smart - it really only affects memoryproxy grids..
42622         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42623             var ds = this.grid.getDataSource();
42624             // assumes a json reader..
42625             var data = {}
42626             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42627             ds.loadData( data);
42628         }
42629         Roo.form.GridField.superclass.setValue.call(this, v);
42630         this.refreshValue();
42631         // should load data in the grid really....
42632     },
42633     
42634     // private
42635     refreshValue: function() {
42636          var val = [];
42637         this.grid.getDataSource().each(function(r) {
42638             val.push(r.data);
42639         });
42640         this.el.dom.value = Roo.encode(val);
42641     }
42642     
42643      
42644     
42645     
42646 });/*
42647  * Based on:
42648  * Ext JS Library 1.1.1
42649  * Copyright(c) 2006-2007, Ext JS, LLC.
42650  *
42651  * Originally Released Under LGPL - original licence link has changed is not relivant.
42652  *
42653  * Fork - LGPL
42654  * <script type="text/javascript">
42655  */
42656 /**
42657  * @class Roo.form.DisplayField
42658  * @extends Roo.form.Field
42659  * A generic Field to display non-editable data.
42660  * @constructor
42661  * Creates a new Display Field item.
42662  * @param {Object} config Configuration options
42663  */
42664 Roo.form.DisplayField = function(config){
42665     Roo.form.DisplayField.superclass.constructor.call(this, config);
42666     
42667 };
42668
42669 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42670     inputType:      'hidden',
42671     allowBlank:     true,
42672     readOnly:         true,
42673     
42674  
42675     /**
42676      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42677      */
42678     focusClass : undefined,
42679     /**
42680      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42681      */
42682     fieldClass: 'x-form-field',
42683     
42684      /**
42685      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42686      */
42687     valueRenderer: undefined,
42688     
42689     width: 100,
42690     /**
42691      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42692      * {tag: "input", type: "checkbox", autocomplete: "off"})
42693      */
42694      
42695  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42696
42697     onResize : function(){
42698         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42699         
42700     },
42701
42702     initEvents : function(){
42703         // Roo.form.Checkbox.superclass.initEvents.call(this);
42704         // has no events...
42705        
42706     },
42707
42708
42709     getResizeEl : function(){
42710         return this.wrap;
42711     },
42712
42713     getPositionEl : function(){
42714         return this.wrap;
42715     },
42716
42717     // private
42718     onRender : function(ct, position){
42719         
42720         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42721         //if(this.inputValue !== undefined){
42722         this.wrap = this.el.wrap();
42723         
42724         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42725         
42726         if (this.bodyStyle) {
42727             this.viewEl.applyStyles(this.bodyStyle);
42728         }
42729         //this.viewEl.setStyle('padding', '2px');
42730         
42731         this.setValue(this.value);
42732         
42733     },
42734 /*
42735     // private
42736     initValue : Roo.emptyFn,
42737
42738   */
42739
42740         // private
42741     onClick : function(){
42742         
42743     },
42744
42745     /**
42746      * Sets the checked state of the checkbox.
42747      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42748      */
42749     setValue : function(v){
42750         this.value = v;
42751         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42752         // this might be called before we have a dom element..
42753         if (!this.viewEl) {
42754             return;
42755         }
42756         this.viewEl.dom.innerHTML = html;
42757         Roo.form.DisplayField.superclass.setValue.call(this, v);
42758
42759     }
42760 });/*
42761  * 
42762  * Licence- LGPL
42763  * 
42764  */
42765
42766 /**
42767  * @class Roo.form.DayPicker
42768  * @extends Roo.form.Field
42769  * A Day picker show [M] [T] [W] ....
42770  * @constructor
42771  * Creates a new Day Picker
42772  * @param {Object} config Configuration options
42773  */
42774 Roo.form.DayPicker= function(config){
42775     Roo.form.DayPicker.superclass.constructor.call(this, config);
42776      
42777 };
42778
42779 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42780     /**
42781      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42782      */
42783     focusClass : undefined,
42784     /**
42785      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42786      */
42787     fieldClass: "x-form-field",
42788    
42789     /**
42790      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42791      * {tag: "input", type: "checkbox", autocomplete: "off"})
42792      */
42793     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42794     
42795    
42796     actionMode : 'viewEl', 
42797     //
42798     // private
42799  
42800     inputType : 'hidden',
42801     
42802      
42803     inputElement: false, // real input element?
42804     basedOn: false, // ????
42805     
42806     isFormField: true, // not sure where this is needed!!!!
42807
42808     onResize : function(){
42809         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42810         if(!this.boxLabel){
42811             this.el.alignTo(this.wrap, 'c-c');
42812         }
42813     },
42814
42815     initEvents : function(){
42816         Roo.form.Checkbox.superclass.initEvents.call(this);
42817         this.el.on("click", this.onClick,  this);
42818         this.el.on("change", this.onClick,  this);
42819     },
42820
42821
42822     getResizeEl : function(){
42823         return this.wrap;
42824     },
42825
42826     getPositionEl : function(){
42827         return this.wrap;
42828     },
42829
42830     
42831     // private
42832     onRender : function(ct, position){
42833         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42834        
42835         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42836         
42837         var r1 = '<table><tr>';
42838         var r2 = '<tr class="x-form-daypick-icons">';
42839         for (var i=0; i < 7; i++) {
42840             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42841             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42842         }
42843         
42844         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42845         viewEl.select('img').on('click', this.onClick, this);
42846         this.viewEl = viewEl;   
42847         
42848         
42849         // this will not work on Chrome!!!
42850         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42851         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42852         
42853         
42854           
42855
42856     },
42857
42858     // private
42859     initValue : Roo.emptyFn,
42860
42861     /**
42862      * Returns the checked state of the checkbox.
42863      * @return {Boolean} True if checked, else false
42864      */
42865     getValue : function(){
42866         return this.el.dom.value;
42867         
42868     },
42869
42870         // private
42871     onClick : function(e){ 
42872         //this.setChecked(!this.checked);
42873         Roo.get(e.target).toggleClass('x-menu-item-checked');
42874         this.refreshValue();
42875         //if(this.el.dom.checked != this.checked){
42876         //    this.setValue(this.el.dom.checked);
42877        // }
42878     },
42879     
42880     // private
42881     refreshValue : function()
42882     {
42883         var val = '';
42884         this.viewEl.select('img',true).each(function(e,i,n)  {
42885             val += e.is(".x-menu-item-checked") ? String(n) : '';
42886         });
42887         this.setValue(val, true);
42888     },
42889
42890     /**
42891      * Sets the checked state of the checkbox.
42892      * On is always based on a string comparison between inputValue and the param.
42893      * @param {Boolean/String} value - the value to set 
42894      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42895      */
42896     setValue : function(v,suppressEvent){
42897         if (!this.el.dom) {
42898             return;
42899         }
42900         var old = this.el.dom.value ;
42901         this.el.dom.value = v;
42902         if (suppressEvent) {
42903             return ;
42904         }
42905          
42906         // update display..
42907         this.viewEl.select('img',true).each(function(e,i,n)  {
42908             
42909             var on = e.is(".x-menu-item-checked");
42910             var newv = v.indexOf(String(n)) > -1;
42911             if (on != newv) {
42912                 e.toggleClass('x-menu-item-checked');
42913             }
42914             
42915         });
42916         
42917         
42918         this.fireEvent('change', this, v, old);
42919         
42920         
42921     },
42922    
42923     // handle setting of hidden value by some other method!!?!?
42924     setFromHidden: function()
42925     {
42926         if(!this.el){
42927             return;
42928         }
42929         //console.log("SET FROM HIDDEN");
42930         //alert('setFrom hidden');
42931         this.setValue(this.el.dom.value);
42932     },
42933     
42934     onDestroy : function()
42935     {
42936         if(this.viewEl){
42937             Roo.get(this.viewEl).remove();
42938         }
42939          
42940         Roo.form.DayPicker.superclass.onDestroy.call(this);
42941     }
42942
42943 });//<script type="text/javasscript">
42944  
42945
42946 /**
42947  * @class Roo.DDView
42948  * A DnD enabled version of Roo.View.
42949  * @param {Element/String} container The Element in which to create the View.
42950  * @param {String} tpl The template string used to create the markup for each element of the View
42951  * @param {Object} config The configuration properties. These include all the config options of
42952  * {@link Roo.View} plus some specific to this class.<br>
42953  * <p>
42954  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42955  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42956  * <p>
42957  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42958 .x-view-drag-insert-above {
42959         border-top:1px dotted #3366cc;
42960 }
42961 .x-view-drag-insert-below {
42962         border-bottom:1px dotted #3366cc;
42963 }
42964 </code></pre>
42965  * 
42966  */
42967  
42968 Roo.DDView = function(container, tpl, config) {
42969     Roo.DDView.superclass.constructor.apply(this, arguments);
42970     this.getEl().setStyle("outline", "0px none");
42971     this.getEl().unselectable();
42972     if (this.dragGroup) {
42973                 this.setDraggable(this.dragGroup.split(","));
42974     }
42975     if (this.dropGroup) {
42976                 this.setDroppable(this.dropGroup.split(","));
42977     }
42978     if (this.deletable) {
42979         this.setDeletable();
42980     }
42981     this.isDirtyFlag = false;
42982         this.addEvents({
42983                 "drop" : true
42984         });
42985 };
42986
42987 Roo.extend(Roo.DDView, Roo.View, {
42988 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42989 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42990 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42991 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42992
42993         isFormField: true,
42994
42995         reset: Roo.emptyFn,
42996         
42997         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42998
42999         validate: function() {
43000                 return true;
43001         },
43002         
43003         destroy: function() {
43004                 this.purgeListeners();
43005                 this.getEl.removeAllListeners();
43006                 this.getEl().remove();
43007                 if (this.dragZone) {
43008                         if (this.dragZone.destroy) {
43009                                 this.dragZone.destroy();
43010                         }
43011                 }
43012                 if (this.dropZone) {
43013                         if (this.dropZone.destroy) {
43014                                 this.dropZone.destroy();
43015                         }
43016                 }
43017         },
43018
43019 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43020         getName: function() {
43021                 return this.name;
43022         },
43023
43024 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43025         setValue: function(v) {
43026                 if (!this.store) {
43027                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43028                 }
43029                 var data = {};
43030                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43031                 this.store.proxy = new Roo.data.MemoryProxy(data);
43032                 this.store.load();
43033         },
43034
43035 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43036         getValue: function() {
43037                 var result = '(';
43038                 this.store.each(function(rec) {
43039                         result += rec.id + ',';
43040                 });
43041                 return result.substr(0, result.length - 1) + ')';
43042         },
43043         
43044         getIds: function() {
43045                 var i = 0, result = new Array(this.store.getCount());
43046                 this.store.each(function(rec) {
43047                         result[i++] = rec.id;
43048                 });
43049                 return result;
43050         },
43051         
43052         isDirty: function() {
43053                 return this.isDirtyFlag;
43054         },
43055
43056 /**
43057  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43058  *      whole Element becomes the target, and this causes the drop gesture to append.
43059  */
43060     getTargetFromEvent : function(e) {
43061                 var target = e.getTarget();
43062                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43063                 target = target.parentNode;
43064                 }
43065                 if (!target) {
43066                         target = this.el.dom.lastChild || this.el.dom;
43067                 }
43068                 return target;
43069     },
43070
43071 /**
43072  *      Create the drag data which consists of an object which has the property "ddel" as
43073  *      the drag proxy element. 
43074  */
43075     getDragData : function(e) {
43076         var target = this.findItemFromChild(e.getTarget());
43077                 if(target) {
43078                         this.handleSelection(e);
43079                         var selNodes = this.getSelectedNodes();
43080             var dragData = {
43081                 source: this,
43082                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43083                 nodes: selNodes,
43084                 records: []
43085                         };
43086                         var selectedIndices = this.getSelectedIndexes();
43087                         for (var i = 0; i < selectedIndices.length; i++) {
43088                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43089                         }
43090                         if (selNodes.length == 1) {
43091                                 dragData.ddel = target.cloneNode(true); // the div element
43092                         } else {
43093                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43094                                 div.className = 'multi-proxy';
43095                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43096                                         div.appendChild(selNodes[i].cloneNode(true));
43097                                 }
43098                                 dragData.ddel = div;
43099                         }
43100             //console.log(dragData)
43101             //console.log(dragData.ddel.innerHTML)
43102                         return dragData;
43103                 }
43104         //console.log('nodragData')
43105                 return false;
43106     },
43107     
43108 /**     Specify to which ddGroup items in this DDView may be dragged. */
43109     setDraggable: function(ddGroup) {
43110         if (ddGroup instanceof Array) {
43111                 Roo.each(ddGroup, this.setDraggable, this);
43112                 return;
43113         }
43114         if (this.dragZone) {
43115                 this.dragZone.addToGroup(ddGroup);
43116         } else {
43117                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43118                                 containerScroll: true,
43119                                 ddGroup: ddGroup 
43120
43121                         });
43122 //                      Draggability implies selection. DragZone's mousedown selects the element.
43123                         if (!this.multiSelect) { this.singleSelect = true; }
43124
43125 //                      Wire the DragZone's handlers up to methods in *this*
43126                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43127                 }
43128     },
43129
43130 /**     Specify from which ddGroup this DDView accepts drops. */
43131     setDroppable: function(ddGroup) {
43132         if (ddGroup instanceof Array) {
43133                 Roo.each(ddGroup, this.setDroppable, this);
43134                 return;
43135         }
43136         if (this.dropZone) {
43137                 this.dropZone.addToGroup(ddGroup);
43138         } else {
43139                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43140                                 containerScroll: true,
43141                                 ddGroup: ddGroup
43142                         });
43143
43144 //                      Wire the DropZone's handlers up to methods in *this*
43145                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43146                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43147                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43148                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43149                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43150                 }
43151     },
43152
43153 /**     Decide whether to drop above or below a View node. */
43154     getDropPoint : function(e, n, dd){
43155         if (n == this.el.dom) { return "above"; }
43156                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43157                 var c = t + (b - t) / 2;
43158                 var y = Roo.lib.Event.getPageY(e);
43159                 if(y <= c) {
43160                         return "above";
43161                 }else{
43162                         return "below";
43163                 }
43164     },
43165
43166     onNodeEnter : function(n, dd, e, data){
43167                 return false;
43168     },
43169     
43170     onNodeOver : function(n, dd, e, data){
43171                 var pt = this.getDropPoint(e, n, dd);
43172                 // set the insert point style on the target node
43173                 var dragElClass = this.dropNotAllowed;
43174                 if (pt) {
43175                         var targetElClass;
43176                         if (pt == "above"){
43177                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43178                                 targetElClass = "x-view-drag-insert-above";
43179                         } else {
43180                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43181                                 targetElClass = "x-view-drag-insert-below";
43182                         }
43183                         if (this.lastInsertClass != targetElClass){
43184                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43185                                 this.lastInsertClass = targetElClass;
43186                         }
43187                 }
43188                 return dragElClass;
43189         },
43190
43191     onNodeOut : function(n, dd, e, data){
43192                 this.removeDropIndicators(n);
43193     },
43194
43195     onNodeDrop : function(n, dd, e, data){
43196         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43197                 return false;
43198         }
43199         var pt = this.getDropPoint(e, n, dd);
43200                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43201                 if (pt == "below") { insertAt++; }
43202                 for (var i = 0; i < data.records.length; i++) {
43203                         var r = data.records[i];
43204                         var dup = this.store.getById(r.id);
43205                         if (dup && (dd != this.dragZone)) {
43206                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43207                         } else {
43208                                 if (data.copy) {
43209                                         this.store.insert(insertAt++, r.copy());
43210                                 } else {
43211                                         data.source.isDirtyFlag = true;
43212                                         r.store.remove(r);
43213                                         this.store.insert(insertAt++, r);
43214                                 }
43215                                 this.isDirtyFlag = true;
43216                         }
43217                 }
43218                 this.dragZone.cachedTarget = null;
43219                 return true;
43220     },
43221
43222     removeDropIndicators : function(n){
43223                 if(n){
43224                         Roo.fly(n).removeClass([
43225                                 "x-view-drag-insert-above",
43226                                 "x-view-drag-insert-below"]);
43227                         this.lastInsertClass = "_noclass";
43228                 }
43229     },
43230
43231 /**
43232  *      Utility method. Add a delete option to the DDView's context menu.
43233  *      @param {String} imageUrl The URL of the "delete" icon image.
43234  */
43235         setDeletable: function(imageUrl) {
43236                 if (!this.singleSelect && !this.multiSelect) {
43237                         this.singleSelect = true;
43238                 }
43239                 var c = this.getContextMenu();
43240                 this.contextMenu.on("itemclick", function(item) {
43241                         switch (item.id) {
43242                                 case "delete":
43243                                         this.remove(this.getSelectedIndexes());
43244                                         break;
43245                         }
43246                 }, this);
43247                 this.contextMenu.add({
43248                         icon: imageUrl,
43249                         id: "delete",
43250                         text: 'Delete'
43251                 });
43252         },
43253         
43254 /**     Return the context menu for this DDView. */
43255         getContextMenu: function() {
43256                 if (!this.contextMenu) {
43257 //                      Create the View's context menu
43258                         this.contextMenu = new Roo.menu.Menu({
43259                                 id: this.id + "-contextmenu"
43260                         });
43261                         this.el.on("contextmenu", this.showContextMenu, this);
43262                 }
43263                 return this.contextMenu;
43264         },
43265         
43266         disableContextMenu: function() {
43267                 if (this.contextMenu) {
43268                         this.el.un("contextmenu", this.showContextMenu, this);
43269                 }
43270         },
43271
43272         showContextMenu: function(e, item) {
43273         item = this.findItemFromChild(e.getTarget());
43274                 if (item) {
43275                         e.stopEvent();
43276                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43277                         this.contextMenu.showAt(e.getXY());
43278             }
43279     },
43280
43281 /**
43282  *      Remove {@link Roo.data.Record}s at the specified indices.
43283  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43284  */
43285     remove: function(selectedIndices) {
43286                 selectedIndices = [].concat(selectedIndices);
43287                 for (var i = 0; i < selectedIndices.length; i++) {
43288                         var rec = this.store.getAt(selectedIndices[i]);
43289                         this.store.remove(rec);
43290                 }
43291     },
43292
43293 /**
43294  *      Double click fires the event, but also, if this is draggable, and there is only one other
43295  *      related DropZone, it transfers the selected node.
43296  */
43297     onDblClick : function(e){
43298         var item = this.findItemFromChild(e.getTarget());
43299         if(item){
43300             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43301                 return false;
43302             }
43303             if (this.dragGroup) {
43304                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43305                     while (targets.indexOf(this.dropZone) > -1) {
43306                             targets.remove(this.dropZone);
43307                                 }
43308                     if (targets.length == 1) {
43309                                         this.dragZone.cachedTarget = null;
43310                         var el = Roo.get(targets[0].getEl());
43311                         var box = el.getBox(true);
43312                         targets[0].onNodeDrop(el.dom, {
43313                                 target: el.dom,
43314                                 xy: [box.x, box.y + box.height - 1]
43315                         }, null, this.getDragData(e));
43316                     }
43317                 }
43318         }
43319     },
43320     
43321     handleSelection: function(e) {
43322                 this.dragZone.cachedTarget = null;
43323         var item = this.findItemFromChild(e.getTarget());
43324         if (!item) {
43325                 this.clearSelections(true);
43326                 return;
43327         }
43328                 if (item && (this.multiSelect || this.singleSelect)){
43329                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43330                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43331                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43332                                 this.unselect(item);
43333                         } else {
43334                                 this.select(item, this.multiSelect && e.ctrlKey);
43335                                 this.lastSelection = item;
43336                         }
43337                 }
43338     },
43339
43340     onItemClick : function(item, index, e){
43341                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43342                         return false;
43343                 }
43344                 return true;
43345     },
43346
43347     unselect : function(nodeInfo, suppressEvent){
43348                 var node = this.getNode(nodeInfo);
43349                 if(node && this.isSelected(node)){
43350                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43351                                 Roo.fly(node).removeClass(this.selectedClass);
43352                                 this.selections.remove(node);
43353                                 if(!suppressEvent){
43354                                         this.fireEvent("selectionchange", this, this.selections);
43355                                 }
43356                         }
43357                 }
43358     }
43359 });
43360 /*
43361  * Based on:
43362  * Ext JS Library 1.1.1
43363  * Copyright(c) 2006-2007, Ext JS, LLC.
43364  *
43365  * Originally Released Under LGPL - original licence link has changed is not relivant.
43366  *
43367  * Fork - LGPL
43368  * <script type="text/javascript">
43369  */
43370  
43371 /**
43372  * @class Roo.LayoutManager
43373  * @extends Roo.util.Observable
43374  * Base class for layout managers.
43375  */
43376 Roo.LayoutManager = function(container, config){
43377     Roo.LayoutManager.superclass.constructor.call(this);
43378     this.el = Roo.get(container);
43379     // ie scrollbar fix
43380     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43381         document.body.scroll = "no";
43382     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43383         this.el.position('relative');
43384     }
43385     this.id = this.el.id;
43386     this.el.addClass("x-layout-container");
43387     /** false to disable window resize monitoring @type Boolean */
43388     this.monitorWindowResize = true;
43389     this.regions = {};
43390     this.addEvents({
43391         /**
43392          * @event layout
43393          * Fires when a layout is performed. 
43394          * @param {Roo.LayoutManager} this
43395          */
43396         "layout" : true,
43397         /**
43398          * @event regionresized
43399          * Fires when the user resizes a region. 
43400          * @param {Roo.LayoutRegion} region The resized region
43401          * @param {Number} newSize The new size (width for east/west, height for north/south)
43402          */
43403         "regionresized" : true,
43404         /**
43405          * @event regioncollapsed
43406          * Fires when a region is collapsed. 
43407          * @param {Roo.LayoutRegion} region The collapsed region
43408          */
43409         "regioncollapsed" : true,
43410         /**
43411          * @event regionexpanded
43412          * Fires when a region is expanded.  
43413          * @param {Roo.LayoutRegion} region The expanded region
43414          */
43415         "regionexpanded" : true
43416     });
43417     this.updating = false;
43418     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43419 };
43420
43421 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43422     /**
43423      * Returns true if this layout is currently being updated
43424      * @return {Boolean}
43425      */
43426     isUpdating : function(){
43427         return this.updating; 
43428     },
43429     
43430     /**
43431      * Suspend the LayoutManager from doing auto-layouts while
43432      * making multiple add or remove calls
43433      */
43434     beginUpdate : function(){
43435         this.updating = true;    
43436     },
43437     
43438     /**
43439      * Restore auto-layouts and optionally disable the manager from performing a layout
43440      * @param {Boolean} noLayout true to disable a layout update 
43441      */
43442     endUpdate : function(noLayout){
43443         this.updating = false;
43444         if(!noLayout){
43445             this.layout();
43446         }    
43447     },
43448     
43449     layout: function(){
43450         
43451     },
43452     
43453     onRegionResized : function(region, newSize){
43454         this.fireEvent("regionresized", region, newSize);
43455         this.layout();
43456     },
43457     
43458     onRegionCollapsed : function(region){
43459         this.fireEvent("regioncollapsed", region);
43460     },
43461     
43462     onRegionExpanded : function(region){
43463         this.fireEvent("regionexpanded", region);
43464     },
43465         
43466     /**
43467      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43468      * performs box-model adjustments.
43469      * @return {Object} The size as an object {width: (the width), height: (the height)}
43470      */
43471     getViewSize : function(){
43472         var size;
43473         if(this.el.dom != document.body){
43474             size = this.el.getSize();
43475         }else{
43476             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43477         }
43478         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43479         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43480         return size;
43481     },
43482     
43483     /**
43484      * Returns the Element this layout is bound to.
43485      * @return {Roo.Element}
43486      */
43487     getEl : function(){
43488         return this.el;
43489     },
43490     
43491     /**
43492      * Returns the specified region.
43493      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43494      * @return {Roo.LayoutRegion}
43495      */
43496     getRegion : function(target){
43497         return this.regions[target.toLowerCase()];
43498     },
43499     
43500     onWindowResize : function(){
43501         if(this.monitorWindowResize){
43502             this.layout();
43503         }
43504     }
43505 });/*
43506  * Based on:
43507  * Ext JS Library 1.1.1
43508  * Copyright(c) 2006-2007, Ext JS, LLC.
43509  *
43510  * Originally Released Under LGPL - original licence link has changed is not relivant.
43511  *
43512  * Fork - LGPL
43513  * <script type="text/javascript">
43514  */
43515 /**
43516  * @class Roo.BorderLayout
43517  * @extends Roo.LayoutManager
43518  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43519  * please see: <br><br>
43520  * <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>
43521  * <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>
43522  * Example:
43523  <pre><code>
43524  var layout = new Roo.BorderLayout(document.body, {
43525     north: {
43526         initialSize: 25,
43527         titlebar: false
43528     },
43529     west: {
43530         split:true,
43531         initialSize: 200,
43532         minSize: 175,
43533         maxSize: 400,
43534         titlebar: true,
43535         collapsible: true
43536     },
43537     east: {
43538         split:true,
43539         initialSize: 202,
43540         minSize: 175,
43541         maxSize: 400,
43542         titlebar: true,
43543         collapsible: true
43544     },
43545     south: {
43546         split:true,
43547         initialSize: 100,
43548         minSize: 100,
43549         maxSize: 200,
43550         titlebar: true,
43551         collapsible: true
43552     },
43553     center: {
43554         titlebar: true,
43555         autoScroll:true,
43556         resizeTabs: true,
43557         minTabWidth: 50,
43558         preferredTabWidth: 150
43559     }
43560 });
43561
43562 // shorthand
43563 var CP = Roo.ContentPanel;
43564
43565 layout.beginUpdate();
43566 layout.add("north", new CP("north", "North"));
43567 layout.add("south", new CP("south", {title: "South", closable: true}));
43568 layout.add("west", new CP("west", {title: "West"}));
43569 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43570 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43571 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43572 layout.getRegion("center").showPanel("center1");
43573 layout.endUpdate();
43574 </code></pre>
43575
43576 <b>The container the layout is rendered into can be either the body element or any other element.
43577 If it is not the body element, the container needs to either be an absolute positioned element,
43578 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43579 the container size if it is not the body element.</b>
43580
43581 * @constructor
43582 * Create a new BorderLayout
43583 * @param {String/HTMLElement/Element} container The container this layout is bound to
43584 * @param {Object} config Configuration options
43585  */
43586 Roo.BorderLayout = function(container, config){
43587     config = config || {};
43588     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43589     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43590     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43591         var target = this.factory.validRegions[i];
43592         if(config[target]){
43593             this.addRegion(target, config[target]);
43594         }
43595     }
43596 };
43597
43598 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43599     /**
43600      * Creates and adds a new region if it doesn't already exist.
43601      * @param {String} target The target region key (north, south, east, west or center).
43602      * @param {Object} config The regions config object
43603      * @return {BorderLayoutRegion} The new region
43604      */
43605     addRegion : function(target, config){
43606         if(!this.regions[target]){
43607             var r = this.factory.create(target, this, config);
43608             this.bindRegion(target, r);
43609         }
43610         return this.regions[target];
43611     },
43612
43613     // private (kinda)
43614     bindRegion : function(name, r){
43615         this.regions[name] = r;
43616         r.on("visibilitychange", this.layout, this);
43617         r.on("paneladded", this.layout, this);
43618         r.on("panelremoved", this.layout, this);
43619         r.on("invalidated", this.layout, this);
43620         r.on("resized", this.onRegionResized, this);
43621         r.on("collapsed", this.onRegionCollapsed, this);
43622         r.on("expanded", this.onRegionExpanded, this);
43623     },
43624
43625     /**
43626      * Performs a layout update.
43627      */
43628     layout : function(){
43629         if(this.updating) return;
43630         var size = this.getViewSize();
43631         var w = size.width;
43632         var h = size.height;
43633         var centerW = w;
43634         var centerH = h;
43635         var centerY = 0;
43636         var centerX = 0;
43637         //var x = 0, y = 0;
43638
43639         var rs = this.regions;
43640         var north = rs["north"];
43641         var south = rs["south"]; 
43642         var west = rs["west"];
43643         var east = rs["east"];
43644         var center = rs["center"];
43645         //if(this.hideOnLayout){ // not supported anymore
43646             //c.el.setStyle("display", "none");
43647         //}
43648         if(north && north.isVisible()){
43649             var b = north.getBox();
43650             var m = north.getMargins();
43651             b.width = w - (m.left+m.right);
43652             b.x = m.left;
43653             b.y = m.top;
43654             centerY = b.height + b.y + m.bottom;
43655             centerH -= centerY;
43656             north.updateBox(this.safeBox(b));
43657         }
43658         if(south && south.isVisible()){
43659             var b = south.getBox();
43660             var m = south.getMargins();
43661             b.width = w - (m.left+m.right);
43662             b.x = m.left;
43663             var totalHeight = (b.height + m.top + m.bottom);
43664             b.y = h - totalHeight + m.top;
43665             centerH -= totalHeight;
43666             south.updateBox(this.safeBox(b));
43667         }
43668         if(west && west.isVisible()){
43669             var b = west.getBox();
43670             var m = west.getMargins();
43671             b.height = centerH - (m.top+m.bottom);
43672             b.x = m.left;
43673             b.y = centerY + m.top;
43674             var totalWidth = (b.width + m.left + m.right);
43675             centerX += totalWidth;
43676             centerW -= totalWidth;
43677             west.updateBox(this.safeBox(b));
43678         }
43679         if(east && east.isVisible()){
43680             var b = east.getBox();
43681             var m = east.getMargins();
43682             b.height = centerH - (m.top+m.bottom);
43683             var totalWidth = (b.width + m.left + m.right);
43684             b.x = w - totalWidth + m.left;
43685             b.y = centerY + m.top;
43686             centerW -= totalWidth;
43687             east.updateBox(this.safeBox(b));
43688         }
43689         if(center){
43690             var m = center.getMargins();
43691             var centerBox = {
43692                 x: centerX + m.left,
43693                 y: centerY + m.top,
43694                 width: centerW - (m.left+m.right),
43695                 height: centerH - (m.top+m.bottom)
43696             };
43697             //if(this.hideOnLayout){
43698                 //center.el.setStyle("display", "block");
43699             //}
43700             center.updateBox(this.safeBox(centerBox));
43701         }
43702         this.el.repaint();
43703         this.fireEvent("layout", this);
43704     },
43705
43706     // private
43707     safeBox : function(box){
43708         box.width = Math.max(0, box.width);
43709         box.height = Math.max(0, box.height);
43710         return box;
43711     },
43712
43713     /**
43714      * Adds a ContentPanel (or subclass) to this layout.
43715      * @param {String} target The target region key (north, south, east, west or center).
43716      * @param {Roo.ContentPanel} panel The panel to add
43717      * @return {Roo.ContentPanel} The added panel
43718      */
43719     add : function(target, panel){
43720          
43721         target = target.toLowerCase();
43722         return this.regions[target].add(panel);
43723     },
43724
43725     /**
43726      * Remove a ContentPanel (or subclass) to this layout.
43727      * @param {String} target The target region key (north, south, east, west or center).
43728      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43729      * @return {Roo.ContentPanel} The removed panel
43730      */
43731     remove : function(target, panel){
43732         target = target.toLowerCase();
43733         return this.regions[target].remove(panel);
43734     },
43735
43736     /**
43737      * Searches all regions for a panel with the specified id
43738      * @param {String} panelId
43739      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43740      */
43741     findPanel : function(panelId){
43742         var rs = this.regions;
43743         for(var target in rs){
43744             if(typeof rs[target] != "function"){
43745                 var p = rs[target].getPanel(panelId);
43746                 if(p){
43747                     return p;
43748                 }
43749             }
43750         }
43751         return null;
43752     },
43753
43754     /**
43755      * Searches all regions for a panel with the specified id and activates (shows) it.
43756      * @param {String/ContentPanel} panelId The panels id or the panel itself
43757      * @return {Roo.ContentPanel} The shown panel or null
43758      */
43759     showPanel : function(panelId) {
43760       var rs = this.regions;
43761       for(var target in rs){
43762          var r = rs[target];
43763          if(typeof r != "function"){
43764             if(r.hasPanel(panelId)){
43765                return r.showPanel(panelId);
43766             }
43767          }
43768       }
43769       return null;
43770    },
43771
43772    /**
43773      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43774      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43775      */
43776     restoreState : function(provider){
43777         if(!provider){
43778             provider = Roo.state.Manager;
43779         }
43780         var sm = new Roo.LayoutStateManager();
43781         sm.init(this, provider);
43782     },
43783
43784     /**
43785      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43786      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43787      * a valid ContentPanel config object.  Example:
43788      * <pre><code>
43789 // Create the main layout
43790 var layout = new Roo.BorderLayout('main-ct', {
43791     west: {
43792         split:true,
43793         minSize: 175,
43794         titlebar: true
43795     },
43796     center: {
43797         title:'Components'
43798     }
43799 }, 'main-ct');
43800
43801 // Create and add multiple ContentPanels at once via configs
43802 layout.batchAdd({
43803    west: {
43804        id: 'source-files',
43805        autoCreate:true,
43806        title:'Ext Source Files',
43807        autoScroll:true,
43808        fitToFrame:true
43809    },
43810    center : {
43811        el: cview,
43812        autoScroll:true,
43813        fitToFrame:true,
43814        toolbar: tb,
43815        resizeEl:'cbody'
43816    }
43817 });
43818 </code></pre>
43819      * @param {Object} regions An object containing ContentPanel configs by region name
43820      */
43821     batchAdd : function(regions){
43822         this.beginUpdate();
43823         for(var rname in regions){
43824             var lr = this.regions[rname];
43825             if(lr){
43826                 this.addTypedPanels(lr, regions[rname]);
43827             }
43828         }
43829         this.endUpdate();
43830     },
43831
43832     // private
43833     addTypedPanels : function(lr, ps){
43834         if(typeof ps == 'string'){
43835             lr.add(new Roo.ContentPanel(ps));
43836         }
43837         else if(ps instanceof Array){
43838             for(var i =0, len = ps.length; i < len; i++){
43839                 this.addTypedPanels(lr, ps[i]);
43840             }
43841         }
43842         else if(!ps.events){ // raw config?
43843             var el = ps.el;
43844             delete ps.el; // prevent conflict
43845             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43846         }
43847         else {  // panel object assumed!
43848             lr.add(ps);
43849         }
43850     },
43851     /**
43852      * Adds a xtype elements to the layout.
43853      * <pre><code>
43854
43855 layout.addxtype({
43856        xtype : 'ContentPanel',
43857        region: 'west',
43858        items: [ .... ]
43859    }
43860 );
43861
43862 layout.addxtype({
43863         xtype : 'NestedLayoutPanel',
43864         region: 'west',
43865         layout: {
43866            center: { },
43867            west: { }   
43868         },
43869         items : [ ... list of content panels or nested layout panels.. ]
43870    }
43871 );
43872 </code></pre>
43873      * @param {Object} cfg Xtype definition of item to add.
43874      */
43875     addxtype : function(cfg)
43876     {
43877         // basically accepts a pannel...
43878         // can accept a layout region..!?!?
43879        // console.log('BorderLayout add ' + cfg.xtype)
43880         
43881         if (!cfg.xtype.match(/Panel$/)) {
43882             return false;
43883         }
43884         var ret = false;
43885         var region = cfg.region;
43886         delete cfg.region;
43887         
43888           
43889         var xitems = [];
43890         if (cfg.items) {
43891             xitems = cfg.items;
43892             delete cfg.items;
43893         }
43894         
43895         
43896         switch(cfg.xtype) 
43897         {
43898             case 'ContentPanel':  // ContentPanel (el, cfg)
43899             case 'ScrollPanel':  // ContentPanel (el, cfg)
43900                 if(cfg.autoCreate) {
43901                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43902                 } else {
43903                     var el = this.el.createChild();
43904                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43905                 }
43906                 
43907                 this.add(region, ret);
43908                 break;
43909             
43910             
43911             case 'TreePanel': // our new panel!
43912                 cfg.el = this.el.createChild();
43913                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43914                 this.add(region, ret);
43915                 break;
43916             
43917             case 'NestedLayoutPanel': 
43918                 // create a new Layout (which is  a Border Layout...
43919                 var el = this.el.createChild();
43920                 var clayout = cfg.layout;
43921                 delete cfg.layout;
43922                 clayout.items   = clayout.items  || [];
43923                 // replace this exitems with the clayout ones..
43924                 xitems = clayout.items;
43925                  
43926                 
43927                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43928                     cfg.background = false;
43929                 }
43930                 var layout = new Roo.BorderLayout(el, clayout);
43931                 
43932                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43933                 //console.log('adding nested layout panel '  + cfg.toSource());
43934                 this.add(region, ret);
43935                 
43936                 break;
43937                 
43938             case 'GridPanel': 
43939             
43940                 // needs grid and region
43941                 
43942                 //var el = this.getRegion(region).el.createChild();
43943                 var el = this.el.createChild();
43944                 // create the grid first...
43945                 
43946                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43947                 delete cfg.grid;
43948                 if (region == 'center' && this.active ) {
43949                     cfg.background = false;
43950                 }
43951                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43952                 
43953                 this.add(region, ret);
43954                 if (cfg.background) {
43955                     ret.on('activate', function(gp) {
43956                         if (!gp.grid.rendered) {
43957                             gp.grid.render();
43958                         }
43959                     });
43960                 } else {
43961                     grid.render();
43962                 }
43963                 break;
43964            
43965                
43966                 
43967                 
43968             default: 
43969                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43970                 return null;
43971              // GridPanel (grid, cfg)
43972             
43973         }
43974         this.beginUpdate();
43975         // add children..
43976         Roo.each(xitems, function(i)  {
43977             ret.addxtype(i);
43978         });
43979         this.endUpdate();
43980         return ret;
43981         
43982     }
43983 });
43984
43985 /**
43986  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43987  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43988  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43989  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43990  * <pre><code>
43991 // shorthand
43992 var CP = Roo.ContentPanel;
43993
43994 var layout = Roo.BorderLayout.create({
43995     north: {
43996         initialSize: 25,
43997         titlebar: false,
43998         panels: [new CP("north", "North")]
43999     },
44000     west: {
44001         split:true,
44002         initialSize: 200,
44003         minSize: 175,
44004         maxSize: 400,
44005         titlebar: true,
44006         collapsible: true,
44007         panels: [new CP("west", {title: "West"})]
44008     },
44009     east: {
44010         split:true,
44011         initialSize: 202,
44012         minSize: 175,
44013         maxSize: 400,
44014         titlebar: true,
44015         collapsible: true,
44016         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44017     },
44018     south: {
44019         split:true,
44020         initialSize: 100,
44021         minSize: 100,
44022         maxSize: 200,
44023         titlebar: true,
44024         collapsible: true,
44025         panels: [new CP("south", {title: "South", closable: true})]
44026     },
44027     center: {
44028         titlebar: true,
44029         autoScroll:true,
44030         resizeTabs: true,
44031         minTabWidth: 50,
44032         preferredTabWidth: 150,
44033         panels: [
44034             new CP("center1", {title: "Close Me", closable: true}),
44035             new CP("center2", {title: "Center Panel", closable: false})
44036         ]
44037     }
44038 }, document.body);
44039
44040 layout.getRegion("center").showPanel("center1");
44041 </code></pre>
44042  * @param config
44043  * @param targetEl
44044  */
44045 Roo.BorderLayout.create = function(config, targetEl){
44046     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44047     layout.beginUpdate();
44048     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44049     for(var j = 0, jlen = regions.length; j < jlen; j++){
44050         var lr = regions[j];
44051         if(layout.regions[lr] && config[lr].panels){
44052             var r = layout.regions[lr];
44053             var ps = config[lr].panels;
44054             layout.addTypedPanels(r, ps);
44055         }
44056     }
44057     layout.endUpdate();
44058     return layout;
44059 };
44060
44061 // private
44062 Roo.BorderLayout.RegionFactory = {
44063     // private
44064     validRegions : ["north","south","east","west","center"],
44065
44066     // private
44067     create : function(target, mgr, config){
44068         target = target.toLowerCase();
44069         if(config.lightweight || config.basic){
44070             return new Roo.BasicLayoutRegion(mgr, config, target);
44071         }
44072         switch(target){
44073             case "north":
44074                 return new Roo.NorthLayoutRegion(mgr, config);
44075             case "south":
44076                 return new Roo.SouthLayoutRegion(mgr, config);
44077             case "east":
44078                 return new Roo.EastLayoutRegion(mgr, config);
44079             case "west":
44080                 return new Roo.WestLayoutRegion(mgr, config);
44081             case "center":
44082                 return new Roo.CenterLayoutRegion(mgr, config);
44083         }
44084         throw 'Layout region "'+target+'" not supported.';
44085     }
44086 };/*
44087  * Based on:
44088  * Ext JS Library 1.1.1
44089  * Copyright(c) 2006-2007, Ext JS, LLC.
44090  *
44091  * Originally Released Under LGPL - original licence link has changed is not relivant.
44092  *
44093  * Fork - LGPL
44094  * <script type="text/javascript">
44095  */
44096  
44097 /**
44098  * @class Roo.BasicLayoutRegion
44099  * @extends Roo.util.Observable
44100  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44101  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44102  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44103  */
44104 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44105     this.mgr = mgr;
44106     this.position  = pos;
44107     this.events = {
44108         /**
44109          * @scope Roo.BasicLayoutRegion
44110          */
44111         
44112         /**
44113          * @event beforeremove
44114          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44115          * @param {Roo.LayoutRegion} this
44116          * @param {Roo.ContentPanel} panel The panel
44117          * @param {Object} e The cancel event object
44118          */
44119         "beforeremove" : true,
44120         /**
44121          * @event invalidated
44122          * Fires when the layout for this region is changed.
44123          * @param {Roo.LayoutRegion} this
44124          */
44125         "invalidated" : true,
44126         /**
44127          * @event visibilitychange
44128          * Fires when this region is shown or hidden 
44129          * @param {Roo.LayoutRegion} this
44130          * @param {Boolean} visibility true or false
44131          */
44132         "visibilitychange" : true,
44133         /**
44134          * @event paneladded
44135          * Fires when a panel is added. 
44136          * @param {Roo.LayoutRegion} this
44137          * @param {Roo.ContentPanel} panel The panel
44138          */
44139         "paneladded" : true,
44140         /**
44141          * @event panelremoved
44142          * Fires when a panel is removed. 
44143          * @param {Roo.LayoutRegion} this
44144          * @param {Roo.ContentPanel} panel The panel
44145          */
44146         "panelremoved" : true,
44147         /**
44148          * @event collapsed
44149          * Fires when this region is collapsed.
44150          * @param {Roo.LayoutRegion} this
44151          */
44152         "collapsed" : true,
44153         /**
44154          * @event expanded
44155          * Fires when this region is expanded.
44156          * @param {Roo.LayoutRegion} this
44157          */
44158         "expanded" : true,
44159         /**
44160          * @event slideshow
44161          * Fires when this region is slid into view.
44162          * @param {Roo.LayoutRegion} this
44163          */
44164         "slideshow" : true,
44165         /**
44166          * @event slidehide
44167          * Fires when this region slides out of view. 
44168          * @param {Roo.LayoutRegion} this
44169          */
44170         "slidehide" : true,
44171         /**
44172          * @event panelactivated
44173          * Fires when a panel is activated. 
44174          * @param {Roo.LayoutRegion} this
44175          * @param {Roo.ContentPanel} panel The activated panel
44176          */
44177         "panelactivated" : true,
44178         /**
44179          * @event resized
44180          * Fires when the user resizes this region. 
44181          * @param {Roo.LayoutRegion} this
44182          * @param {Number} newSize The new size (width for east/west, height for north/south)
44183          */
44184         "resized" : true
44185     };
44186     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44187     this.panels = new Roo.util.MixedCollection();
44188     this.panels.getKey = this.getPanelId.createDelegate(this);
44189     this.box = null;
44190     this.activePanel = null;
44191     // ensure listeners are added...
44192     
44193     if (config.listeners || config.events) {
44194         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44195             listeners : config.listeners || {},
44196             events : config.events || {}
44197         });
44198     }
44199     
44200     if(skipConfig !== true){
44201         this.applyConfig(config);
44202     }
44203 };
44204
44205 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44206     getPanelId : function(p){
44207         return p.getId();
44208     },
44209     
44210     applyConfig : function(config){
44211         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44212         this.config = config;
44213         
44214     },
44215     
44216     /**
44217      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44218      * the width, for horizontal (north, south) the height.
44219      * @param {Number} newSize The new width or height
44220      */
44221     resizeTo : function(newSize){
44222         var el = this.el ? this.el :
44223                  (this.activePanel ? this.activePanel.getEl() : null);
44224         if(el){
44225             switch(this.position){
44226                 case "east":
44227                 case "west":
44228                     el.setWidth(newSize);
44229                     this.fireEvent("resized", this, newSize);
44230                 break;
44231                 case "north":
44232                 case "south":
44233                     el.setHeight(newSize);
44234                     this.fireEvent("resized", this, newSize);
44235                 break;                
44236             }
44237         }
44238     },
44239     
44240     getBox : function(){
44241         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44242     },
44243     
44244     getMargins : function(){
44245         return this.margins;
44246     },
44247     
44248     updateBox : function(box){
44249         this.box = box;
44250         var el = this.activePanel.getEl();
44251         el.dom.style.left = box.x + "px";
44252         el.dom.style.top = box.y + "px";
44253         this.activePanel.setSize(box.width, box.height);
44254     },
44255     
44256     /**
44257      * Returns the container element for this region.
44258      * @return {Roo.Element}
44259      */
44260     getEl : function(){
44261         return this.activePanel;
44262     },
44263     
44264     /**
44265      * Returns true if this region is currently visible.
44266      * @return {Boolean}
44267      */
44268     isVisible : function(){
44269         return this.activePanel ? true : false;
44270     },
44271     
44272     setActivePanel : function(panel){
44273         panel = this.getPanel(panel);
44274         if(this.activePanel && this.activePanel != panel){
44275             this.activePanel.setActiveState(false);
44276             this.activePanel.getEl().setLeftTop(-10000,-10000);
44277         }
44278         this.activePanel = panel;
44279         panel.setActiveState(true);
44280         if(this.box){
44281             panel.setSize(this.box.width, this.box.height);
44282         }
44283         this.fireEvent("panelactivated", this, panel);
44284         this.fireEvent("invalidated");
44285     },
44286     
44287     /**
44288      * Show the specified panel.
44289      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44290      * @return {Roo.ContentPanel} The shown panel or null
44291      */
44292     showPanel : function(panel){
44293         if(panel = this.getPanel(panel)){
44294             this.setActivePanel(panel);
44295         }
44296         return panel;
44297     },
44298     
44299     /**
44300      * Get the active panel for this region.
44301      * @return {Roo.ContentPanel} The active panel or null
44302      */
44303     getActivePanel : function(){
44304         return this.activePanel;
44305     },
44306     
44307     /**
44308      * Add the passed ContentPanel(s)
44309      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44310      * @return {Roo.ContentPanel} The panel added (if only one was added)
44311      */
44312     add : function(panel){
44313         if(arguments.length > 1){
44314             for(var i = 0, len = arguments.length; i < len; i++) {
44315                 this.add(arguments[i]);
44316             }
44317             return null;
44318         }
44319         if(this.hasPanel(panel)){
44320             this.showPanel(panel);
44321             return panel;
44322         }
44323         var el = panel.getEl();
44324         if(el.dom.parentNode != this.mgr.el.dom){
44325             this.mgr.el.dom.appendChild(el.dom);
44326         }
44327         if(panel.setRegion){
44328             panel.setRegion(this);
44329         }
44330         this.panels.add(panel);
44331         el.setStyle("position", "absolute");
44332         if(!panel.background){
44333             this.setActivePanel(panel);
44334             if(this.config.initialSize && this.panels.getCount()==1){
44335                 this.resizeTo(this.config.initialSize);
44336             }
44337         }
44338         this.fireEvent("paneladded", this, panel);
44339         return panel;
44340     },
44341     
44342     /**
44343      * Returns true if the panel is in this region.
44344      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44345      * @return {Boolean}
44346      */
44347     hasPanel : function(panel){
44348         if(typeof panel == "object"){ // must be panel obj
44349             panel = panel.getId();
44350         }
44351         return this.getPanel(panel) ? true : false;
44352     },
44353     
44354     /**
44355      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44356      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44357      * @param {Boolean} preservePanel Overrides the config preservePanel option
44358      * @return {Roo.ContentPanel} The panel that was removed
44359      */
44360     remove : function(panel, preservePanel){
44361         panel = this.getPanel(panel);
44362         if(!panel){
44363             return null;
44364         }
44365         var e = {};
44366         this.fireEvent("beforeremove", this, panel, e);
44367         if(e.cancel === true){
44368             return null;
44369         }
44370         var panelId = panel.getId();
44371         this.panels.removeKey(panelId);
44372         return panel;
44373     },
44374     
44375     /**
44376      * Returns the panel specified or null if it's not in this region.
44377      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44378      * @return {Roo.ContentPanel}
44379      */
44380     getPanel : function(id){
44381         if(typeof id == "object"){ // must be panel obj
44382             return id;
44383         }
44384         return this.panels.get(id);
44385     },
44386     
44387     /**
44388      * Returns this regions position (north/south/east/west/center).
44389      * @return {String} 
44390      */
44391     getPosition: function(){
44392         return this.position;    
44393     }
44394 });/*
44395  * Based on:
44396  * Ext JS Library 1.1.1
44397  * Copyright(c) 2006-2007, Ext JS, LLC.
44398  *
44399  * Originally Released Under LGPL - original licence link has changed is not relivant.
44400  *
44401  * Fork - LGPL
44402  * <script type="text/javascript">
44403  */
44404  
44405 /**
44406  * @class Roo.LayoutRegion
44407  * @extends Roo.BasicLayoutRegion
44408  * This class represents a region in a layout manager.
44409  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
44410  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
44411  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
44412  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44413  * @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})
44414  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
44415  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
44416  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
44417  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
44418  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
44419  * @cfg {String}    title           The title for the region (overrides panel titles)
44420  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
44421  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44422  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
44423  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44424  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
44425  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44426  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
44427  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
44428  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
44429  * @cfg {Boolean}   showPin         True to show a pin button
44430  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
44431  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
44432  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
44433  * @cfg {Number}    width           For East/West panels
44434  * @cfg {Number}    height          For North/South panels
44435  * @cfg {Boolean}   split           To show the splitter
44436  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
44437  */
44438 Roo.LayoutRegion = function(mgr, config, pos){
44439     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44440     var dh = Roo.DomHelper;
44441     /** This region's container element 
44442     * @type Roo.Element */
44443     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44444     /** This region's title element 
44445     * @type Roo.Element */
44446
44447     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44448         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44449         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44450     ]}, true);
44451     this.titleEl.enableDisplayMode();
44452     /** This region's title text element 
44453     * @type HTMLElement */
44454     this.titleTextEl = this.titleEl.dom.firstChild;
44455     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44456     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44457     this.closeBtn.enableDisplayMode();
44458     this.closeBtn.on("click", this.closeClicked, this);
44459     this.closeBtn.hide();
44460
44461     this.createBody(config);
44462     this.visible = true;
44463     this.collapsed = false;
44464
44465     if(config.hideWhenEmpty){
44466         this.hide();
44467         this.on("paneladded", this.validateVisibility, this);
44468         this.on("panelremoved", this.validateVisibility, this);
44469     }
44470     this.applyConfig(config);
44471 };
44472
44473 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44474
44475     createBody : function(){
44476         /** This region's body element 
44477         * @type Roo.Element */
44478         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44479     },
44480
44481     applyConfig : function(c){
44482         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44483             var dh = Roo.DomHelper;
44484             if(c.titlebar !== false){
44485                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44486                 this.collapseBtn.on("click", this.collapse, this);
44487                 this.collapseBtn.enableDisplayMode();
44488
44489                 if(c.showPin === true || this.showPin){
44490                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44491                     this.stickBtn.enableDisplayMode();
44492                     this.stickBtn.on("click", this.expand, this);
44493                     this.stickBtn.hide();
44494                 }
44495             }
44496             /** This region's collapsed element
44497             * @type Roo.Element */
44498             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44499                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44500             ]}, true);
44501             if(c.floatable !== false){
44502                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44503                this.collapsedEl.on("click", this.collapseClick, this);
44504             }
44505
44506             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44507                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44508                    id: "message", unselectable: "on", style:{"float":"left"}});
44509                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44510              }
44511             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44512             this.expandBtn.on("click", this.expand, this);
44513         }
44514         if(this.collapseBtn){
44515             this.collapseBtn.setVisible(c.collapsible == true);
44516         }
44517         this.cmargins = c.cmargins || this.cmargins ||
44518                          (this.position == "west" || this.position == "east" ?
44519                              {top: 0, left: 2, right:2, bottom: 0} :
44520                              {top: 2, left: 0, right:0, bottom: 2});
44521         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44522         this.bottomTabs = c.tabPosition != "top";
44523         this.autoScroll = c.autoScroll || false;
44524         if(this.autoScroll){
44525             this.bodyEl.setStyle("overflow", "auto");
44526         }else{
44527             this.bodyEl.setStyle("overflow", "hidden");
44528         }
44529         //if(c.titlebar !== false){
44530             if((!c.titlebar && !c.title) || c.titlebar === false){
44531                 this.titleEl.hide();
44532             }else{
44533                 this.titleEl.show();
44534                 if(c.title){
44535                     this.titleTextEl.innerHTML = c.title;
44536                 }
44537             }
44538         //}
44539         this.duration = c.duration || .30;
44540         this.slideDuration = c.slideDuration || .45;
44541         this.config = c;
44542         if(c.collapsed){
44543             this.collapse(true);
44544         }
44545         if(c.hidden){
44546             this.hide();
44547         }
44548     },
44549     /**
44550      * Returns true if this region is currently visible.
44551      * @return {Boolean}
44552      */
44553     isVisible : function(){
44554         return this.visible;
44555     },
44556
44557     /**
44558      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44559      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44560      */
44561     setCollapsedTitle : function(title){
44562         title = title || "&#160;";
44563         if(this.collapsedTitleTextEl){
44564             this.collapsedTitleTextEl.innerHTML = title;
44565         }
44566     },
44567
44568     getBox : function(){
44569         var b;
44570         if(!this.collapsed){
44571             b = this.el.getBox(false, true);
44572         }else{
44573             b = this.collapsedEl.getBox(false, true);
44574         }
44575         return b;
44576     },
44577
44578     getMargins : function(){
44579         return this.collapsed ? this.cmargins : this.margins;
44580     },
44581
44582     highlight : function(){
44583         this.el.addClass("x-layout-panel-dragover");
44584     },
44585
44586     unhighlight : function(){
44587         this.el.removeClass("x-layout-panel-dragover");
44588     },
44589
44590     updateBox : function(box){
44591         this.box = box;
44592         if(!this.collapsed){
44593             this.el.dom.style.left = box.x + "px";
44594             this.el.dom.style.top = box.y + "px";
44595             this.updateBody(box.width, box.height);
44596         }else{
44597             this.collapsedEl.dom.style.left = box.x + "px";
44598             this.collapsedEl.dom.style.top = box.y + "px";
44599             this.collapsedEl.setSize(box.width, box.height);
44600         }
44601         if(this.tabs){
44602             this.tabs.autoSizeTabs();
44603         }
44604     },
44605
44606     updateBody : function(w, h){
44607         if(w !== null){
44608             this.el.setWidth(w);
44609             w -= this.el.getBorderWidth("rl");
44610             if(this.config.adjustments){
44611                 w += this.config.adjustments[0];
44612             }
44613         }
44614         if(h !== null){
44615             this.el.setHeight(h);
44616             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44617             h -= this.el.getBorderWidth("tb");
44618             if(this.config.adjustments){
44619                 h += this.config.adjustments[1];
44620             }
44621             this.bodyEl.setHeight(h);
44622             if(this.tabs){
44623                 h = this.tabs.syncHeight(h);
44624             }
44625         }
44626         if(this.panelSize){
44627             w = w !== null ? w : this.panelSize.width;
44628             h = h !== null ? h : this.panelSize.height;
44629         }
44630         if(this.activePanel){
44631             var el = this.activePanel.getEl();
44632             w = w !== null ? w : el.getWidth();
44633             h = h !== null ? h : el.getHeight();
44634             this.panelSize = {width: w, height: h};
44635             this.activePanel.setSize(w, h);
44636         }
44637         if(Roo.isIE && this.tabs){
44638             this.tabs.el.repaint();
44639         }
44640     },
44641
44642     /**
44643      * Returns the container element for this region.
44644      * @return {Roo.Element}
44645      */
44646     getEl : function(){
44647         return this.el;
44648     },
44649
44650     /**
44651      * Hides this region.
44652      */
44653     hide : function(){
44654         if(!this.collapsed){
44655             this.el.dom.style.left = "-2000px";
44656             this.el.hide();
44657         }else{
44658             this.collapsedEl.dom.style.left = "-2000px";
44659             this.collapsedEl.hide();
44660         }
44661         this.visible = false;
44662         this.fireEvent("visibilitychange", this, false);
44663     },
44664
44665     /**
44666      * Shows this region if it was previously hidden.
44667      */
44668     show : function(){
44669         if(!this.collapsed){
44670             this.el.show();
44671         }else{
44672             this.collapsedEl.show();
44673         }
44674         this.visible = true;
44675         this.fireEvent("visibilitychange", this, true);
44676     },
44677
44678     closeClicked : function(){
44679         if(this.activePanel){
44680             this.remove(this.activePanel);
44681         }
44682     },
44683
44684     collapseClick : function(e){
44685         if(this.isSlid){
44686            e.stopPropagation();
44687            this.slideIn();
44688         }else{
44689            e.stopPropagation();
44690            this.slideOut();
44691         }
44692     },
44693
44694     /**
44695      * Collapses this region.
44696      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44697      */
44698     collapse : function(skipAnim){
44699         if(this.collapsed) return;
44700         this.collapsed = true;
44701         if(this.split){
44702             this.split.el.hide();
44703         }
44704         if(this.config.animate && skipAnim !== true){
44705             this.fireEvent("invalidated", this);
44706             this.animateCollapse();
44707         }else{
44708             this.el.setLocation(-20000,-20000);
44709             this.el.hide();
44710             this.collapsedEl.show();
44711             this.fireEvent("collapsed", this);
44712             this.fireEvent("invalidated", this);
44713         }
44714     },
44715
44716     animateCollapse : function(){
44717         // overridden
44718     },
44719
44720     /**
44721      * Expands this region if it was previously collapsed.
44722      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44723      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44724      */
44725     expand : function(e, skipAnim){
44726         if(e) e.stopPropagation();
44727         if(!this.collapsed || this.el.hasActiveFx()) return;
44728         if(this.isSlid){
44729             this.afterSlideIn();
44730             skipAnim = true;
44731         }
44732         this.collapsed = false;
44733         if(this.config.animate && skipAnim !== true){
44734             this.animateExpand();
44735         }else{
44736             this.el.show();
44737             if(this.split){
44738                 this.split.el.show();
44739             }
44740             this.collapsedEl.setLocation(-2000,-2000);
44741             this.collapsedEl.hide();
44742             this.fireEvent("invalidated", this);
44743             this.fireEvent("expanded", this);
44744         }
44745     },
44746
44747     animateExpand : function(){
44748         // overridden
44749     },
44750
44751     initTabs : function()
44752     {
44753         this.bodyEl.setStyle("overflow", "hidden");
44754         var ts = new Roo.TabPanel(
44755                 this.bodyEl.dom,
44756                 {
44757                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
44758                     disableTooltips: this.config.disableTabTips,
44759                     toolbar : this.config.toolbar
44760                 }
44761         );
44762         if(this.config.hideTabs){
44763             ts.stripWrap.setDisplayed(false);
44764         }
44765         this.tabs = ts;
44766         ts.resizeTabs = this.config.resizeTabs === true;
44767         ts.minTabWidth = this.config.minTabWidth || 40;
44768         ts.maxTabWidth = this.config.maxTabWidth || 250;
44769         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44770         ts.monitorResize = false;
44771         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44772         ts.bodyEl.addClass('x-layout-tabs-body');
44773         this.panels.each(this.initPanelAsTab, this);
44774     },
44775
44776     initPanelAsTab : function(panel){
44777         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44778                     this.config.closeOnTab && panel.isClosable());
44779         if(panel.tabTip !== undefined){
44780             ti.setTooltip(panel.tabTip);
44781         }
44782         ti.on("activate", function(){
44783               this.setActivePanel(panel);
44784         }, this);
44785         if(this.config.closeOnTab){
44786             ti.on("beforeclose", function(t, e){
44787                 e.cancel = true;
44788                 this.remove(panel);
44789             }, this);
44790         }
44791         return ti;
44792     },
44793
44794     updatePanelTitle : function(panel, title){
44795         if(this.activePanel == panel){
44796             this.updateTitle(title);
44797         }
44798         if(this.tabs){
44799             var ti = this.tabs.getTab(panel.getEl().id);
44800             ti.setText(title);
44801             if(panel.tabTip !== undefined){
44802                 ti.setTooltip(panel.tabTip);
44803             }
44804         }
44805     },
44806
44807     updateTitle : function(title){
44808         if(this.titleTextEl && !this.config.title){
44809             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44810         }
44811     },
44812
44813     setActivePanel : function(panel){
44814         panel = this.getPanel(panel);
44815         if(this.activePanel && this.activePanel != panel){
44816             this.activePanel.setActiveState(false);
44817         }
44818         this.activePanel = panel;
44819         panel.setActiveState(true);
44820         if(this.panelSize){
44821             panel.setSize(this.panelSize.width, this.panelSize.height);
44822         }
44823         if(this.closeBtn){
44824             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44825         }
44826         this.updateTitle(panel.getTitle());
44827         if(this.tabs){
44828             this.fireEvent("invalidated", this);
44829         }
44830         this.fireEvent("panelactivated", this, panel);
44831     },
44832
44833     /**
44834      * Shows the specified panel.
44835      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44836      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44837      */
44838     showPanel : function(panel){
44839         if(panel = this.getPanel(panel)){
44840             if(this.tabs){
44841                 var tab = this.tabs.getTab(panel.getEl().id);
44842                 if(tab.isHidden()){
44843                     this.tabs.unhideTab(tab.id);
44844                 }
44845                 tab.activate();
44846             }else{
44847                 this.setActivePanel(panel);
44848             }
44849         }
44850         return panel;
44851     },
44852
44853     /**
44854      * Get the active panel for this region.
44855      * @return {Roo.ContentPanel} The active panel or null
44856      */
44857     getActivePanel : function(){
44858         return this.activePanel;
44859     },
44860
44861     validateVisibility : function(){
44862         if(this.panels.getCount() < 1){
44863             this.updateTitle("&#160;");
44864             this.closeBtn.hide();
44865             this.hide();
44866         }else{
44867             if(!this.isVisible()){
44868                 this.show();
44869             }
44870         }
44871     },
44872
44873     /**
44874      * Adds the passed ContentPanel(s) to this region.
44875      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44876      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44877      */
44878     add : function(panel){
44879         if(arguments.length > 1){
44880             for(var i = 0, len = arguments.length; i < len; i++) {
44881                 this.add(arguments[i]);
44882             }
44883             return null;
44884         }
44885         if(this.hasPanel(panel)){
44886             this.showPanel(panel);
44887             return panel;
44888         }
44889         panel.setRegion(this);
44890         this.panels.add(panel);
44891         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44892             this.bodyEl.dom.appendChild(panel.getEl().dom);
44893             if(panel.background !== true){
44894                 this.setActivePanel(panel);
44895             }
44896             this.fireEvent("paneladded", this, panel);
44897             return panel;
44898         }
44899         if(!this.tabs){
44900             this.initTabs();
44901         }else{
44902             this.initPanelAsTab(panel);
44903         }
44904         if(panel.background !== true){
44905             this.tabs.activate(panel.getEl().id);
44906         }
44907         this.fireEvent("paneladded", this, panel);
44908         return panel;
44909     },
44910
44911     /**
44912      * Hides the tab for the specified panel.
44913      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44914      */
44915     hidePanel : function(panel){
44916         if(this.tabs && (panel = this.getPanel(panel))){
44917             this.tabs.hideTab(panel.getEl().id);
44918         }
44919     },
44920
44921     /**
44922      * Unhides the tab for a previously hidden panel.
44923      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44924      */
44925     unhidePanel : function(panel){
44926         if(this.tabs && (panel = this.getPanel(panel))){
44927             this.tabs.unhideTab(panel.getEl().id);
44928         }
44929     },
44930
44931     clearPanels : function(){
44932         while(this.panels.getCount() > 0){
44933              this.remove(this.panels.first());
44934         }
44935     },
44936
44937     /**
44938      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44939      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44940      * @param {Boolean} preservePanel Overrides the config preservePanel option
44941      * @return {Roo.ContentPanel} The panel that was removed
44942      */
44943     remove : function(panel, preservePanel){
44944         panel = this.getPanel(panel);
44945         if(!panel){
44946             return null;
44947         }
44948         var e = {};
44949         this.fireEvent("beforeremove", this, panel, e);
44950         if(e.cancel === true){
44951             return null;
44952         }
44953         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44954         var panelId = panel.getId();
44955         this.panels.removeKey(panelId);
44956         if(preservePanel){
44957             document.body.appendChild(panel.getEl().dom);
44958         }
44959         if(this.tabs){
44960             this.tabs.removeTab(panel.getEl().id);
44961         }else if (!preservePanel){
44962             this.bodyEl.dom.removeChild(panel.getEl().dom);
44963         }
44964         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44965             var p = this.panels.first();
44966             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44967             tempEl.appendChild(p.getEl().dom);
44968             this.bodyEl.update("");
44969             this.bodyEl.dom.appendChild(p.getEl().dom);
44970             tempEl = null;
44971             this.updateTitle(p.getTitle());
44972             this.tabs = null;
44973             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44974             this.setActivePanel(p);
44975         }
44976         panel.setRegion(null);
44977         if(this.activePanel == panel){
44978             this.activePanel = null;
44979         }
44980         if(this.config.autoDestroy !== false && preservePanel !== true){
44981             try{panel.destroy();}catch(e){}
44982         }
44983         this.fireEvent("panelremoved", this, panel);
44984         return panel;
44985     },
44986
44987     /**
44988      * Returns the TabPanel component used by this region
44989      * @return {Roo.TabPanel}
44990      */
44991     getTabs : function(){
44992         return this.tabs;
44993     },
44994
44995     createTool : function(parentEl, className){
44996         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44997             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44998         btn.addClassOnOver("x-layout-tools-button-over");
44999         return btn;
45000     }
45001 });/*
45002  * Based on:
45003  * Ext JS Library 1.1.1
45004  * Copyright(c) 2006-2007, Ext JS, LLC.
45005  *
45006  * Originally Released Under LGPL - original licence link has changed is not relivant.
45007  *
45008  * Fork - LGPL
45009  * <script type="text/javascript">
45010  */
45011  
45012
45013
45014 /**
45015  * @class Roo.SplitLayoutRegion
45016  * @extends Roo.LayoutRegion
45017  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45018  */
45019 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45020     this.cursor = cursor;
45021     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45022 };
45023
45024 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45025     splitTip : "Drag to resize.",
45026     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45027     useSplitTips : false,
45028
45029     applyConfig : function(config){
45030         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45031         if(config.split){
45032             if(!this.split){
45033                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45034                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45035                 /** The SplitBar for this region 
45036                 * @type Roo.SplitBar */
45037                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45038                 this.split.on("moved", this.onSplitMove, this);
45039                 this.split.useShim = config.useShim === true;
45040                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45041                 if(this.useSplitTips){
45042                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45043                 }
45044                 if(config.collapsible){
45045                     this.split.el.on("dblclick", this.collapse,  this);
45046                 }
45047             }
45048             if(typeof config.minSize != "undefined"){
45049                 this.split.minSize = config.minSize;
45050             }
45051             if(typeof config.maxSize != "undefined"){
45052                 this.split.maxSize = config.maxSize;
45053             }
45054             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45055                 this.hideSplitter();
45056             }
45057         }
45058     },
45059
45060     getHMaxSize : function(){
45061          var cmax = this.config.maxSize || 10000;
45062          var center = this.mgr.getRegion("center");
45063          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45064     },
45065
45066     getVMaxSize : function(){
45067          var cmax = this.config.maxSize || 10000;
45068          var center = this.mgr.getRegion("center");
45069          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45070     },
45071
45072     onSplitMove : function(split, newSize){
45073         this.fireEvent("resized", this, newSize);
45074     },
45075     
45076     /** 
45077      * Returns the {@link Roo.SplitBar} for this region.
45078      * @return {Roo.SplitBar}
45079      */
45080     getSplitBar : function(){
45081         return this.split;
45082     },
45083     
45084     hide : function(){
45085         this.hideSplitter();
45086         Roo.SplitLayoutRegion.superclass.hide.call(this);
45087     },
45088
45089     hideSplitter : function(){
45090         if(this.split){
45091             this.split.el.setLocation(-2000,-2000);
45092             this.split.el.hide();
45093         }
45094     },
45095
45096     show : function(){
45097         if(this.split){
45098             this.split.el.show();
45099         }
45100         Roo.SplitLayoutRegion.superclass.show.call(this);
45101     },
45102     
45103     beforeSlide: function(){
45104         if(Roo.isGecko){// firefox overflow auto bug workaround
45105             this.bodyEl.clip();
45106             if(this.tabs) this.tabs.bodyEl.clip();
45107             if(this.activePanel){
45108                 this.activePanel.getEl().clip();
45109                 
45110                 if(this.activePanel.beforeSlide){
45111                     this.activePanel.beforeSlide();
45112                 }
45113             }
45114         }
45115     },
45116     
45117     afterSlide : function(){
45118         if(Roo.isGecko){// firefox overflow auto bug workaround
45119             this.bodyEl.unclip();
45120             if(this.tabs) this.tabs.bodyEl.unclip();
45121             if(this.activePanel){
45122                 this.activePanel.getEl().unclip();
45123                 if(this.activePanel.afterSlide){
45124                     this.activePanel.afterSlide();
45125                 }
45126             }
45127         }
45128     },
45129
45130     initAutoHide : function(){
45131         if(this.autoHide !== false){
45132             if(!this.autoHideHd){
45133                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45134                 this.autoHideHd = {
45135                     "mouseout": function(e){
45136                         if(!e.within(this.el, true)){
45137                             st.delay(500);
45138                         }
45139                     },
45140                     "mouseover" : function(e){
45141                         st.cancel();
45142                     },
45143                     scope : this
45144                 };
45145             }
45146             this.el.on(this.autoHideHd);
45147         }
45148     },
45149
45150     clearAutoHide : function(){
45151         if(this.autoHide !== false){
45152             this.el.un("mouseout", this.autoHideHd.mouseout);
45153             this.el.un("mouseover", this.autoHideHd.mouseover);
45154         }
45155     },
45156
45157     clearMonitor : function(){
45158         Roo.get(document).un("click", this.slideInIf, this);
45159     },
45160
45161     // these names are backwards but not changed for compat
45162     slideOut : function(){
45163         if(this.isSlid || this.el.hasActiveFx()){
45164             return;
45165         }
45166         this.isSlid = true;
45167         if(this.collapseBtn){
45168             this.collapseBtn.hide();
45169         }
45170         this.closeBtnState = this.closeBtn.getStyle('display');
45171         this.closeBtn.hide();
45172         if(this.stickBtn){
45173             this.stickBtn.show();
45174         }
45175         this.el.show();
45176         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45177         this.beforeSlide();
45178         this.el.setStyle("z-index", 10001);
45179         this.el.slideIn(this.getSlideAnchor(), {
45180             callback: function(){
45181                 this.afterSlide();
45182                 this.initAutoHide();
45183                 Roo.get(document).on("click", this.slideInIf, this);
45184                 this.fireEvent("slideshow", this);
45185             },
45186             scope: this,
45187             block: true
45188         });
45189     },
45190
45191     afterSlideIn : function(){
45192         this.clearAutoHide();
45193         this.isSlid = false;
45194         this.clearMonitor();
45195         this.el.setStyle("z-index", "");
45196         if(this.collapseBtn){
45197             this.collapseBtn.show();
45198         }
45199         this.closeBtn.setStyle('display', this.closeBtnState);
45200         if(this.stickBtn){
45201             this.stickBtn.hide();
45202         }
45203         this.fireEvent("slidehide", this);
45204     },
45205
45206     slideIn : function(cb){
45207         if(!this.isSlid || this.el.hasActiveFx()){
45208             Roo.callback(cb);
45209             return;
45210         }
45211         this.isSlid = false;
45212         this.beforeSlide();
45213         this.el.slideOut(this.getSlideAnchor(), {
45214             callback: function(){
45215                 this.el.setLeftTop(-10000, -10000);
45216                 this.afterSlide();
45217                 this.afterSlideIn();
45218                 Roo.callback(cb);
45219             },
45220             scope: this,
45221             block: true
45222         });
45223     },
45224     
45225     slideInIf : function(e){
45226         if(!e.within(this.el)){
45227             this.slideIn();
45228         }
45229     },
45230
45231     animateCollapse : function(){
45232         this.beforeSlide();
45233         this.el.setStyle("z-index", 20000);
45234         var anchor = this.getSlideAnchor();
45235         this.el.slideOut(anchor, {
45236             callback : function(){
45237                 this.el.setStyle("z-index", "");
45238                 this.collapsedEl.slideIn(anchor, {duration:.3});
45239                 this.afterSlide();
45240                 this.el.setLocation(-10000,-10000);
45241                 this.el.hide();
45242                 this.fireEvent("collapsed", this);
45243             },
45244             scope: this,
45245             block: true
45246         });
45247     },
45248
45249     animateExpand : function(){
45250         this.beforeSlide();
45251         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45252         this.el.setStyle("z-index", 20000);
45253         this.collapsedEl.hide({
45254             duration:.1
45255         });
45256         this.el.slideIn(this.getSlideAnchor(), {
45257             callback : function(){
45258                 this.el.setStyle("z-index", "");
45259                 this.afterSlide();
45260                 if(this.split){
45261                     this.split.el.show();
45262                 }
45263                 this.fireEvent("invalidated", this);
45264                 this.fireEvent("expanded", this);
45265             },
45266             scope: this,
45267             block: true
45268         });
45269     },
45270
45271     anchors : {
45272         "west" : "left",
45273         "east" : "right",
45274         "north" : "top",
45275         "south" : "bottom"
45276     },
45277
45278     sanchors : {
45279         "west" : "l",
45280         "east" : "r",
45281         "north" : "t",
45282         "south" : "b"
45283     },
45284
45285     canchors : {
45286         "west" : "tl-tr",
45287         "east" : "tr-tl",
45288         "north" : "tl-bl",
45289         "south" : "bl-tl"
45290     },
45291
45292     getAnchor : function(){
45293         return this.anchors[this.position];
45294     },
45295
45296     getCollapseAnchor : function(){
45297         return this.canchors[this.position];
45298     },
45299
45300     getSlideAnchor : function(){
45301         return this.sanchors[this.position];
45302     },
45303
45304     getAlignAdj : function(){
45305         var cm = this.cmargins;
45306         switch(this.position){
45307             case "west":
45308                 return [0, 0];
45309             break;
45310             case "east":
45311                 return [0, 0];
45312             break;
45313             case "north":
45314                 return [0, 0];
45315             break;
45316             case "south":
45317                 return [0, 0];
45318             break;
45319         }
45320     },
45321
45322     getExpandAdj : function(){
45323         var c = this.collapsedEl, cm = this.cmargins;
45324         switch(this.position){
45325             case "west":
45326                 return [-(cm.right+c.getWidth()+cm.left), 0];
45327             break;
45328             case "east":
45329                 return [cm.right+c.getWidth()+cm.left, 0];
45330             break;
45331             case "north":
45332                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45333             break;
45334             case "south":
45335                 return [0, cm.top+cm.bottom+c.getHeight()];
45336             break;
45337         }
45338     }
45339 });/*
45340  * Based on:
45341  * Ext JS Library 1.1.1
45342  * Copyright(c) 2006-2007, Ext JS, LLC.
45343  *
45344  * Originally Released Under LGPL - original licence link has changed is not relivant.
45345  *
45346  * Fork - LGPL
45347  * <script type="text/javascript">
45348  */
45349 /*
45350  * These classes are private internal classes
45351  */
45352 Roo.CenterLayoutRegion = function(mgr, config){
45353     Roo.LayoutRegion.call(this, mgr, config, "center");
45354     this.visible = true;
45355     this.minWidth = config.minWidth || 20;
45356     this.minHeight = config.minHeight || 20;
45357 };
45358
45359 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45360     hide : function(){
45361         // center panel can't be hidden
45362     },
45363     
45364     show : function(){
45365         // center panel can't be hidden
45366     },
45367     
45368     getMinWidth: function(){
45369         return this.minWidth;
45370     },
45371     
45372     getMinHeight: function(){
45373         return this.minHeight;
45374     }
45375 });
45376
45377
45378 Roo.NorthLayoutRegion = function(mgr, config){
45379     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45380     if(this.split){
45381         this.split.placement = Roo.SplitBar.TOP;
45382         this.split.orientation = Roo.SplitBar.VERTICAL;
45383         this.split.el.addClass("x-layout-split-v");
45384     }
45385     var size = config.initialSize || config.height;
45386     if(typeof size != "undefined"){
45387         this.el.setHeight(size);
45388     }
45389 };
45390 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45391     orientation: Roo.SplitBar.VERTICAL,
45392     getBox : function(){
45393         if(this.collapsed){
45394             return this.collapsedEl.getBox();
45395         }
45396         var box = this.el.getBox();
45397         if(this.split){
45398             box.height += this.split.el.getHeight();
45399         }
45400         return box;
45401     },
45402     
45403     updateBox : function(box){
45404         if(this.split && !this.collapsed){
45405             box.height -= this.split.el.getHeight();
45406             this.split.el.setLeft(box.x);
45407             this.split.el.setTop(box.y+box.height);
45408             this.split.el.setWidth(box.width);
45409         }
45410         if(this.collapsed){
45411             this.updateBody(box.width, null);
45412         }
45413         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45414     }
45415 });
45416
45417 Roo.SouthLayoutRegion = function(mgr, config){
45418     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45419     if(this.split){
45420         this.split.placement = Roo.SplitBar.BOTTOM;
45421         this.split.orientation = Roo.SplitBar.VERTICAL;
45422         this.split.el.addClass("x-layout-split-v");
45423     }
45424     var size = config.initialSize || config.height;
45425     if(typeof size != "undefined"){
45426         this.el.setHeight(size);
45427     }
45428 };
45429 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45430     orientation: Roo.SplitBar.VERTICAL,
45431     getBox : function(){
45432         if(this.collapsed){
45433             return this.collapsedEl.getBox();
45434         }
45435         var box = this.el.getBox();
45436         if(this.split){
45437             var sh = this.split.el.getHeight();
45438             box.height += sh;
45439             box.y -= sh;
45440         }
45441         return box;
45442     },
45443     
45444     updateBox : function(box){
45445         if(this.split && !this.collapsed){
45446             var sh = this.split.el.getHeight();
45447             box.height -= sh;
45448             box.y += sh;
45449             this.split.el.setLeft(box.x);
45450             this.split.el.setTop(box.y-sh);
45451             this.split.el.setWidth(box.width);
45452         }
45453         if(this.collapsed){
45454             this.updateBody(box.width, null);
45455         }
45456         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45457     }
45458 });
45459
45460 Roo.EastLayoutRegion = function(mgr, config){
45461     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45462     if(this.split){
45463         this.split.placement = Roo.SplitBar.RIGHT;
45464         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45465         this.split.el.addClass("x-layout-split-h");
45466     }
45467     var size = config.initialSize || config.width;
45468     if(typeof size != "undefined"){
45469         this.el.setWidth(size);
45470     }
45471 };
45472 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45473     orientation: Roo.SplitBar.HORIZONTAL,
45474     getBox : function(){
45475         if(this.collapsed){
45476             return this.collapsedEl.getBox();
45477         }
45478         var box = this.el.getBox();
45479         if(this.split){
45480             var sw = this.split.el.getWidth();
45481             box.width += sw;
45482             box.x -= sw;
45483         }
45484         return box;
45485     },
45486
45487     updateBox : function(box){
45488         if(this.split && !this.collapsed){
45489             var sw = this.split.el.getWidth();
45490             box.width -= sw;
45491             this.split.el.setLeft(box.x);
45492             this.split.el.setTop(box.y);
45493             this.split.el.setHeight(box.height);
45494             box.x += sw;
45495         }
45496         if(this.collapsed){
45497             this.updateBody(null, box.height);
45498         }
45499         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45500     }
45501 });
45502
45503 Roo.WestLayoutRegion = function(mgr, config){
45504     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45505     if(this.split){
45506         this.split.placement = Roo.SplitBar.LEFT;
45507         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45508         this.split.el.addClass("x-layout-split-h");
45509     }
45510     var size = config.initialSize || config.width;
45511     if(typeof size != "undefined"){
45512         this.el.setWidth(size);
45513     }
45514 };
45515 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45516     orientation: Roo.SplitBar.HORIZONTAL,
45517     getBox : function(){
45518         if(this.collapsed){
45519             return this.collapsedEl.getBox();
45520         }
45521         var box = this.el.getBox();
45522         if(this.split){
45523             box.width += this.split.el.getWidth();
45524         }
45525         return box;
45526     },
45527     
45528     updateBox : function(box){
45529         if(this.split && !this.collapsed){
45530             var sw = this.split.el.getWidth();
45531             box.width -= sw;
45532             this.split.el.setLeft(box.x+box.width);
45533             this.split.el.setTop(box.y);
45534             this.split.el.setHeight(box.height);
45535         }
45536         if(this.collapsed){
45537             this.updateBody(null, box.height);
45538         }
45539         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45540     }
45541 });
45542 /*
45543  * Based on:
45544  * Ext JS Library 1.1.1
45545  * Copyright(c) 2006-2007, Ext JS, LLC.
45546  *
45547  * Originally Released Under LGPL - original licence link has changed is not relivant.
45548  *
45549  * Fork - LGPL
45550  * <script type="text/javascript">
45551  */
45552  
45553  
45554 /*
45555  * Private internal class for reading and applying state
45556  */
45557 Roo.LayoutStateManager = function(layout){
45558      // default empty state
45559      this.state = {
45560         north: {},
45561         south: {},
45562         east: {},
45563         west: {}       
45564     };
45565 };
45566
45567 Roo.LayoutStateManager.prototype = {
45568     init : function(layout, provider){
45569         this.provider = provider;
45570         var state = provider.get(layout.id+"-layout-state");
45571         if(state){
45572             var wasUpdating = layout.isUpdating();
45573             if(!wasUpdating){
45574                 layout.beginUpdate();
45575             }
45576             for(var key in state){
45577                 if(typeof state[key] != "function"){
45578                     var rstate = state[key];
45579                     var r = layout.getRegion(key);
45580                     if(r && rstate){
45581                         if(rstate.size){
45582                             r.resizeTo(rstate.size);
45583                         }
45584                         if(rstate.collapsed == true){
45585                             r.collapse(true);
45586                         }else{
45587                             r.expand(null, true);
45588                         }
45589                     }
45590                 }
45591             }
45592             if(!wasUpdating){
45593                 layout.endUpdate();
45594             }
45595             this.state = state; 
45596         }
45597         this.layout = layout;
45598         layout.on("regionresized", this.onRegionResized, this);
45599         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45600         layout.on("regionexpanded", this.onRegionExpanded, this);
45601     },
45602     
45603     storeState : function(){
45604         this.provider.set(this.layout.id+"-layout-state", this.state);
45605     },
45606     
45607     onRegionResized : function(region, newSize){
45608         this.state[region.getPosition()].size = newSize;
45609         this.storeState();
45610     },
45611     
45612     onRegionCollapsed : function(region){
45613         this.state[region.getPosition()].collapsed = true;
45614         this.storeState();
45615     },
45616     
45617     onRegionExpanded : function(region){
45618         this.state[region.getPosition()].collapsed = false;
45619         this.storeState();
45620     }
45621 };/*
45622  * Based on:
45623  * Ext JS Library 1.1.1
45624  * Copyright(c) 2006-2007, Ext JS, LLC.
45625  *
45626  * Originally Released Under LGPL - original licence link has changed is not relivant.
45627  *
45628  * Fork - LGPL
45629  * <script type="text/javascript">
45630  */
45631 /**
45632  * @class Roo.ContentPanel
45633  * @extends Roo.util.Observable
45634  * A basic ContentPanel element.
45635  * @cfg {Boolean} fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45636  * @cfg {Boolean} fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45637  * @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
45638  * @cfg {Boolean} closable      True if the panel can be closed/removed
45639  * @cfg {Boolean} background    True if the panel should not be activated when it is added (defaults to false)
45640  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45641  * @cfg {Toolbar} toolbar       A toolbar for this panel
45642  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45643  * @cfg {String} title          The title for this panel
45644  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45645  * @cfg {String} url            Calls {@link #setUrl} with this value
45646  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45647  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45648  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45649  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
45650
45651  * @constructor
45652  * Create a new ContentPanel.
45653  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45654  * @param {String/Object} config A string to set only the title or a config object
45655  * @param {String} content (optional) Set the HTML content for this panel
45656  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45657  */
45658 Roo.ContentPanel = function(el, config, content){
45659     
45660      
45661     /*
45662     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45663         config = el;
45664         el = Roo.id();
45665     }
45666     if (config && config.parentLayout) { 
45667         el = config.parentLayout.el.createChild(); 
45668     }
45669     */
45670     if(el.autoCreate){ // xtype is available if this is called from factory
45671         config = el;
45672         el = Roo.id();
45673     }
45674     this.el = Roo.get(el);
45675     if(!this.el && config && config.autoCreate){
45676         if(typeof config.autoCreate == "object"){
45677             if(!config.autoCreate.id){
45678                 config.autoCreate.id = config.id||el;
45679             }
45680             this.el = Roo.DomHelper.append(document.body,
45681                         config.autoCreate, true);
45682         }else{
45683             this.el = Roo.DomHelper.append(document.body,
45684                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45685         }
45686     }
45687     this.closable = false;
45688     this.loaded = false;
45689     this.active = false;
45690     if(typeof config == "string"){
45691         this.title = config;
45692     }else{
45693         Roo.apply(this, config);
45694     }
45695     
45696     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45697         this.wrapEl = this.el.wrap();    
45698         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45699         
45700     }
45701     
45702     
45703     
45704     if(this.resizeEl){
45705         this.resizeEl = Roo.get(this.resizeEl, true);
45706     }else{
45707         this.resizeEl = this.el;
45708     }
45709     this.addEvents({
45710         /**
45711          * @event activate
45712          * Fires when this panel is activated. 
45713          * @param {Roo.ContentPanel} this
45714          */
45715         "activate" : true,
45716         /**
45717          * @event deactivate
45718          * Fires when this panel is activated. 
45719          * @param {Roo.ContentPanel} this
45720          */
45721         "deactivate" : true,
45722
45723         /**
45724          * @event resize
45725          * Fires when this panel is resized if fitToFrame is true.
45726          * @param {Roo.ContentPanel} this
45727          * @param {Number} width The width after any component adjustments
45728          * @param {Number} height The height after any component adjustments
45729          */
45730         "resize" : true
45731     });
45732     if(this.autoScroll){
45733         this.resizeEl.setStyle("overflow", "auto");
45734     } else {
45735         // fix randome scrolling
45736         this.el.on('scroll', function() {
45737             Roo.log('fix random scolling');
45738             this.scrollTo('top',0); 
45739         });
45740     }
45741     content = content || this.content;
45742     if(content){
45743         this.setContent(content);
45744     }
45745     if(config && config.url){
45746         this.setUrl(this.url, this.params, this.loadOnce);
45747     }
45748     
45749     
45750     
45751     Roo.ContentPanel.superclass.constructor.call(this);
45752 };
45753
45754 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45755     tabTip:'',
45756     setRegion : function(region){
45757         this.region = region;
45758         if(region){
45759            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45760         }else{
45761            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45762         } 
45763     },
45764     
45765     /**
45766      * Returns the toolbar for this Panel if one was configured. 
45767      * @return {Roo.Toolbar} 
45768      */
45769     getToolbar : function(){
45770         return this.toolbar;
45771     },
45772     
45773     setActiveState : function(active){
45774         this.active = active;
45775         if(!active){
45776             this.fireEvent("deactivate", this);
45777         }else{
45778             this.fireEvent("activate", this);
45779         }
45780     },
45781     /**
45782      * Updates this panel's element
45783      * @param {String} content The new content
45784      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45785     */
45786     setContent : function(content, loadScripts){
45787         this.el.update(content, loadScripts);
45788     },
45789
45790     ignoreResize : function(w, h){
45791         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45792             return true;
45793         }else{
45794             this.lastSize = {width: w, height: h};
45795             return false;
45796         }
45797     },
45798     /**
45799      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45800      * @return {Roo.UpdateManager} The UpdateManager
45801      */
45802     getUpdateManager : function(){
45803         return this.el.getUpdateManager();
45804     },
45805      /**
45806      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45807      * @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:
45808 <pre><code>
45809 panel.load({
45810     url: "your-url.php",
45811     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45812     callback: yourFunction,
45813     scope: yourObject, //(optional scope)
45814     discardUrl: false,
45815     nocache: false,
45816     text: "Loading...",
45817     timeout: 30,
45818     scripts: false
45819 });
45820 </code></pre>
45821      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45822      * 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.
45823      * @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}
45824      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45825      * @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.
45826      * @return {Roo.ContentPanel} this
45827      */
45828     load : function(){
45829         var um = this.el.getUpdateManager();
45830         um.update.apply(um, arguments);
45831         return this;
45832     },
45833
45834
45835     /**
45836      * 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.
45837      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45838      * @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)
45839      * @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)
45840      * @return {Roo.UpdateManager} The UpdateManager
45841      */
45842     setUrl : function(url, params, loadOnce){
45843         if(this.refreshDelegate){
45844             this.removeListener("activate", this.refreshDelegate);
45845         }
45846         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45847         this.on("activate", this.refreshDelegate);
45848         return this.el.getUpdateManager();
45849     },
45850     
45851     _handleRefresh : function(url, params, loadOnce){
45852         if(!loadOnce || !this.loaded){
45853             var updater = this.el.getUpdateManager();
45854             updater.update(url, params, this._setLoaded.createDelegate(this));
45855         }
45856     },
45857     
45858     _setLoaded : function(){
45859         this.loaded = true;
45860     }, 
45861     
45862     /**
45863      * Returns this panel's id
45864      * @return {String} 
45865      */
45866     getId : function(){
45867         return this.el.id;
45868     },
45869     
45870     /** 
45871      * Returns this panel's element - used by regiosn to add.
45872      * @return {Roo.Element} 
45873      */
45874     getEl : function(){
45875         return this.wrapEl || this.el;
45876     },
45877     
45878     adjustForComponents : function(width, height){
45879         if(this.resizeEl != this.el){
45880             width -= this.el.getFrameWidth('lr');
45881             height -= this.el.getFrameWidth('tb');
45882         }
45883         if(this.toolbar){
45884             var te = this.toolbar.getEl();
45885             height -= te.getHeight();
45886             te.setWidth(width);
45887         }
45888         if(this.adjustments){
45889             width += this.adjustments[0];
45890             height += this.adjustments[1];
45891         }
45892         return {"width": width, "height": height};
45893     },
45894     
45895     setSize : function(width, height){
45896         if(this.fitToFrame && !this.ignoreResize(width, height)){
45897             if(this.fitContainer && this.resizeEl != this.el){
45898                 this.el.setSize(width, height);
45899             }
45900             var size = this.adjustForComponents(width, height);
45901             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45902             this.fireEvent('resize', this, size.width, size.height);
45903         }
45904     },
45905     
45906     /**
45907      * Returns this panel's title
45908      * @return {String} 
45909      */
45910     getTitle : function(){
45911         return this.title;
45912     },
45913     
45914     /**
45915      * Set this panel's title
45916      * @param {String} title
45917      */
45918     setTitle : function(title){
45919         this.title = title;
45920         if(this.region){
45921             this.region.updatePanelTitle(this, title);
45922         }
45923     },
45924     
45925     /**
45926      * Returns true is this panel was configured to be closable
45927      * @return {Boolean} 
45928      */
45929     isClosable : function(){
45930         return this.closable;
45931     },
45932     
45933     beforeSlide : function(){
45934         this.el.clip();
45935         this.resizeEl.clip();
45936     },
45937     
45938     afterSlide : function(){
45939         this.el.unclip();
45940         this.resizeEl.unclip();
45941     },
45942     
45943     /**
45944      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45945      *   Will fail silently if the {@link #setUrl} method has not been called.
45946      *   This does not activate the panel, just updates its content.
45947      */
45948     refresh : function(){
45949         if(this.refreshDelegate){
45950            this.loaded = false;
45951            this.refreshDelegate();
45952         }
45953     },
45954     
45955     /**
45956      * Destroys this panel
45957      */
45958     destroy : function(){
45959         this.el.removeAllListeners();
45960         var tempEl = document.createElement("span");
45961         tempEl.appendChild(this.el.dom);
45962         tempEl.innerHTML = "";
45963         this.el.remove();
45964         this.el = null;
45965     },
45966     
45967       /**
45968      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45969      * <pre><code>
45970
45971 layout.addxtype({
45972        xtype : 'Form',
45973        items: [ .... ]
45974    }
45975 );
45976
45977 </code></pre>
45978      * @param {Object} cfg Xtype definition of item to add.
45979      */
45980     
45981     addxtype : function(cfg) {
45982         // add form..
45983         if (cfg.xtype.match(/^Form$/)) {
45984             var el = this.el.createChild();
45985
45986             this.form = new  Roo.form.Form(cfg);
45987             
45988             
45989             if ( this.form.allItems.length) this.form.render(el.dom);
45990             return this.form;
45991         }
45992         
45993         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45994             // views..
45995             cfg.el = this.el.appendChild(document.createElement("div"));
45996             // factory?
45997             var ret = new Roo[cfg.xtype](cfg);
45998             ret.render && ret.render(false, ''); // render blank..
45999             return ret;
46000             
46001         }
46002         return false;
46003         
46004     }
46005 });
46006
46007 /**
46008  * @class Roo.GridPanel
46009  * @extends Roo.ContentPanel
46010  * @constructor
46011  * Create a new GridPanel.
46012  * @param {Roo.grid.Grid} grid The grid for this panel
46013  * @param {String/Object} config A string to set only the panel's title, or a config object
46014  */
46015 Roo.GridPanel = function(grid, config){
46016     
46017   
46018     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46019         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46020         
46021     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46022     
46023     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46024     
46025     if(this.toolbar){
46026         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46027     }
46028     // xtype created footer. - not sure if will work as we normally have to render first..
46029     if (this.footer && !this.footer.el && this.footer.xtype) {
46030         
46031         this.footer.container = this.grid.getView().getFooterPanel(true);
46032         this.footer.dataSource = this.grid.dataSource;
46033         this.footer = Roo.factory(this.footer, Roo);
46034         
46035     }
46036     
46037     grid.monitorWindowResize = false; // turn off autosizing
46038     grid.autoHeight = false;
46039     grid.autoWidth = false;
46040     this.grid = grid;
46041     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46042 };
46043
46044 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46045     getId : function(){
46046         return this.grid.id;
46047     },
46048     
46049     /**
46050      * Returns the grid for this panel
46051      * @return {Roo.grid.Grid} 
46052      */
46053     getGrid : function(){
46054         return this.grid;    
46055     },
46056     
46057     setSize : function(width, height){
46058         if(!this.ignoreResize(width, height)){
46059             var grid = this.grid;
46060             var size = this.adjustForComponents(width, height);
46061             grid.getGridEl().setSize(size.width, size.height);
46062             grid.autoSize();
46063         }
46064     },
46065     
46066     beforeSlide : function(){
46067         this.grid.getView().scroller.clip();
46068     },
46069     
46070     afterSlide : function(){
46071         this.grid.getView().scroller.unclip();
46072     },
46073     
46074     destroy : function(){
46075         this.grid.destroy();
46076         delete this.grid;
46077         Roo.GridPanel.superclass.destroy.call(this); 
46078     }
46079 });
46080
46081
46082 /**
46083  * @class Roo.NestedLayoutPanel
46084  * @extends Roo.ContentPanel
46085  * @constructor
46086  * Create a new NestedLayoutPanel.
46087  * 
46088  * 
46089  * @param {Roo.BorderLayout} layout The layout for this panel
46090  * @param {String/Object} config A string to set only the title or a config object
46091  */
46092 Roo.NestedLayoutPanel = function(layout, config)
46093 {
46094     // construct with only one argument..
46095     /* FIXME - implement nicer consturctors
46096     if (layout.layout) {
46097         config = layout;
46098         layout = config.layout;
46099         delete config.layout;
46100     }
46101     if (layout.xtype && !layout.getEl) {
46102         // then layout needs constructing..
46103         layout = Roo.factory(layout, Roo);
46104     }
46105     */
46106     
46107     
46108     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46109     
46110     layout.monitorWindowResize = false; // turn off autosizing
46111     this.layout = layout;
46112     this.layout.getEl().addClass("x-layout-nested-layout");
46113     
46114     
46115     
46116     
46117 };
46118
46119 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46120
46121     setSize : function(width, height){
46122         if(!this.ignoreResize(width, height)){
46123             var size = this.adjustForComponents(width, height);
46124             var el = this.layout.getEl();
46125             el.setSize(size.width, size.height);
46126             var touch = el.dom.offsetWidth;
46127             this.layout.layout();
46128             // ie requires a double layout on the first pass
46129             if(Roo.isIE && !this.initialized){
46130                 this.initialized = true;
46131                 this.layout.layout();
46132             }
46133         }
46134     },
46135     
46136     // activate all subpanels if not currently active..
46137     
46138     setActiveState : function(active){
46139         this.active = active;
46140         if(!active){
46141             this.fireEvent("deactivate", this);
46142             return;
46143         }
46144         
46145         this.fireEvent("activate", this);
46146         // not sure if this should happen before or after..
46147         if (!this.layout) {
46148             return; // should not happen..
46149         }
46150         var reg = false;
46151         for (var r in this.layout.regions) {
46152             reg = this.layout.getRegion(r);
46153             if (reg.getActivePanel()) {
46154                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46155                 reg.setActivePanel(reg.getActivePanel());
46156                 continue;
46157             }
46158             if (!reg.panels.length) {
46159                 continue;
46160             }
46161             reg.showPanel(reg.getPanel(0));
46162         }
46163         
46164         
46165         
46166         
46167     },
46168     
46169     /**
46170      * Returns the nested BorderLayout for this panel
46171      * @return {Roo.BorderLayout} 
46172      */
46173     getLayout : function(){
46174         return this.layout;
46175     },
46176     
46177      /**
46178      * Adds a xtype elements to the layout of the nested panel
46179      * <pre><code>
46180
46181 panel.addxtype({
46182        xtype : 'ContentPanel',
46183        region: 'west',
46184        items: [ .... ]
46185    }
46186 );
46187
46188 panel.addxtype({
46189         xtype : 'NestedLayoutPanel',
46190         region: 'west',
46191         layout: {
46192            center: { },
46193            west: { }   
46194         },
46195         items : [ ... list of content panels or nested layout panels.. ]
46196    }
46197 );
46198 </code></pre>
46199      * @param {Object} cfg Xtype definition of item to add.
46200      */
46201     addxtype : function(cfg) {
46202         return this.layout.addxtype(cfg);
46203     
46204     }
46205 });
46206
46207 Roo.ScrollPanel = function(el, config, content){
46208     config = config || {};
46209     config.fitToFrame = true;
46210     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46211     
46212     this.el.dom.style.overflow = "hidden";
46213     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46214     this.el.removeClass("x-layout-inactive-content");
46215     this.el.on("mousewheel", this.onWheel, this);
46216
46217     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46218     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46219     up.unselectable(); down.unselectable();
46220     up.on("click", this.scrollUp, this);
46221     down.on("click", this.scrollDown, this);
46222     up.addClassOnOver("x-scroller-btn-over");
46223     down.addClassOnOver("x-scroller-btn-over");
46224     up.addClassOnClick("x-scroller-btn-click");
46225     down.addClassOnClick("x-scroller-btn-click");
46226     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46227
46228     this.resizeEl = this.el;
46229     this.el = wrap; this.up = up; this.down = down;
46230 };
46231
46232 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46233     increment : 100,
46234     wheelIncrement : 5,
46235     scrollUp : function(){
46236         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46237     },
46238
46239     scrollDown : function(){
46240         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46241     },
46242
46243     afterScroll : function(){
46244         var el = this.resizeEl;
46245         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46246         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46247         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46248     },
46249
46250     setSize : function(){
46251         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46252         this.afterScroll();
46253     },
46254
46255     onWheel : function(e){
46256         var d = e.getWheelDelta();
46257         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46258         this.afterScroll();
46259         e.stopEvent();
46260     },
46261
46262     setContent : function(content, loadScripts){
46263         this.resizeEl.update(content, loadScripts);
46264     }
46265
46266 });
46267
46268
46269
46270
46271
46272
46273
46274
46275
46276 /**
46277  * @class Roo.TreePanel
46278  * @extends Roo.ContentPanel
46279  * @constructor
46280  * Create a new TreePanel. - defaults to fit/scoll contents.
46281  * @param {String/Object} config A string to set only the panel's title, or a config object
46282  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46283  */
46284 Roo.TreePanel = function(config){
46285     var el = config.el;
46286     var tree = config.tree;
46287     delete config.tree; 
46288     delete config.el; // hopefull!
46289     
46290     // wrapper for IE7 strict & safari scroll issue
46291     
46292     var treeEl = el.createChild();
46293     config.resizeEl = treeEl;
46294     
46295     
46296     
46297     Roo.TreePanel.superclass.constructor.call(this, el, config);
46298  
46299  
46300     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46301     //console.log(tree);
46302     this.on('activate', function()
46303     {
46304         if (this.tree.rendered) {
46305             return;
46306         }
46307         //console.log('render tree');
46308         this.tree.render();
46309     });
46310     
46311     this.on('resize',  function (cp, w, h) {
46312             this.tree.innerCt.setWidth(w);
46313             this.tree.innerCt.setHeight(h);
46314             this.tree.innerCt.setStyle('overflow-y', 'auto');
46315     });
46316
46317         
46318     
46319 };
46320
46321 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46322     fitToFrame : true,
46323     autoScroll : true
46324 });
46325
46326
46327
46328
46329
46330
46331
46332
46333
46334
46335
46336 /*
46337  * Based on:
46338  * Ext JS Library 1.1.1
46339  * Copyright(c) 2006-2007, Ext JS, LLC.
46340  *
46341  * Originally Released Under LGPL - original licence link has changed is not relivant.
46342  *
46343  * Fork - LGPL
46344  * <script type="text/javascript">
46345  */
46346  
46347
46348 /**
46349  * @class Roo.ReaderLayout
46350  * @extends Roo.BorderLayout
46351  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46352  * center region containing two nested regions (a top one for a list view and one for item preview below),
46353  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46354  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46355  * expedites the setup of the overall layout and regions for this common application style.
46356  * Example:
46357  <pre><code>
46358 var reader = new Roo.ReaderLayout();
46359 var CP = Roo.ContentPanel;  // shortcut for adding
46360
46361 reader.beginUpdate();
46362 reader.add("north", new CP("north", "North"));
46363 reader.add("west", new CP("west", {title: "West"}));
46364 reader.add("east", new CP("east", {title: "East"}));
46365
46366 reader.regions.listView.add(new CP("listView", "List"));
46367 reader.regions.preview.add(new CP("preview", "Preview"));
46368 reader.endUpdate();
46369 </code></pre>
46370 * @constructor
46371 * Create a new ReaderLayout
46372 * @param {Object} config Configuration options
46373 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46374 * document.body if omitted)
46375 */
46376 Roo.ReaderLayout = function(config, renderTo){
46377     var c = config || {size:{}};
46378     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46379         north: c.north !== false ? Roo.apply({
46380             split:false,
46381             initialSize: 32,
46382             titlebar: false
46383         }, c.north) : false,
46384         west: c.west !== false ? Roo.apply({
46385             split:true,
46386             initialSize: 200,
46387             minSize: 175,
46388             maxSize: 400,
46389             titlebar: true,
46390             collapsible: true,
46391             animate: true,
46392             margins:{left:5,right:0,bottom:5,top:5},
46393             cmargins:{left:5,right:5,bottom:5,top:5}
46394         }, c.west) : false,
46395         east: c.east !== false ? Roo.apply({
46396             split:true,
46397             initialSize: 200,
46398             minSize: 175,
46399             maxSize: 400,
46400             titlebar: true,
46401             collapsible: true,
46402             animate: true,
46403             margins:{left:0,right:5,bottom:5,top:5},
46404             cmargins:{left:5,right:5,bottom:5,top:5}
46405         }, c.east) : false,
46406         center: Roo.apply({
46407             tabPosition: 'top',
46408             autoScroll:false,
46409             closeOnTab: true,
46410             titlebar:false,
46411             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46412         }, c.center)
46413     });
46414
46415     this.el.addClass('x-reader');
46416
46417     this.beginUpdate();
46418
46419     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46420         south: c.preview !== false ? Roo.apply({
46421             split:true,
46422             initialSize: 200,
46423             minSize: 100,
46424             autoScroll:true,
46425             collapsible:true,
46426             titlebar: true,
46427             cmargins:{top:5,left:0, right:0, bottom:0}
46428         }, c.preview) : false,
46429         center: Roo.apply({
46430             autoScroll:false,
46431             titlebar:false,
46432             minHeight:200
46433         }, c.listView)
46434     });
46435     this.add('center', new Roo.NestedLayoutPanel(inner,
46436             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46437
46438     this.endUpdate();
46439
46440     this.regions.preview = inner.getRegion('south');
46441     this.regions.listView = inner.getRegion('center');
46442 };
46443
46444 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46445  * Based on:
46446  * Ext JS Library 1.1.1
46447  * Copyright(c) 2006-2007, Ext JS, LLC.
46448  *
46449  * Originally Released Under LGPL - original licence link has changed is not relivant.
46450  *
46451  * Fork - LGPL
46452  * <script type="text/javascript">
46453  */
46454  
46455 /**
46456  * @class Roo.grid.Grid
46457  * @extends Roo.util.Observable
46458  * This class represents the primary interface of a component based grid control.
46459  * <br><br>Usage:<pre><code>
46460  var grid = new Roo.grid.Grid("my-container-id", {
46461      ds: myDataStore,
46462      cm: myColModel,
46463      selModel: mySelectionModel,
46464      autoSizeColumns: true,
46465      monitorWindowResize: false,
46466      trackMouseOver: true
46467  });
46468  // set any options
46469  grid.render();
46470  * </code></pre>
46471  * <b>Common Problems:</b><br/>
46472  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46473  * element will correct this<br/>
46474  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46475  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46476  * are unpredictable.<br/>
46477  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46478  * grid to calculate dimensions/offsets.<br/>
46479   * @constructor
46480  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46481  * The container MUST have some type of size defined for the grid to fill. The container will be
46482  * automatically set to position relative if it isn't already.
46483  * @param {Object} config A config object that sets properties on this grid.
46484  */
46485 Roo.grid.Grid = function(container, config){
46486         // initialize the container
46487         this.container = Roo.get(container);
46488         this.container.update("");
46489         this.container.setStyle("overflow", "hidden");
46490     this.container.addClass('x-grid-container');
46491
46492     this.id = this.container.id;
46493
46494     Roo.apply(this, config);
46495     // check and correct shorthanded configs
46496     if(this.ds){
46497         this.dataSource = this.ds;
46498         delete this.ds;
46499     }
46500     if(this.cm){
46501         this.colModel = this.cm;
46502         delete this.cm;
46503     }
46504     if(this.sm){
46505         this.selModel = this.sm;
46506         delete this.sm;
46507     }
46508
46509     if (this.selModel) {
46510         this.selModel = Roo.factory(this.selModel, Roo.grid);
46511         this.sm = this.selModel;
46512         this.sm.xmodule = this.xmodule || false;
46513     }
46514     if (typeof(this.colModel.config) == 'undefined') {
46515         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46516         this.cm = this.colModel;
46517         this.cm.xmodule = this.xmodule || false;
46518     }
46519     if (this.dataSource) {
46520         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46521         this.ds = this.dataSource;
46522         this.ds.xmodule = this.xmodule || false;
46523          
46524     }
46525     
46526     
46527     
46528     if(this.width){
46529         this.container.setWidth(this.width);
46530     }
46531
46532     if(this.height){
46533         this.container.setHeight(this.height);
46534     }
46535     /** @private */
46536         this.addEvents({
46537         // raw events
46538         /**
46539          * @event click
46540          * The raw click event for the entire grid.
46541          * @param {Roo.EventObject} e
46542          */
46543         "click" : true,
46544         /**
46545          * @event dblclick
46546          * The raw dblclick event for the entire grid.
46547          * @param {Roo.EventObject} e
46548          */
46549         "dblclick" : true,
46550         /**
46551          * @event contextmenu
46552          * The raw contextmenu event for the entire grid.
46553          * @param {Roo.EventObject} e
46554          */
46555         "contextmenu" : true,
46556         /**
46557          * @event mousedown
46558          * The raw mousedown event for the entire grid.
46559          * @param {Roo.EventObject} e
46560          */
46561         "mousedown" : true,
46562         /**
46563          * @event mouseup
46564          * The raw mouseup event for the entire grid.
46565          * @param {Roo.EventObject} e
46566          */
46567         "mouseup" : true,
46568         /**
46569          * @event mouseover
46570          * The raw mouseover event for the entire grid.
46571          * @param {Roo.EventObject} e
46572          */
46573         "mouseover" : true,
46574         /**
46575          * @event mouseout
46576          * The raw mouseout event for the entire grid.
46577          * @param {Roo.EventObject} e
46578          */
46579         "mouseout" : true,
46580         /**
46581          * @event keypress
46582          * The raw keypress event for the entire grid.
46583          * @param {Roo.EventObject} e
46584          */
46585         "keypress" : true,
46586         /**
46587          * @event keydown
46588          * The raw keydown event for the entire grid.
46589          * @param {Roo.EventObject} e
46590          */
46591         "keydown" : true,
46592
46593         // custom events
46594
46595         /**
46596          * @event cellclick
46597          * Fires when a cell is clicked
46598          * @param {Grid} this
46599          * @param {Number} rowIndex
46600          * @param {Number} columnIndex
46601          * @param {Roo.EventObject} e
46602          */
46603         "cellclick" : true,
46604         /**
46605          * @event celldblclick
46606          * Fires when a cell is double clicked
46607          * @param {Grid} this
46608          * @param {Number} rowIndex
46609          * @param {Number} columnIndex
46610          * @param {Roo.EventObject} e
46611          */
46612         "celldblclick" : true,
46613         /**
46614          * @event rowclick
46615          * Fires when a row is clicked
46616          * @param {Grid} this
46617          * @param {Number} rowIndex
46618          * @param {Roo.EventObject} e
46619          */
46620         "rowclick" : true,
46621         /**
46622          * @event rowdblclick
46623          * Fires when a row is double clicked
46624          * @param {Grid} this
46625          * @param {Number} rowIndex
46626          * @param {Roo.EventObject} e
46627          */
46628         "rowdblclick" : true,
46629         /**
46630          * @event headerclick
46631          * Fires when a header is clicked
46632          * @param {Grid} this
46633          * @param {Number} columnIndex
46634          * @param {Roo.EventObject} e
46635          */
46636         "headerclick" : true,
46637         /**
46638          * @event headerdblclick
46639          * Fires when a header cell is double clicked
46640          * @param {Grid} this
46641          * @param {Number} columnIndex
46642          * @param {Roo.EventObject} e
46643          */
46644         "headerdblclick" : true,
46645         /**
46646          * @event rowcontextmenu
46647          * Fires when a row is right clicked
46648          * @param {Grid} this
46649          * @param {Number} rowIndex
46650          * @param {Roo.EventObject} e
46651          */
46652         "rowcontextmenu" : true,
46653         /**
46654          * @event cellcontextmenu
46655          * Fires when a cell is right clicked
46656          * @param {Grid} this
46657          * @param {Number} rowIndex
46658          * @param {Number} cellIndex
46659          * @param {Roo.EventObject} e
46660          */
46661          "cellcontextmenu" : true,
46662         /**
46663          * @event headercontextmenu
46664          * Fires when a header is right clicked
46665          * @param {Grid} this
46666          * @param {Number} columnIndex
46667          * @param {Roo.EventObject} e
46668          */
46669         "headercontextmenu" : true,
46670         /**
46671          * @event bodyscroll
46672          * Fires when the body element is scrolled
46673          * @param {Number} scrollLeft
46674          * @param {Number} scrollTop
46675          */
46676         "bodyscroll" : true,
46677         /**
46678          * @event columnresize
46679          * Fires when the user resizes a column
46680          * @param {Number} columnIndex
46681          * @param {Number} newSize
46682          */
46683         "columnresize" : true,
46684         /**
46685          * @event columnmove
46686          * Fires when the user moves a column
46687          * @param {Number} oldIndex
46688          * @param {Number} newIndex
46689          */
46690         "columnmove" : true,
46691         /**
46692          * @event startdrag
46693          * Fires when row(s) start being dragged
46694          * @param {Grid} this
46695          * @param {Roo.GridDD} dd The drag drop object
46696          * @param {event} e The raw browser event
46697          */
46698         "startdrag" : true,
46699         /**
46700          * @event enddrag
46701          * Fires when a drag operation is complete
46702          * @param {Grid} this
46703          * @param {Roo.GridDD} dd The drag drop object
46704          * @param {event} e The raw browser event
46705          */
46706         "enddrag" : true,
46707         /**
46708          * @event dragdrop
46709          * Fires when dragged row(s) are dropped on a valid DD target
46710          * @param {Grid} this
46711          * @param {Roo.GridDD} dd The drag drop object
46712          * @param {String} targetId The target drag drop object
46713          * @param {event} e The raw browser event
46714          */
46715         "dragdrop" : true,
46716         /**
46717          * @event dragover
46718          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46719          * @param {Grid} this
46720          * @param {Roo.GridDD} dd The drag drop object
46721          * @param {String} targetId The target drag drop object
46722          * @param {event} e The raw browser event
46723          */
46724         "dragover" : true,
46725         /**
46726          * @event dragenter
46727          *  Fires when the dragged row(s) first cross another DD target while being dragged
46728          * @param {Grid} this
46729          * @param {Roo.GridDD} dd The drag drop object
46730          * @param {String} targetId The target drag drop object
46731          * @param {event} e The raw browser event
46732          */
46733         "dragenter" : true,
46734         /**
46735          * @event dragout
46736          * Fires when the dragged row(s) leave another DD target while being dragged
46737          * @param {Grid} this
46738          * @param {Roo.GridDD} dd The drag drop object
46739          * @param {String} targetId The target drag drop object
46740          * @param {event} e The raw browser event
46741          */
46742         "dragout" : true,
46743         /**
46744          * @event rowclass
46745          * Fires when a row is rendered, so you can change add a style to it.
46746          * @param {GridView} gridview   The grid view
46747          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46748          */
46749         'rowclass' : true,
46750
46751         /**
46752          * @event render
46753          * Fires when the grid is rendered
46754          * @param {Grid} grid
46755          */
46756         'render' : true
46757     });
46758
46759     Roo.grid.Grid.superclass.constructor.call(this);
46760 };
46761 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46762     
46763     /**
46764      * @cfg {String} ddGroup - drag drop group.
46765      */
46766
46767     /**
46768      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46769      */
46770     minColumnWidth : 25,
46771
46772     /**
46773      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46774      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46775      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46776      */
46777     autoSizeColumns : false,
46778
46779     /**
46780      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46781      */
46782     autoSizeHeaders : true,
46783
46784     /**
46785      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46786      */
46787     monitorWindowResize : true,
46788
46789     /**
46790      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46791      * rows measured to get a columns size. Default is 0 (all rows).
46792      */
46793     maxRowsToMeasure : 0,
46794
46795     /**
46796      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46797      */
46798     trackMouseOver : true,
46799
46800     /**
46801     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46802     */
46803     
46804     /**
46805     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46806     */
46807     enableDragDrop : false,
46808     
46809     /**
46810     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46811     */
46812     enableColumnMove : true,
46813     
46814     /**
46815     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46816     */
46817     enableColumnHide : true,
46818     
46819     /**
46820     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46821     */
46822     enableRowHeightSync : false,
46823     
46824     /**
46825     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46826     */
46827     stripeRows : true,
46828     
46829     /**
46830     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46831     */
46832     autoHeight : false,
46833
46834     /**
46835      * @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.
46836      */
46837     autoExpandColumn : false,
46838
46839     /**
46840     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46841     * Default is 50.
46842     */
46843     autoExpandMin : 50,
46844
46845     /**
46846     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46847     */
46848     autoExpandMax : 1000,
46849
46850     /**
46851     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46852     */
46853     view : null,
46854
46855     /**
46856     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46857     */
46858     loadMask : false,
46859     /**
46860     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46861     */
46862     dropTarget: false,
46863     
46864    
46865     
46866     // private
46867     rendered : false,
46868
46869     /**
46870     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46871     * of a fixed width. Default is false.
46872     */
46873     /**
46874     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46875     */
46876     /**
46877      * Called once after all setup has been completed and the grid is ready to be rendered.
46878      * @return {Roo.grid.Grid} this
46879      */
46880     render : function()
46881     {
46882         var c = this.container;
46883         // try to detect autoHeight/width mode
46884         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46885             this.autoHeight = true;
46886         }
46887         var view = this.getView();
46888         view.init(this);
46889
46890         c.on("click", this.onClick, this);
46891         c.on("dblclick", this.onDblClick, this);
46892         c.on("contextmenu", this.onContextMenu, this);
46893         c.on("keydown", this.onKeyDown, this);
46894
46895         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46896
46897         this.getSelectionModel().init(this);
46898
46899         view.render();
46900
46901         if(this.loadMask){
46902             this.loadMask = new Roo.LoadMask(this.container,
46903                     Roo.apply({store:this.dataSource}, this.loadMask));
46904         }
46905         
46906         
46907         if (this.toolbar && this.toolbar.xtype) {
46908             this.toolbar.container = this.getView().getHeaderPanel(true);
46909             this.toolbar = new Roo.Toolbar(this.toolbar);
46910         }
46911         if (this.footer && this.footer.xtype) {
46912             this.footer.dataSource = this.getDataSource();
46913             this.footer.container = this.getView().getFooterPanel(true);
46914             this.footer = Roo.factory(this.footer, Roo);
46915         }
46916         if (this.dropTarget && this.dropTarget.xtype) {
46917             delete this.dropTarget.xtype;
46918             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46919         }
46920         
46921         
46922         this.rendered = true;
46923         this.fireEvent('render', this);
46924         return this;
46925     },
46926
46927         /**
46928          * Reconfigures the grid to use a different Store and Column Model.
46929          * The View will be bound to the new objects and refreshed.
46930          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46931          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46932          */
46933     reconfigure : function(dataSource, colModel){
46934         if(this.loadMask){
46935             this.loadMask.destroy();
46936             this.loadMask = new Roo.LoadMask(this.container,
46937                     Roo.apply({store:dataSource}, this.loadMask));
46938         }
46939         this.view.bind(dataSource, colModel);
46940         this.dataSource = dataSource;
46941         this.colModel = colModel;
46942         this.view.refresh(true);
46943     },
46944
46945     // private
46946     onKeyDown : function(e){
46947         this.fireEvent("keydown", e);
46948     },
46949
46950     /**
46951      * Destroy this grid.
46952      * @param {Boolean} removeEl True to remove the element
46953      */
46954     destroy : function(removeEl, keepListeners){
46955         if(this.loadMask){
46956             this.loadMask.destroy();
46957         }
46958         var c = this.container;
46959         c.removeAllListeners();
46960         this.view.destroy();
46961         this.colModel.purgeListeners();
46962         if(!keepListeners){
46963             this.purgeListeners();
46964         }
46965         c.update("");
46966         if(removeEl === true){
46967             c.remove();
46968         }
46969     },
46970
46971     // private
46972     processEvent : function(name, e){
46973         this.fireEvent(name, e);
46974         var t = e.getTarget();
46975         var v = this.view;
46976         var header = v.findHeaderIndex(t);
46977         if(header !== false){
46978             this.fireEvent("header" + name, this, header, e);
46979         }else{
46980             var row = v.findRowIndex(t);
46981             var cell = v.findCellIndex(t);
46982             if(row !== false){
46983                 this.fireEvent("row" + name, this, row, e);
46984                 if(cell !== false){
46985                     this.fireEvent("cell" + name, this, row, cell, e);
46986                 }
46987             }
46988         }
46989     },
46990
46991     // private
46992     onClick : function(e){
46993         this.processEvent("click", e);
46994     },
46995
46996     // private
46997     onContextMenu : function(e, t){
46998         this.processEvent("contextmenu", e);
46999     },
47000
47001     // private
47002     onDblClick : function(e){
47003         this.processEvent("dblclick", e);
47004     },
47005
47006     // private
47007     walkCells : function(row, col, step, fn, scope){
47008         var cm = this.colModel, clen = cm.getColumnCount();
47009         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47010         if(step < 0){
47011             if(col < 0){
47012                 row--;
47013                 first = false;
47014             }
47015             while(row >= 0){
47016                 if(!first){
47017                     col = clen-1;
47018                 }
47019                 first = false;
47020                 while(col >= 0){
47021                     if(fn.call(scope || this, row, col, cm) === true){
47022                         return [row, col];
47023                     }
47024                     col--;
47025                 }
47026                 row--;
47027             }
47028         } else {
47029             if(col >= clen){
47030                 row++;
47031                 first = false;
47032             }
47033             while(row < rlen){
47034                 if(!first){
47035                     col = 0;
47036                 }
47037                 first = false;
47038                 while(col < clen){
47039                     if(fn.call(scope || this, row, col, cm) === true){
47040                         return [row, col];
47041                     }
47042                     col++;
47043                 }
47044                 row++;
47045             }
47046         }
47047         return null;
47048     },
47049
47050     // private
47051     getSelections : function(){
47052         return this.selModel.getSelections();
47053     },
47054
47055     /**
47056      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47057      * but if manual update is required this method will initiate it.
47058      */
47059     autoSize : function(){
47060         if(this.rendered){
47061             this.view.layout();
47062             if(this.view.adjustForScroll){
47063                 this.view.adjustForScroll();
47064             }
47065         }
47066     },
47067
47068     /**
47069      * Returns the grid's underlying element.
47070      * @return {Element} The element
47071      */
47072     getGridEl : function(){
47073         return this.container;
47074     },
47075
47076     // private for compatibility, overridden by editor grid
47077     stopEditing : function(){},
47078
47079     /**
47080      * Returns the grid's SelectionModel.
47081      * @return {SelectionModel}
47082      */
47083     getSelectionModel : function(){
47084         if(!this.selModel){
47085             this.selModel = new Roo.grid.RowSelectionModel();
47086         }
47087         return this.selModel;
47088     },
47089
47090     /**
47091      * Returns the grid's DataSource.
47092      * @return {DataSource}
47093      */
47094     getDataSource : function(){
47095         return this.dataSource;
47096     },
47097
47098     /**
47099      * Returns the grid's ColumnModel.
47100      * @return {ColumnModel}
47101      */
47102     getColumnModel : function(){
47103         return this.colModel;
47104     },
47105
47106     /**
47107      * Returns the grid's GridView object.
47108      * @return {GridView}
47109      */
47110     getView : function(){
47111         if(!this.view){
47112             this.view = new Roo.grid.GridView(this.viewConfig);
47113         }
47114         return this.view;
47115     },
47116     /**
47117      * Called to get grid's drag proxy text, by default returns this.ddText.
47118      * @return {String}
47119      */
47120     getDragDropText : function(){
47121         var count = this.selModel.getCount();
47122         return String.format(this.ddText, count, count == 1 ? '' : 's');
47123     }
47124 });
47125 /**
47126  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47127  * %0 is replaced with the number of selected rows.
47128  * @type String
47129  */
47130 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47131  * Based on:
47132  * Ext JS Library 1.1.1
47133  * Copyright(c) 2006-2007, Ext JS, LLC.
47134  *
47135  * Originally Released Under LGPL - original licence link has changed is not relivant.
47136  *
47137  * Fork - LGPL
47138  * <script type="text/javascript">
47139  */
47140  
47141 Roo.grid.AbstractGridView = function(){
47142         this.grid = null;
47143         
47144         this.events = {
47145             "beforerowremoved" : true,
47146             "beforerowsinserted" : true,
47147             "beforerefresh" : true,
47148             "rowremoved" : true,
47149             "rowsinserted" : true,
47150             "rowupdated" : true,
47151             "refresh" : true
47152         };
47153     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47154 };
47155
47156 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47157     rowClass : "x-grid-row",
47158     cellClass : "x-grid-cell",
47159     tdClass : "x-grid-td",
47160     hdClass : "x-grid-hd",
47161     splitClass : "x-grid-hd-split",
47162     
47163         init: function(grid){
47164         this.grid = grid;
47165                 var cid = this.grid.getGridEl().id;
47166         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47167         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47168         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47169         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47170         },
47171         
47172         getColumnRenderers : function(){
47173         var renderers = [];
47174         var cm = this.grid.colModel;
47175         var colCount = cm.getColumnCount();
47176         for(var i = 0; i < colCount; i++){
47177             renderers[i] = cm.getRenderer(i);
47178         }
47179         return renderers;
47180     },
47181     
47182     getColumnIds : function(){
47183         var ids = [];
47184         var cm = this.grid.colModel;
47185         var colCount = cm.getColumnCount();
47186         for(var i = 0; i < colCount; i++){
47187             ids[i] = cm.getColumnId(i);
47188         }
47189         return ids;
47190     },
47191     
47192     getDataIndexes : function(){
47193         if(!this.indexMap){
47194             this.indexMap = this.buildIndexMap();
47195         }
47196         return this.indexMap.colToData;
47197     },
47198     
47199     getColumnIndexByDataIndex : function(dataIndex){
47200         if(!this.indexMap){
47201             this.indexMap = this.buildIndexMap();
47202         }
47203         return this.indexMap.dataToCol[dataIndex];
47204     },
47205     
47206     /**
47207      * Set a css style for a column dynamically. 
47208      * @param {Number} colIndex The index of the column
47209      * @param {String} name The css property name
47210      * @param {String} value The css value
47211      */
47212     setCSSStyle : function(colIndex, name, value){
47213         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47214         Roo.util.CSS.updateRule(selector, name, value);
47215     },
47216     
47217     generateRules : function(cm){
47218         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47219         Roo.util.CSS.removeStyleSheet(rulesId);
47220         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47221             var cid = cm.getColumnId(i);
47222             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47223                          this.tdSelector, cid, " {\n}\n",
47224                          this.hdSelector, cid, " {\n}\n",
47225                          this.splitSelector, cid, " {\n}\n");
47226         }
47227         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47228     }
47229 });/*
47230  * Based on:
47231  * Ext JS Library 1.1.1
47232  * Copyright(c) 2006-2007, Ext JS, LLC.
47233  *
47234  * Originally Released Under LGPL - original licence link has changed is not relivant.
47235  *
47236  * Fork - LGPL
47237  * <script type="text/javascript">
47238  */
47239
47240 // private
47241 // This is a support class used internally by the Grid components
47242 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47243     this.grid = grid;
47244     this.view = grid.getView();
47245     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47246     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47247     if(hd2){
47248         this.setHandleElId(Roo.id(hd));
47249         this.setOuterHandleElId(Roo.id(hd2));
47250     }
47251     this.scroll = false;
47252 };
47253 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47254     maxDragWidth: 120,
47255     getDragData : function(e){
47256         var t = Roo.lib.Event.getTarget(e);
47257         var h = this.view.findHeaderCell(t);
47258         if(h){
47259             return {ddel: h.firstChild, header:h};
47260         }
47261         return false;
47262     },
47263
47264     onInitDrag : function(e){
47265         this.view.headersDisabled = true;
47266         var clone = this.dragData.ddel.cloneNode(true);
47267         clone.id = Roo.id();
47268         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47269         this.proxy.update(clone);
47270         return true;
47271     },
47272
47273     afterValidDrop : function(){
47274         var v = this.view;
47275         setTimeout(function(){
47276             v.headersDisabled = false;
47277         }, 50);
47278     },
47279
47280     afterInvalidDrop : function(){
47281         var v = this.view;
47282         setTimeout(function(){
47283             v.headersDisabled = false;
47284         }, 50);
47285     }
47286 });
47287 /*
47288  * Based on:
47289  * Ext JS Library 1.1.1
47290  * Copyright(c) 2006-2007, Ext JS, LLC.
47291  *
47292  * Originally Released Under LGPL - original licence link has changed is not relivant.
47293  *
47294  * Fork - LGPL
47295  * <script type="text/javascript">
47296  */
47297 // private
47298 // This is a support class used internally by the Grid components
47299 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47300     this.grid = grid;
47301     this.view = grid.getView();
47302     // split the proxies so they don't interfere with mouse events
47303     this.proxyTop = Roo.DomHelper.append(document.body, {
47304         cls:"col-move-top", html:"&#160;"
47305     }, true);
47306     this.proxyBottom = Roo.DomHelper.append(document.body, {
47307         cls:"col-move-bottom", html:"&#160;"
47308     }, true);
47309     this.proxyTop.hide = this.proxyBottom.hide = function(){
47310         this.setLeftTop(-100,-100);
47311         this.setStyle("visibility", "hidden");
47312     };
47313     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47314     // temporarily disabled
47315     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47316     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47317 };
47318 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47319     proxyOffsets : [-4, -9],
47320     fly: Roo.Element.fly,
47321
47322     getTargetFromEvent : function(e){
47323         var t = Roo.lib.Event.getTarget(e);
47324         var cindex = this.view.findCellIndex(t);
47325         if(cindex !== false){
47326             return this.view.getHeaderCell(cindex);
47327         }
47328         return null;
47329     },
47330
47331     nextVisible : function(h){
47332         var v = this.view, cm = this.grid.colModel;
47333         h = h.nextSibling;
47334         while(h){
47335             if(!cm.isHidden(v.getCellIndex(h))){
47336                 return h;
47337             }
47338             h = h.nextSibling;
47339         }
47340         return null;
47341     },
47342
47343     prevVisible : function(h){
47344         var v = this.view, cm = this.grid.colModel;
47345         h = h.prevSibling;
47346         while(h){
47347             if(!cm.isHidden(v.getCellIndex(h))){
47348                 return h;
47349             }
47350             h = h.prevSibling;
47351         }
47352         return null;
47353     },
47354
47355     positionIndicator : function(h, n, e){
47356         var x = Roo.lib.Event.getPageX(e);
47357         var r = Roo.lib.Dom.getRegion(n.firstChild);
47358         var px, pt, py = r.top + this.proxyOffsets[1];
47359         if((r.right - x) <= (r.right-r.left)/2){
47360             px = r.right+this.view.borderWidth;
47361             pt = "after";
47362         }else{
47363             px = r.left;
47364             pt = "before";
47365         }
47366         var oldIndex = this.view.getCellIndex(h);
47367         var newIndex = this.view.getCellIndex(n);
47368
47369         if(this.grid.colModel.isFixed(newIndex)){
47370             return false;
47371         }
47372
47373         var locked = this.grid.colModel.isLocked(newIndex);
47374
47375         if(pt == "after"){
47376             newIndex++;
47377         }
47378         if(oldIndex < newIndex){
47379             newIndex--;
47380         }
47381         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47382             return false;
47383         }
47384         px +=  this.proxyOffsets[0];
47385         this.proxyTop.setLeftTop(px, py);
47386         this.proxyTop.show();
47387         if(!this.bottomOffset){
47388             this.bottomOffset = this.view.mainHd.getHeight();
47389         }
47390         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47391         this.proxyBottom.show();
47392         return pt;
47393     },
47394
47395     onNodeEnter : function(n, dd, e, data){
47396         if(data.header != n){
47397             this.positionIndicator(data.header, n, e);
47398         }
47399     },
47400
47401     onNodeOver : function(n, dd, e, data){
47402         var result = false;
47403         if(data.header != n){
47404             result = this.positionIndicator(data.header, n, e);
47405         }
47406         if(!result){
47407             this.proxyTop.hide();
47408             this.proxyBottom.hide();
47409         }
47410         return result ? this.dropAllowed : this.dropNotAllowed;
47411     },
47412
47413     onNodeOut : function(n, dd, e, data){
47414         this.proxyTop.hide();
47415         this.proxyBottom.hide();
47416     },
47417
47418     onNodeDrop : function(n, dd, e, data){
47419         var h = data.header;
47420         if(h != n){
47421             var cm = this.grid.colModel;
47422             var x = Roo.lib.Event.getPageX(e);
47423             var r = Roo.lib.Dom.getRegion(n.firstChild);
47424             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47425             var oldIndex = this.view.getCellIndex(h);
47426             var newIndex = this.view.getCellIndex(n);
47427             var locked = cm.isLocked(newIndex);
47428             if(pt == "after"){
47429                 newIndex++;
47430             }
47431             if(oldIndex < newIndex){
47432                 newIndex--;
47433             }
47434             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47435                 return false;
47436             }
47437             cm.setLocked(oldIndex, locked, true);
47438             cm.moveColumn(oldIndex, newIndex);
47439             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47440             return true;
47441         }
47442         return false;
47443     }
47444 });
47445 /*
47446  * Based on:
47447  * Ext JS Library 1.1.1
47448  * Copyright(c) 2006-2007, Ext JS, LLC.
47449  *
47450  * Originally Released Under LGPL - original licence link has changed is not relivant.
47451  *
47452  * Fork - LGPL
47453  * <script type="text/javascript">
47454  */
47455   
47456 /**
47457  * @class Roo.grid.GridView
47458  * @extends Roo.util.Observable
47459  *
47460  * @constructor
47461  * @param {Object} config
47462  */
47463 Roo.grid.GridView = function(config){
47464     Roo.grid.GridView.superclass.constructor.call(this);
47465     this.el = null;
47466
47467     Roo.apply(this, config);
47468 };
47469
47470 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47471
47472     /**
47473      * Override this function to apply custom css classes to rows during rendering
47474      * @param {Record} record The record
47475      * @param {Number} index
47476      * @method getRowClass
47477      */
47478     rowClass : "x-grid-row",
47479
47480     cellClass : "x-grid-col",
47481
47482     tdClass : "x-grid-td",
47483
47484     hdClass : "x-grid-hd",
47485
47486     splitClass : "x-grid-split",
47487
47488     sortClasses : ["sort-asc", "sort-desc"],
47489
47490     enableMoveAnim : false,
47491
47492     hlColor: "C3DAF9",
47493
47494     dh : Roo.DomHelper,
47495
47496     fly : Roo.Element.fly,
47497
47498     css : Roo.util.CSS,
47499
47500     borderWidth: 1,
47501
47502     splitOffset: 3,
47503
47504     scrollIncrement : 22,
47505
47506     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47507
47508     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47509
47510     bind : function(ds, cm){
47511         if(this.ds){
47512             this.ds.un("load", this.onLoad, this);
47513             this.ds.un("datachanged", this.onDataChange, this);
47514             this.ds.un("add", this.onAdd, this);
47515             this.ds.un("remove", this.onRemove, this);
47516             this.ds.un("update", this.onUpdate, this);
47517             this.ds.un("clear", this.onClear, this);
47518         }
47519         if(ds){
47520             ds.on("load", this.onLoad, this);
47521             ds.on("datachanged", this.onDataChange, this);
47522             ds.on("add", this.onAdd, this);
47523             ds.on("remove", this.onRemove, this);
47524             ds.on("update", this.onUpdate, this);
47525             ds.on("clear", this.onClear, this);
47526         }
47527         this.ds = ds;
47528
47529         if(this.cm){
47530             this.cm.un("widthchange", this.onColWidthChange, this);
47531             this.cm.un("headerchange", this.onHeaderChange, this);
47532             this.cm.un("hiddenchange", this.onHiddenChange, this);
47533             this.cm.un("columnmoved", this.onColumnMove, this);
47534             this.cm.un("columnlockchange", this.onColumnLock, this);
47535         }
47536         if(cm){
47537             this.generateRules(cm);
47538             cm.on("widthchange", this.onColWidthChange, this);
47539             cm.on("headerchange", this.onHeaderChange, this);
47540             cm.on("hiddenchange", this.onHiddenChange, this);
47541             cm.on("columnmoved", this.onColumnMove, this);
47542             cm.on("columnlockchange", this.onColumnLock, this);
47543         }
47544         this.cm = cm;
47545     },
47546
47547     init: function(grid){
47548         Roo.grid.GridView.superclass.init.call(this, grid);
47549
47550         this.bind(grid.dataSource, grid.colModel);
47551
47552         grid.on("headerclick", this.handleHeaderClick, this);
47553
47554         if(grid.trackMouseOver){
47555             grid.on("mouseover", this.onRowOver, this);
47556             grid.on("mouseout", this.onRowOut, this);
47557         }
47558         grid.cancelTextSelection = function(){};
47559         this.gridId = grid.id;
47560
47561         var tpls = this.templates || {};
47562
47563         if(!tpls.master){
47564             tpls.master = new Roo.Template(
47565                '<div class="x-grid" hidefocus="true">',
47566                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47567                   '<div class="x-grid-topbar"></div>',
47568                   '<div class="x-grid-scroller"><div></div></div>',
47569                   '<div class="x-grid-locked">',
47570                       '<div class="x-grid-header">{lockedHeader}</div>',
47571                       '<div class="x-grid-body">{lockedBody}</div>',
47572                   "</div>",
47573                   '<div class="x-grid-viewport">',
47574                       '<div class="x-grid-header">{header}</div>',
47575                       '<div class="x-grid-body">{body}</div>',
47576                   "</div>",
47577                   '<div class="x-grid-bottombar"></div>',
47578                  
47579                   '<div class="x-grid-resize-proxy">&#160;</div>',
47580                "</div>"
47581             );
47582             tpls.master.disableformats = true;
47583         }
47584
47585         if(!tpls.header){
47586             tpls.header = new Roo.Template(
47587                '<table border="0" cellspacing="0" cellpadding="0">',
47588                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47589                "</table>{splits}"
47590             );
47591             tpls.header.disableformats = true;
47592         }
47593         tpls.header.compile();
47594
47595         if(!tpls.hcell){
47596             tpls.hcell = new Roo.Template(
47597                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47598                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47599                 "</div></td>"
47600              );
47601              tpls.hcell.disableFormats = true;
47602         }
47603         tpls.hcell.compile();
47604
47605         if(!tpls.hsplit){
47606             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47607             tpls.hsplit.disableFormats = true;
47608         }
47609         tpls.hsplit.compile();
47610
47611         if(!tpls.body){
47612             tpls.body = new Roo.Template(
47613                '<table border="0" cellspacing="0" cellpadding="0">',
47614                "<tbody>{rows}</tbody>",
47615                "</table>"
47616             );
47617             tpls.body.disableFormats = true;
47618         }
47619         tpls.body.compile();
47620
47621         if(!tpls.row){
47622             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47623             tpls.row.disableFormats = true;
47624         }
47625         tpls.row.compile();
47626
47627         if(!tpls.cell){
47628             tpls.cell = new Roo.Template(
47629                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47630                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47631                 "</td>"
47632             );
47633             tpls.cell.disableFormats = true;
47634         }
47635         tpls.cell.compile();
47636
47637         this.templates = tpls;
47638     },
47639
47640     // remap these for backwards compat
47641     onColWidthChange : function(){
47642         this.updateColumns.apply(this, arguments);
47643     },
47644     onHeaderChange : function(){
47645         this.updateHeaders.apply(this, arguments);
47646     }, 
47647     onHiddenChange : function(){
47648         this.handleHiddenChange.apply(this, arguments);
47649     },
47650     onColumnMove : function(){
47651         this.handleColumnMove.apply(this, arguments);
47652     },
47653     onColumnLock : function(){
47654         this.handleLockChange.apply(this, arguments);
47655     },
47656
47657     onDataChange : function(){
47658         this.refresh();
47659         this.updateHeaderSortState();
47660     },
47661
47662     onClear : function(){
47663         this.refresh();
47664     },
47665
47666     onUpdate : function(ds, record){
47667         this.refreshRow(record);
47668     },
47669
47670     refreshRow : function(record){
47671         var ds = this.ds, index;
47672         if(typeof record == 'number'){
47673             index = record;
47674             record = ds.getAt(index);
47675         }else{
47676             index = ds.indexOf(record);
47677         }
47678         this.insertRows(ds, index, index, true);
47679         this.onRemove(ds, record, index+1, true);
47680         this.syncRowHeights(index, index);
47681         this.layout();
47682         this.fireEvent("rowupdated", this, index, record);
47683     },
47684
47685     onAdd : function(ds, records, index){
47686         this.insertRows(ds, index, index + (records.length-1));
47687     },
47688
47689     onRemove : function(ds, record, index, isUpdate){
47690         if(isUpdate !== true){
47691             this.fireEvent("beforerowremoved", this, index, record);
47692         }
47693         var bt = this.getBodyTable(), lt = this.getLockedTable();
47694         if(bt.rows[index]){
47695             bt.firstChild.removeChild(bt.rows[index]);
47696         }
47697         if(lt.rows[index]){
47698             lt.firstChild.removeChild(lt.rows[index]);
47699         }
47700         if(isUpdate !== true){
47701             this.stripeRows(index);
47702             this.syncRowHeights(index, index);
47703             this.layout();
47704             this.fireEvent("rowremoved", this, index, record);
47705         }
47706     },
47707
47708     onLoad : function(){
47709         this.scrollToTop();
47710     },
47711
47712     /**
47713      * Scrolls the grid to the top
47714      */
47715     scrollToTop : function(){
47716         if(this.scroller){
47717             this.scroller.dom.scrollTop = 0;
47718             this.syncScroll();
47719         }
47720     },
47721
47722     /**
47723      * Gets a panel in the header of the grid that can be used for toolbars etc.
47724      * After modifying the contents of this panel a call to grid.autoSize() may be
47725      * required to register any changes in size.
47726      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47727      * @return Roo.Element
47728      */
47729     getHeaderPanel : function(doShow){
47730         if(doShow){
47731             this.headerPanel.show();
47732         }
47733         return this.headerPanel;
47734     },
47735
47736     /**
47737      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47738      * After modifying the contents of this panel a call to grid.autoSize() may be
47739      * required to register any changes in size.
47740      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47741      * @return Roo.Element
47742      */
47743     getFooterPanel : function(doShow){
47744         if(doShow){
47745             this.footerPanel.show();
47746         }
47747         return this.footerPanel;
47748     },
47749
47750     initElements : function(){
47751         var E = Roo.Element;
47752         var el = this.grid.getGridEl().dom.firstChild;
47753         var cs = el.childNodes;
47754
47755         this.el = new E(el);
47756         
47757          this.focusEl = new E(el.firstChild);
47758         this.focusEl.swallowEvent("click", true);
47759         
47760         this.headerPanel = new E(cs[1]);
47761         this.headerPanel.enableDisplayMode("block");
47762
47763         this.scroller = new E(cs[2]);
47764         this.scrollSizer = new E(this.scroller.dom.firstChild);
47765
47766         this.lockedWrap = new E(cs[3]);
47767         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47768         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47769
47770         this.mainWrap = new E(cs[4]);
47771         this.mainHd = new E(this.mainWrap.dom.firstChild);
47772         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47773
47774         this.footerPanel = new E(cs[5]);
47775         this.footerPanel.enableDisplayMode("block");
47776
47777         this.resizeProxy = new E(cs[6]);
47778
47779         this.headerSelector = String.format(
47780            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47781            this.lockedHd.id, this.mainHd.id
47782         );
47783
47784         this.splitterSelector = String.format(
47785            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47786            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47787         );
47788     },
47789     idToCssName : function(s)
47790     {
47791         return s.replace(/[^a-z0-9]+/ig, '-');
47792     },
47793
47794     getHeaderCell : function(index){
47795         return Roo.DomQuery.select(this.headerSelector)[index];
47796     },
47797
47798     getHeaderCellMeasure : function(index){
47799         return this.getHeaderCell(index).firstChild;
47800     },
47801
47802     getHeaderCellText : function(index){
47803         return this.getHeaderCell(index).firstChild.firstChild;
47804     },
47805
47806     getLockedTable : function(){
47807         return this.lockedBody.dom.firstChild;
47808     },
47809
47810     getBodyTable : function(){
47811         return this.mainBody.dom.firstChild;
47812     },
47813
47814     getLockedRow : function(index){
47815         return this.getLockedTable().rows[index];
47816     },
47817
47818     getRow : function(index){
47819         return this.getBodyTable().rows[index];
47820     },
47821
47822     getRowComposite : function(index){
47823         if(!this.rowEl){
47824             this.rowEl = new Roo.CompositeElementLite();
47825         }
47826         var els = [], lrow, mrow;
47827         if(lrow = this.getLockedRow(index)){
47828             els.push(lrow);
47829         }
47830         if(mrow = this.getRow(index)){
47831             els.push(mrow);
47832         }
47833         this.rowEl.elements = els;
47834         return this.rowEl;
47835     },
47836
47837     getCell : function(rowIndex, colIndex){
47838         var locked = this.cm.getLockedCount();
47839         var source;
47840         if(colIndex < locked){
47841             source = this.lockedBody.dom.firstChild;
47842         }else{
47843             source = this.mainBody.dom.firstChild;
47844             colIndex -= locked;
47845         }
47846         return source.rows[rowIndex].childNodes[colIndex];
47847     },
47848
47849     getCellText : function(rowIndex, colIndex){
47850         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47851     },
47852
47853     getCellBox : function(cell){
47854         var b = this.fly(cell).getBox();
47855         if(Roo.isOpera){ // opera fails to report the Y
47856             b.y = cell.offsetTop + this.mainBody.getY();
47857         }
47858         return b;
47859     },
47860
47861     getCellIndex : function(cell){
47862         var id = String(cell.className).match(this.cellRE);
47863         if(id){
47864             return parseInt(id[1], 10);
47865         }
47866         return 0;
47867     },
47868
47869     findHeaderIndex : function(n){
47870         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47871         return r ? this.getCellIndex(r) : false;
47872     },
47873
47874     findHeaderCell : function(n){
47875         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47876         return r ? r : false;
47877     },
47878
47879     findRowIndex : function(n){
47880         if(!n){
47881             return false;
47882         }
47883         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47884         return r ? r.rowIndex : false;
47885     },
47886
47887     findCellIndex : function(node){
47888         var stop = this.el.dom;
47889         while(node && node != stop){
47890             if(this.findRE.test(node.className)){
47891                 return this.getCellIndex(node);
47892             }
47893             node = node.parentNode;
47894         }
47895         return false;
47896     },
47897
47898     getColumnId : function(index){
47899         return this.cm.getColumnId(index);
47900     },
47901
47902     getSplitters : function()
47903     {
47904         if(this.splitterSelector){
47905            return Roo.DomQuery.select(this.splitterSelector);
47906         }else{
47907             return null;
47908       }
47909     },
47910
47911     getSplitter : function(index){
47912         return this.getSplitters()[index];
47913     },
47914
47915     onRowOver : function(e, t){
47916         var row;
47917         if((row = this.findRowIndex(t)) !== false){
47918             this.getRowComposite(row).addClass("x-grid-row-over");
47919         }
47920     },
47921
47922     onRowOut : function(e, t){
47923         var row;
47924         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47925             this.getRowComposite(row).removeClass("x-grid-row-over");
47926         }
47927     },
47928
47929     renderHeaders : function(){
47930         var cm = this.cm;
47931         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47932         var cb = [], lb = [], sb = [], lsb = [], p = {};
47933         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47934             p.cellId = "x-grid-hd-0-" + i;
47935             p.splitId = "x-grid-csplit-0-" + i;
47936             p.id = cm.getColumnId(i);
47937             p.title = cm.getColumnTooltip(i) || "";
47938             p.value = cm.getColumnHeader(i) || "";
47939             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47940             if(!cm.isLocked(i)){
47941                 cb[cb.length] = ct.apply(p);
47942                 sb[sb.length] = st.apply(p);
47943             }else{
47944                 lb[lb.length] = ct.apply(p);
47945                 lsb[lsb.length] = st.apply(p);
47946             }
47947         }
47948         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47949                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47950     },
47951
47952     updateHeaders : function(){
47953         var html = this.renderHeaders();
47954         this.lockedHd.update(html[0]);
47955         this.mainHd.update(html[1]);
47956     },
47957
47958     /**
47959      * Focuses the specified row.
47960      * @param {Number} row The row index
47961      */
47962     focusRow : function(row)
47963     {
47964         //Roo.log('GridView.focusRow');
47965         var x = this.scroller.dom.scrollLeft;
47966         this.focusCell(row, 0, false);
47967         this.scroller.dom.scrollLeft = x;
47968     },
47969
47970     /**
47971      * Focuses the specified cell.
47972      * @param {Number} row The row index
47973      * @param {Number} col The column index
47974      * @param {Boolean} hscroll false to disable horizontal scrolling
47975      */
47976     focusCell : function(row, col, hscroll)
47977     {
47978         //Roo.log('GridView.focusCell');
47979         var el = this.ensureVisible(row, col, hscroll);
47980         this.focusEl.alignTo(el, "tl-tl");
47981         if(Roo.isGecko){
47982             this.focusEl.focus();
47983         }else{
47984             this.focusEl.focus.defer(1, this.focusEl);
47985         }
47986     },
47987
47988     /**
47989      * Scrolls the specified cell into view
47990      * @param {Number} row The row index
47991      * @param {Number} col The column index
47992      * @param {Boolean} hscroll false to disable horizontal scrolling
47993      */
47994     ensureVisible : function(row, col, hscroll)
47995     {
47996         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
47997         //return null; //disable for testing.
47998         if(typeof row != "number"){
47999             row = row.rowIndex;
48000         }
48001         if(row < 0 && row >= this.ds.getCount()){
48002             return  null;
48003         }
48004         col = (col !== undefined ? col : 0);
48005         var cm = this.grid.colModel;
48006         while(cm.isHidden(col)){
48007             col++;
48008         }
48009
48010         var el = this.getCell(row, col);
48011         if(!el){
48012             return null;
48013         }
48014         var c = this.scroller.dom;
48015
48016         var ctop = parseInt(el.offsetTop, 10);
48017         var cleft = parseInt(el.offsetLeft, 10);
48018         var cbot = ctop + el.offsetHeight;
48019         var cright = cleft + el.offsetWidth;
48020         
48021         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48022         var stop = parseInt(c.scrollTop, 10);
48023         var sleft = parseInt(c.scrollLeft, 10);
48024         var sbot = stop + ch;
48025         var sright = sleft + c.clientWidth;
48026         /*
48027         Roo.log('GridView.ensureVisible:' +
48028                 ' ctop:' + ctop +
48029                 ' c.clientHeight:' + c.clientHeight +
48030                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48031                 ' stop:' + stop +
48032                 ' cbot:' + cbot +
48033                 ' sbot:' + sbot +
48034                 ' ch:' + ch  
48035                 );
48036         */
48037         if(ctop < stop){
48038              c.scrollTop = ctop;
48039             //Roo.log("set scrolltop to ctop DISABLE?");
48040         }else if(cbot > sbot){
48041             //Roo.log("set scrolltop to cbot-ch");
48042             c.scrollTop = cbot-ch;
48043         }
48044         
48045         if(hscroll !== false){
48046             if(cleft < sleft){
48047                 c.scrollLeft = cleft;
48048             }else if(cright > sright){
48049                 c.scrollLeft = cright-c.clientWidth;
48050             }
48051         }
48052          
48053         return el;
48054     },
48055
48056     updateColumns : function(){
48057         this.grid.stopEditing();
48058         var cm = this.grid.colModel, colIds = this.getColumnIds();
48059         //var totalWidth = cm.getTotalWidth();
48060         var pos = 0;
48061         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48062             //if(cm.isHidden(i)) continue;
48063             var w = cm.getColumnWidth(i);
48064             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48065             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48066         }
48067         this.updateSplitters();
48068     },
48069
48070     generateRules : function(cm){
48071         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48072         Roo.util.CSS.removeStyleSheet(rulesId);
48073         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48074             var cid = cm.getColumnId(i);
48075             var align = '';
48076             if(cm.config[i].align){
48077                 align = 'text-align:'+cm.config[i].align+';';
48078             }
48079             var hidden = '';
48080             if(cm.isHidden(i)){
48081                 hidden = 'display:none;';
48082             }
48083             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48084             ruleBuf.push(
48085                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48086                     this.hdSelector, cid, " {\n", align, width, "}\n",
48087                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48088                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48089         }
48090         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48091     },
48092
48093     updateSplitters : function(){
48094         var cm = this.cm, s = this.getSplitters();
48095         if(s){ // splitters not created yet
48096             var pos = 0, locked = true;
48097             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48098                 if(cm.isHidden(i)) continue;
48099                 var w = cm.getColumnWidth(i); // make sure it's a number
48100                 if(!cm.isLocked(i) && locked){
48101                     pos = 0;
48102                     locked = false;
48103                 }
48104                 pos += w;
48105                 s[i].style.left = (pos-this.splitOffset) + "px";
48106             }
48107         }
48108     },
48109
48110     handleHiddenChange : function(colModel, colIndex, hidden){
48111         if(hidden){
48112             this.hideColumn(colIndex);
48113         }else{
48114             this.unhideColumn(colIndex);
48115         }
48116     },
48117
48118     hideColumn : function(colIndex){
48119         var cid = this.getColumnId(colIndex);
48120         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48121         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48122         if(Roo.isSafari){
48123             this.updateHeaders();
48124         }
48125         this.updateSplitters();
48126         this.layout();
48127     },
48128
48129     unhideColumn : function(colIndex){
48130         var cid = this.getColumnId(colIndex);
48131         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48132         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48133
48134         if(Roo.isSafari){
48135             this.updateHeaders();
48136         }
48137         this.updateSplitters();
48138         this.layout();
48139     },
48140
48141     insertRows : function(dm, firstRow, lastRow, isUpdate){
48142         if(firstRow == 0 && lastRow == dm.getCount()-1){
48143             this.refresh();
48144         }else{
48145             if(!isUpdate){
48146                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48147             }
48148             var s = this.getScrollState();
48149             var markup = this.renderRows(firstRow, lastRow);
48150             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48151             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48152             this.restoreScroll(s);
48153             if(!isUpdate){
48154                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48155                 this.syncRowHeights(firstRow, lastRow);
48156                 this.stripeRows(firstRow);
48157                 this.layout();
48158             }
48159         }
48160     },
48161
48162     bufferRows : function(markup, target, index){
48163         var before = null, trows = target.rows, tbody = target.tBodies[0];
48164         if(index < trows.length){
48165             before = trows[index];
48166         }
48167         var b = document.createElement("div");
48168         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48169         var rows = b.firstChild.rows;
48170         for(var i = 0, len = rows.length; i < len; i++){
48171             if(before){
48172                 tbody.insertBefore(rows[0], before);
48173             }else{
48174                 tbody.appendChild(rows[0]);
48175             }
48176         }
48177         b.innerHTML = "";
48178         b = null;
48179     },
48180
48181     deleteRows : function(dm, firstRow, lastRow){
48182         if(dm.getRowCount()<1){
48183             this.fireEvent("beforerefresh", this);
48184             this.mainBody.update("");
48185             this.lockedBody.update("");
48186             this.fireEvent("refresh", this);
48187         }else{
48188             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48189             var bt = this.getBodyTable();
48190             var tbody = bt.firstChild;
48191             var rows = bt.rows;
48192             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48193                 tbody.removeChild(rows[firstRow]);
48194             }
48195             this.stripeRows(firstRow);
48196             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48197         }
48198     },
48199
48200     updateRows : function(dataSource, firstRow, lastRow){
48201         var s = this.getScrollState();
48202         this.refresh();
48203         this.restoreScroll(s);
48204     },
48205
48206     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48207         if(!noRefresh){
48208            this.refresh();
48209         }
48210         this.updateHeaderSortState();
48211     },
48212
48213     getScrollState : function(){
48214         
48215         var sb = this.scroller.dom;
48216         return {left: sb.scrollLeft, top: sb.scrollTop};
48217     },
48218
48219     stripeRows : function(startRow){
48220         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48221             return;
48222         }
48223         startRow = startRow || 0;
48224         var rows = this.getBodyTable().rows;
48225         var lrows = this.getLockedTable().rows;
48226         var cls = ' x-grid-row-alt ';
48227         for(var i = startRow, len = rows.length; i < len; i++){
48228             var row = rows[i], lrow = lrows[i];
48229             var isAlt = ((i+1) % 2 == 0);
48230             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48231             if(isAlt == hasAlt){
48232                 continue;
48233             }
48234             if(isAlt){
48235                 row.className += " x-grid-row-alt";
48236             }else{
48237                 row.className = row.className.replace("x-grid-row-alt", "");
48238             }
48239             if(lrow){
48240                 lrow.className = row.className;
48241             }
48242         }
48243     },
48244
48245     restoreScroll : function(state){
48246         //Roo.log('GridView.restoreScroll');
48247         var sb = this.scroller.dom;
48248         sb.scrollLeft = state.left;
48249         sb.scrollTop = state.top;
48250         this.syncScroll();
48251     },
48252
48253     syncScroll : function(){
48254         //Roo.log('GridView.syncScroll');
48255         var sb = this.scroller.dom;
48256         var sh = this.mainHd.dom;
48257         var bs = this.mainBody.dom;
48258         var lv = this.lockedBody.dom;
48259         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48260         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48261     },
48262
48263     handleScroll : function(e){
48264         this.syncScroll();
48265         var sb = this.scroller.dom;
48266         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48267         e.stopEvent();
48268     },
48269
48270     handleWheel : function(e){
48271         var d = e.getWheelDelta();
48272         this.scroller.dom.scrollTop -= d*22;
48273         // set this here to prevent jumpy scrolling on large tables
48274         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48275         e.stopEvent();
48276     },
48277
48278     renderRows : function(startRow, endRow){
48279         // pull in all the crap needed to render rows
48280         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48281         var colCount = cm.getColumnCount();
48282
48283         if(ds.getCount() < 1){
48284             return ["", ""];
48285         }
48286
48287         // build a map for all the columns
48288         var cs = [];
48289         for(var i = 0; i < colCount; i++){
48290             var name = cm.getDataIndex(i);
48291             cs[i] = {
48292                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48293                 renderer : cm.getRenderer(i),
48294                 id : cm.getColumnId(i),
48295                 locked : cm.isLocked(i)
48296             };
48297         }
48298
48299         startRow = startRow || 0;
48300         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48301
48302         // records to render
48303         var rs = ds.getRange(startRow, endRow);
48304
48305         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48306     },
48307
48308     // As much as I hate to duplicate code, this was branched because FireFox really hates
48309     // [].join("") on strings. The performance difference was substantial enough to
48310     // branch this function
48311     doRender : Roo.isGecko ?
48312             function(cs, rs, ds, startRow, colCount, stripe){
48313                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48314                 // buffers
48315                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48316                 
48317                 var hasListener = this.grid.hasListener('rowclass');
48318                 var rowcfg = {};
48319                 for(var j = 0, len = rs.length; j < len; j++){
48320                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48321                     for(var i = 0; i < colCount; i++){
48322                         c = cs[i];
48323                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48324                         p.id = c.id;
48325                         p.css = p.attr = "";
48326                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48327                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48328                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48329                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48330                         }
48331                         var markup = ct.apply(p);
48332                         if(!c.locked){
48333                             cb+= markup;
48334                         }else{
48335                             lcb+= markup;
48336                         }
48337                     }
48338                     var alt = [];
48339                     if(stripe && ((rowIndex+1) % 2 == 0)){
48340                         alt.push("x-grid-row-alt")
48341                     }
48342                     if(r.dirty){
48343                         alt.push(  " x-grid-dirty-row");
48344                     }
48345                     rp.cells = lcb;
48346                     if(this.getRowClass){
48347                         alt.push(this.getRowClass(r, rowIndex));
48348                     }
48349                     if (hasListener) {
48350                         rowcfg = {
48351                              
48352                             record: r,
48353                             rowIndex : rowIndex,
48354                             rowClass : ''
48355                         }
48356                         this.grid.fireEvent('rowclass', this, rowcfg);
48357                         alt.push(rowcfg.rowClass);
48358                     }
48359                     rp.alt = alt.join(" ");
48360                     lbuf+= rt.apply(rp);
48361                     rp.cells = cb;
48362                     buf+=  rt.apply(rp);
48363                 }
48364                 return [lbuf, buf];
48365             } :
48366             function(cs, rs, ds, startRow, colCount, stripe){
48367                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48368                 // buffers
48369                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48370                 var hasListener = this.grid.hasListener('rowclass');
48371                 var rowcfg = {};
48372                 for(var j = 0, len = rs.length; j < len; j++){
48373                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48374                     for(var i = 0; i < colCount; i++){
48375                         c = cs[i];
48376                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48377                         p.id = c.id;
48378                         p.css = p.attr = "";
48379                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48380                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48381                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48382                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48383                         }
48384                         var markup = ct.apply(p);
48385                         if(!c.locked){
48386                             cb[cb.length] = markup;
48387                         }else{
48388                             lcb[lcb.length] = markup;
48389                         }
48390                     }
48391                     var alt = [];
48392                     if(stripe && ((rowIndex+1) % 2 == 0)){
48393                         alt.push( "x-grid-row-alt");
48394                     }
48395                     if(r.dirty){
48396                         alt.push(" x-grid-dirty-row");
48397                     }
48398                     rp.cells = lcb;
48399                     if(this.getRowClass){
48400                         alt.push( this.getRowClass(r, rowIndex));
48401                     }
48402                     if (hasListener) {
48403                         rowcfg = {
48404                              
48405                             record: r,
48406                             rowIndex : rowIndex,
48407                             rowClass : ''
48408                         }
48409                         this.grid.fireEvent('rowclass', this, rowcfg);
48410                         alt.push(rowcfg.rowClass);
48411                     }
48412                     rp.alt = alt.join(" ");
48413                     rp.cells = lcb.join("");
48414                     lbuf[lbuf.length] = rt.apply(rp);
48415                     rp.cells = cb.join("");
48416                     buf[buf.length] =  rt.apply(rp);
48417                 }
48418                 return [lbuf.join(""), buf.join("")];
48419             },
48420
48421     renderBody : function(){
48422         var markup = this.renderRows();
48423         var bt = this.templates.body;
48424         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48425     },
48426
48427     /**
48428      * Refreshes the grid
48429      * @param {Boolean} headersToo
48430      */
48431     refresh : function(headersToo){
48432         this.fireEvent("beforerefresh", this);
48433         this.grid.stopEditing();
48434         var result = this.renderBody();
48435         this.lockedBody.update(result[0]);
48436         this.mainBody.update(result[1]);
48437         if(headersToo === true){
48438             this.updateHeaders();
48439             this.updateColumns();
48440             this.updateSplitters();
48441             this.updateHeaderSortState();
48442         }
48443         this.syncRowHeights();
48444         this.layout();
48445         this.fireEvent("refresh", this);
48446     },
48447
48448     handleColumnMove : function(cm, oldIndex, newIndex){
48449         this.indexMap = null;
48450         var s = this.getScrollState();
48451         this.refresh(true);
48452         this.restoreScroll(s);
48453         this.afterMove(newIndex);
48454     },
48455
48456     afterMove : function(colIndex){
48457         if(this.enableMoveAnim && Roo.enableFx){
48458             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48459         }
48460         // if multisort - fix sortOrder, and reload..
48461         if (this.grid.dataSource.multiSort) {
48462             // the we can call sort again..
48463             var dm = this.grid.dataSource;
48464             var cm = this.grid.colModel;
48465             var so = [];
48466             for(var i = 0; i < cm.config.length; i++ ) {
48467                 
48468                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48469                     continue; // dont' bother, it's not in sort list or being set.
48470                 }
48471                 
48472                 so.push(cm.config[i].dataIndex);
48473             };
48474             dm.sortOrder = so;
48475             dm.load(dm.lastOptions);
48476             
48477             
48478         }
48479         
48480     },
48481
48482     updateCell : function(dm, rowIndex, dataIndex){
48483         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48484         if(typeof colIndex == "undefined"){ // not present in grid
48485             return;
48486         }
48487         var cm = this.grid.colModel;
48488         var cell = this.getCell(rowIndex, colIndex);
48489         var cellText = this.getCellText(rowIndex, colIndex);
48490
48491         var p = {
48492             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48493             id : cm.getColumnId(colIndex),
48494             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48495         };
48496         var renderer = cm.getRenderer(colIndex);
48497         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48498         if(typeof val == "undefined" || val === "") val = "&#160;";
48499         cellText.innerHTML = val;
48500         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48501         this.syncRowHeights(rowIndex, rowIndex);
48502     },
48503
48504     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48505         var maxWidth = 0;
48506         if(this.grid.autoSizeHeaders){
48507             var h = this.getHeaderCellMeasure(colIndex);
48508             maxWidth = Math.max(maxWidth, h.scrollWidth);
48509         }
48510         var tb, index;
48511         if(this.cm.isLocked(colIndex)){
48512             tb = this.getLockedTable();
48513             index = colIndex;
48514         }else{
48515             tb = this.getBodyTable();
48516             index = colIndex - this.cm.getLockedCount();
48517         }
48518         if(tb && tb.rows){
48519             var rows = tb.rows;
48520             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48521             for(var i = 0; i < stopIndex; i++){
48522                 var cell = rows[i].childNodes[index].firstChild;
48523                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48524             }
48525         }
48526         return maxWidth + /*margin for error in IE*/ 5;
48527     },
48528     /**
48529      * Autofit a column to its content.
48530      * @param {Number} colIndex
48531      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48532      */
48533      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48534          if(this.cm.isHidden(colIndex)){
48535              return; // can't calc a hidden column
48536          }
48537         if(forceMinSize){
48538             var cid = this.cm.getColumnId(colIndex);
48539             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48540            if(this.grid.autoSizeHeaders){
48541                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48542            }
48543         }
48544         var newWidth = this.calcColumnWidth(colIndex);
48545         this.cm.setColumnWidth(colIndex,
48546             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48547         if(!suppressEvent){
48548             this.grid.fireEvent("columnresize", colIndex, newWidth);
48549         }
48550     },
48551
48552     /**
48553      * Autofits all columns to their content and then expands to fit any extra space in the grid
48554      */
48555      autoSizeColumns : function(){
48556         var cm = this.grid.colModel;
48557         var colCount = cm.getColumnCount();
48558         for(var i = 0; i < colCount; i++){
48559             this.autoSizeColumn(i, true, true);
48560         }
48561         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48562             this.fitColumns();
48563         }else{
48564             this.updateColumns();
48565             this.layout();
48566         }
48567     },
48568
48569     /**
48570      * Autofits all columns to the grid's width proportionate with their current size
48571      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48572      */
48573     fitColumns : function(reserveScrollSpace){
48574         var cm = this.grid.colModel;
48575         var colCount = cm.getColumnCount();
48576         var cols = [];
48577         var width = 0;
48578         var i, w;
48579         for (i = 0; i < colCount; i++){
48580             if(!cm.isHidden(i) && !cm.isFixed(i)){
48581                 w = cm.getColumnWidth(i);
48582                 cols.push(i);
48583                 cols.push(w);
48584                 width += w;
48585             }
48586         }
48587         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48588         if(reserveScrollSpace){
48589             avail -= 17;
48590         }
48591         var frac = (avail - cm.getTotalWidth())/width;
48592         while (cols.length){
48593             w = cols.pop();
48594             i = cols.pop();
48595             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48596         }
48597         this.updateColumns();
48598         this.layout();
48599     },
48600
48601     onRowSelect : function(rowIndex){
48602         var row = this.getRowComposite(rowIndex);
48603         row.addClass("x-grid-row-selected");
48604     },
48605
48606     onRowDeselect : function(rowIndex){
48607         var row = this.getRowComposite(rowIndex);
48608         row.removeClass("x-grid-row-selected");
48609     },
48610
48611     onCellSelect : function(row, col){
48612         var cell = this.getCell(row, col);
48613         if(cell){
48614             Roo.fly(cell).addClass("x-grid-cell-selected");
48615         }
48616     },
48617
48618     onCellDeselect : function(row, col){
48619         var cell = this.getCell(row, col);
48620         if(cell){
48621             Roo.fly(cell).removeClass("x-grid-cell-selected");
48622         }
48623     },
48624
48625     updateHeaderSortState : function(){
48626         
48627         // sort state can be single { field: xxx, direction : yyy}
48628         // or   { xxx=>ASC , yyy : DESC ..... }
48629         
48630         var mstate = {};
48631         if (!this.ds.multiSort) { 
48632             var state = this.ds.getSortState();
48633             if(!state){
48634                 return;
48635             }
48636             mstate[state.field] = state.direction;
48637             // FIXME... - this is not used here.. but might be elsewhere..
48638             this.sortState = state;
48639             
48640         } else {
48641             mstate = this.ds.sortToggle;
48642         }
48643         //remove existing sort classes..
48644         
48645         var sc = this.sortClasses;
48646         var hds = this.el.select(this.headerSelector).removeClass(sc);
48647         
48648         for(var f in mstate) {
48649         
48650             var sortColumn = this.cm.findColumnIndex(f);
48651             
48652             if(sortColumn != -1){
48653                 var sortDir = mstate[f];        
48654                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48655             }
48656         }
48657         
48658          
48659         
48660     },
48661
48662
48663     handleHeaderClick : function(g, index){
48664         if(this.headersDisabled){
48665             return;
48666         }
48667         var dm = g.dataSource, cm = g.colModel;
48668         if(!cm.isSortable(index)){
48669             return;
48670         }
48671         g.stopEditing();
48672         
48673         if (dm.multiSort) {
48674             // update the sortOrder
48675             var so = [];
48676             for(var i = 0; i < cm.config.length; i++ ) {
48677                 
48678                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
48679                     continue; // dont' bother, it's not in sort list or being set.
48680                 }
48681                 
48682                 so.push(cm.config[i].dataIndex);
48683             };
48684             dm.sortOrder = so;
48685         }
48686         
48687         
48688         dm.sort(cm.getDataIndex(index));
48689     },
48690
48691
48692     destroy : function(){
48693         if(this.colMenu){
48694             this.colMenu.removeAll();
48695             Roo.menu.MenuMgr.unregister(this.colMenu);
48696             this.colMenu.getEl().remove();
48697             delete this.colMenu;
48698         }
48699         if(this.hmenu){
48700             this.hmenu.removeAll();
48701             Roo.menu.MenuMgr.unregister(this.hmenu);
48702             this.hmenu.getEl().remove();
48703             delete this.hmenu;
48704         }
48705         if(this.grid.enableColumnMove){
48706             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48707             if(dds){
48708                 for(var dd in dds){
48709                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48710                         var elid = dds[dd].dragElId;
48711                         dds[dd].unreg();
48712                         Roo.get(elid).remove();
48713                     } else if(dds[dd].config.isTarget){
48714                         dds[dd].proxyTop.remove();
48715                         dds[dd].proxyBottom.remove();
48716                         dds[dd].unreg();
48717                     }
48718                     if(Roo.dd.DDM.locationCache[dd]){
48719                         delete Roo.dd.DDM.locationCache[dd];
48720                     }
48721                 }
48722                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48723             }
48724         }
48725         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48726         this.bind(null, null);
48727         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48728     },
48729
48730     handleLockChange : function(){
48731         this.refresh(true);
48732     },
48733
48734     onDenyColumnLock : function(){
48735
48736     },
48737
48738     onDenyColumnHide : function(){
48739
48740     },
48741
48742     handleHdMenuClick : function(item){
48743         var index = this.hdCtxIndex;
48744         var cm = this.cm, ds = this.ds;
48745         switch(item.id){
48746             case "asc":
48747                 ds.sort(cm.getDataIndex(index), "ASC");
48748                 break;
48749             case "desc":
48750                 ds.sort(cm.getDataIndex(index), "DESC");
48751                 break;
48752             case "lock":
48753                 var lc = cm.getLockedCount();
48754                 if(cm.getColumnCount(true) <= lc+1){
48755                     this.onDenyColumnLock();
48756                     return;
48757                 }
48758                 if(lc != index){
48759                     cm.setLocked(index, true, true);
48760                     cm.moveColumn(index, lc);
48761                     this.grid.fireEvent("columnmove", index, lc);
48762                 }else{
48763                     cm.setLocked(index, true);
48764                 }
48765             break;
48766             case "unlock":
48767                 var lc = cm.getLockedCount();
48768                 if((lc-1) != index){
48769                     cm.setLocked(index, false, true);
48770                     cm.moveColumn(index, lc-1);
48771                     this.grid.fireEvent("columnmove", index, lc-1);
48772                 }else{
48773                     cm.setLocked(index, false);
48774                 }
48775             break;
48776             default:
48777                 index = cm.getIndexById(item.id.substr(4));
48778                 if(index != -1){
48779                     if(item.checked && cm.getColumnCount(true) <= 1){
48780                         this.onDenyColumnHide();
48781                         return false;
48782                     }
48783                     cm.setHidden(index, item.checked);
48784                 }
48785         }
48786         return true;
48787     },
48788
48789     beforeColMenuShow : function(){
48790         var cm = this.cm,  colCount = cm.getColumnCount();
48791         this.colMenu.removeAll();
48792         for(var i = 0; i < colCount; i++){
48793             this.colMenu.add(new Roo.menu.CheckItem({
48794                 id: "col-"+cm.getColumnId(i),
48795                 text: cm.getColumnHeader(i),
48796                 checked: !cm.isHidden(i),
48797                 hideOnClick:false
48798             }));
48799         }
48800     },
48801
48802     handleHdCtx : function(g, index, e){
48803         e.stopEvent();
48804         var hd = this.getHeaderCell(index);
48805         this.hdCtxIndex = index;
48806         var ms = this.hmenu.items, cm = this.cm;
48807         ms.get("asc").setDisabled(!cm.isSortable(index));
48808         ms.get("desc").setDisabled(!cm.isSortable(index));
48809         if(this.grid.enableColLock !== false){
48810             ms.get("lock").setDisabled(cm.isLocked(index));
48811             ms.get("unlock").setDisabled(!cm.isLocked(index));
48812         }
48813         this.hmenu.show(hd, "tl-bl");
48814     },
48815
48816     handleHdOver : function(e){
48817         var hd = this.findHeaderCell(e.getTarget());
48818         if(hd && !this.headersDisabled){
48819             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48820                this.fly(hd).addClass("x-grid-hd-over");
48821             }
48822         }
48823     },
48824
48825     handleHdOut : function(e){
48826         var hd = this.findHeaderCell(e.getTarget());
48827         if(hd){
48828             this.fly(hd).removeClass("x-grid-hd-over");
48829         }
48830     },
48831
48832     handleSplitDblClick : function(e, t){
48833         var i = this.getCellIndex(t);
48834         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48835             this.autoSizeColumn(i, true);
48836             this.layout();
48837         }
48838     },
48839
48840     render : function(){
48841
48842         var cm = this.cm;
48843         var colCount = cm.getColumnCount();
48844
48845         if(this.grid.monitorWindowResize === true){
48846             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48847         }
48848         var header = this.renderHeaders();
48849         var body = this.templates.body.apply({rows:""});
48850         var html = this.templates.master.apply({
48851             lockedBody: body,
48852             body: body,
48853             lockedHeader: header[0],
48854             header: header[1]
48855         });
48856
48857         //this.updateColumns();
48858
48859         this.grid.getGridEl().dom.innerHTML = html;
48860
48861         this.initElements();
48862         
48863         // a kludge to fix the random scolling effect in webkit
48864         this.el.on("scroll", function() {
48865             this.el.dom.scrollTop=0; // hopefully not recursive..
48866         },this);
48867
48868         this.scroller.on("scroll", this.handleScroll, this);
48869         this.lockedBody.on("mousewheel", this.handleWheel, this);
48870         this.mainBody.on("mousewheel", this.handleWheel, this);
48871
48872         this.mainHd.on("mouseover", this.handleHdOver, this);
48873         this.mainHd.on("mouseout", this.handleHdOut, this);
48874         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48875                 {delegate: "."+this.splitClass});
48876
48877         this.lockedHd.on("mouseover", this.handleHdOver, this);
48878         this.lockedHd.on("mouseout", this.handleHdOut, this);
48879         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48880                 {delegate: "."+this.splitClass});
48881
48882         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48883             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48884         }
48885
48886         this.updateSplitters();
48887
48888         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48889             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48890             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48891         }
48892
48893         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48894             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48895             this.hmenu.add(
48896                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48897                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48898             );
48899             if(this.grid.enableColLock !== false){
48900                 this.hmenu.add('-',
48901                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48902                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48903                 );
48904             }
48905             if(this.grid.enableColumnHide !== false){
48906
48907                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48908                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48909                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48910
48911                 this.hmenu.add('-',
48912                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48913                 );
48914             }
48915             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48916
48917             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48918         }
48919
48920         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48921             this.dd = new Roo.grid.GridDragZone(this.grid, {
48922                 ddGroup : this.grid.ddGroup || 'GridDD'
48923             });
48924         }
48925
48926         /*
48927         for(var i = 0; i < colCount; i++){
48928             if(cm.isHidden(i)){
48929                 this.hideColumn(i);
48930             }
48931             if(cm.config[i].align){
48932                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48933                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48934             }
48935         }*/
48936         
48937         this.updateHeaderSortState();
48938
48939         this.beforeInitialResize();
48940         this.layout(true);
48941
48942         // two part rendering gives faster view to the user
48943         this.renderPhase2.defer(1, this);
48944     },
48945
48946     renderPhase2 : function(){
48947         // render the rows now
48948         this.refresh();
48949         if(this.grid.autoSizeColumns){
48950             this.autoSizeColumns();
48951         }
48952     },
48953
48954     beforeInitialResize : function(){
48955
48956     },
48957
48958     onColumnSplitterMoved : function(i, w){
48959         this.userResized = true;
48960         var cm = this.grid.colModel;
48961         cm.setColumnWidth(i, w, true);
48962         var cid = cm.getColumnId(i);
48963         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48964         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48965         this.updateSplitters();
48966         this.layout();
48967         this.grid.fireEvent("columnresize", i, w);
48968     },
48969
48970     syncRowHeights : function(startIndex, endIndex){
48971         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48972             startIndex = startIndex || 0;
48973             var mrows = this.getBodyTable().rows;
48974             var lrows = this.getLockedTable().rows;
48975             var len = mrows.length-1;
48976             endIndex = Math.min(endIndex || len, len);
48977             for(var i = startIndex; i <= endIndex; i++){
48978                 var m = mrows[i], l = lrows[i];
48979                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48980                 m.style.height = l.style.height = h + "px";
48981             }
48982         }
48983     },
48984
48985     layout : function(initialRender, is2ndPass){
48986         var g = this.grid;
48987         var auto = g.autoHeight;
48988         var scrollOffset = 16;
48989         var c = g.getGridEl(), cm = this.cm,
48990                 expandCol = g.autoExpandColumn,
48991                 gv = this;
48992         //c.beginMeasure();
48993
48994         if(!c.dom.offsetWidth){ // display:none?
48995             if(initialRender){
48996                 this.lockedWrap.show();
48997                 this.mainWrap.show();
48998             }
48999             return;
49000         }
49001
49002         var hasLock = this.cm.isLocked(0);
49003
49004         var tbh = this.headerPanel.getHeight();
49005         var bbh = this.footerPanel.getHeight();
49006
49007         if(auto){
49008             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49009             var newHeight = ch + c.getBorderWidth("tb");
49010             if(g.maxHeight){
49011                 newHeight = Math.min(g.maxHeight, newHeight);
49012             }
49013             c.setHeight(newHeight);
49014         }
49015
49016         if(g.autoWidth){
49017             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49018         }
49019
49020         var s = this.scroller;
49021
49022         var csize = c.getSize(true);
49023
49024         this.el.setSize(csize.width, csize.height);
49025
49026         this.headerPanel.setWidth(csize.width);
49027         this.footerPanel.setWidth(csize.width);
49028
49029         var hdHeight = this.mainHd.getHeight();
49030         var vw = csize.width;
49031         var vh = csize.height - (tbh + bbh);
49032
49033         s.setSize(vw, vh);
49034
49035         var bt = this.getBodyTable();
49036         var ltWidth = hasLock ?
49037                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49038
49039         var scrollHeight = bt.offsetHeight;
49040         var scrollWidth = ltWidth + bt.offsetWidth;
49041         var vscroll = false, hscroll = false;
49042
49043         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49044
49045         var lw = this.lockedWrap, mw = this.mainWrap;
49046         var lb = this.lockedBody, mb = this.mainBody;
49047
49048         setTimeout(function(){
49049             var t = s.dom.offsetTop;
49050             var w = s.dom.clientWidth,
49051                 h = s.dom.clientHeight;
49052
49053             lw.setTop(t);
49054             lw.setSize(ltWidth, h);
49055
49056             mw.setLeftTop(ltWidth, t);
49057             mw.setSize(w-ltWidth, h);
49058
49059             lb.setHeight(h-hdHeight);
49060             mb.setHeight(h-hdHeight);
49061
49062             if(is2ndPass !== true && !gv.userResized && expandCol){
49063                 // high speed resize without full column calculation
49064                 
49065                 var ci = cm.getIndexById(expandCol);
49066                 if (ci < 0) {
49067                     ci = cm.findColumnIndex(expandCol);
49068                 }
49069                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49070                 var expandId = cm.getColumnId(ci);
49071                 var  tw = cm.getTotalWidth(false);
49072                 var currentWidth = cm.getColumnWidth(ci);
49073                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49074                 if(currentWidth != cw){
49075                     cm.setColumnWidth(ci, cw, true);
49076                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49077                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49078                     gv.updateSplitters();
49079                     gv.layout(false, true);
49080                 }
49081             }
49082
49083             if(initialRender){
49084                 lw.show();
49085                 mw.show();
49086             }
49087             //c.endMeasure();
49088         }, 10);
49089     },
49090
49091     onWindowResize : function(){
49092         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49093             return;
49094         }
49095         this.layout();
49096     },
49097
49098     appendFooter : function(parentEl){
49099         return null;
49100     },
49101
49102     sortAscText : "Sort Ascending",
49103     sortDescText : "Sort Descending",
49104     lockText : "Lock Column",
49105     unlockText : "Unlock Column",
49106     columnsText : "Columns"
49107 });
49108
49109
49110 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49111     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49112     this.proxy.el.addClass('x-grid3-col-dd');
49113 };
49114
49115 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49116     handleMouseDown : function(e){
49117
49118     },
49119
49120     callHandleMouseDown : function(e){
49121         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49122     }
49123 });
49124 /*
49125  * Based on:
49126  * Ext JS Library 1.1.1
49127  * Copyright(c) 2006-2007, Ext JS, LLC.
49128  *
49129  * Originally Released Under LGPL - original licence link has changed is not relivant.
49130  *
49131  * Fork - LGPL
49132  * <script type="text/javascript">
49133  */
49134  
49135 // private
49136 // This is a support class used internally by the Grid components
49137 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49138     this.grid = grid;
49139     this.view = grid.getView();
49140     this.proxy = this.view.resizeProxy;
49141     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49142         "gridSplitters" + this.grid.getGridEl().id, {
49143         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49144     });
49145     this.setHandleElId(Roo.id(hd));
49146     this.setOuterHandleElId(Roo.id(hd2));
49147     this.scroll = false;
49148 };
49149 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49150     fly: Roo.Element.fly,
49151
49152     b4StartDrag : function(x, y){
49153         this.view.headersDisabled = true;
49154         this.proxy.setHeight(this.view.mainWrap.getHeight());
49155         var w = this.cm.getColumnWidth(this.cellIndex);
49156         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49157         this.resetConstraints();
49158         this.setXConstraint(minw, 1000);
49159         this.setYConstraint(0, 0);
49160         this.minX = x - minw;
49161         this.maxX = x + 1000;
49162         this.startPos = x;
49163         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49164     },
49165
49166
49167     handleMouseDown : function(e){
49168         ev = Roo.EventObject.setEvent(e);
49169         var t = this.fly(ev.getTarget());
49170         if(t.hasClass("x-grid-split")){
49171             this.cellIndex = this.view.getCellIndex(t.dom);
49172             this.split = t.dom;
49173             this.cm = this.grid.colModel;
49174             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49175                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49176             }
49177         }
49178     },
49179
49180     endDrag : function(e){
49181         this.view.headersDisabled = false;
49182         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49183         var diff = endX - this.startPos;
49184         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49185     },
49186
49187     autoOffset : function(){
49188         this.setDelta(0,0);
49189     }
49190 });/*
49191  * Based on:
49192  * Ext JS Library 1.1.1
49193  * Copyright(c) 2006-2007, Ext JS, LLC.
49194  *
49195  * Originally Released Under LGPL - original licence link has changed is not relivant.
49196  *
49197  * Fork - LGPL
49198  * <script type="text/javascript">
49199  */
49200  
49201 // private
49202 // This is a support class used internally by the Grid components
49203 Roo.grid.GridDragZone = function(grid, config){
49204     this.view = grid.getView();
49205     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49206     if(this.view.lockedBody){
49207         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49208         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49209     }
49210     this.scroll = false;
49211     this.grid = grid;
49212     this.ddel = document.createElement('div');
49213     this.ddel.className = 'x-grid-dd-wrap';
49214 };
49215
49216 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49217     ddGroup : "GridDD",
49218
49219     getDragData : function(e){
49220         var t = Roo.lib.Event.getTarget(e);
49221         var rowIndex = this.view.findRowIndex(t);
49222         if(rowIndex !== false){
49223             var sm = this.grid.selModel;
49224             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49225               //  sm.mouseDown(e, t);
49226             //}
49227             if (e.hasModifier()){
49228                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49229             }
49230             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49231         }
49232         return false;
49233     },
49234
49235     onInitDrag : function(e){
49236         var data = this.dragData;
49237         this.ddel.innerHTML = this.grid.getDragDropText();
49238         this.proxy.update(this.ddel);
49239         // fire start drag?
49240     },
49241
49242     afterRepair : function(){
49243         this.dragging = false;
49244     },
49245
49246     getRepairXY : function(e, data){
49247         return false;
49248     },
49249
49250     onEndDrag : function(data, e){
49251         // fire end drag?
49252     },
49253
49254     onValidDrop : function(dd, e, id){
49255         // fire drag drop?
49256         this.hideProxy();
49257     },
49258
49259     beforeInvalidDrop : function(e, id){
49260
49261     }
49262 });/*
49263  * Based on:
49264  * Ext JS Library 1.1.1
49265  * Copyright(c) 2006-2007, Ext JS, LLC.
49266  *
49267  * Originally Released Under LGPL - original licence link has changed is not relivant.
49268  *
49269  * Fork - LGPL
49270  * <script type="text/javascript">
49271  */
49272  
49273
49274 /**
49275  * @class Roo.grid.ColumnModel
49276  * @extends Roo.util.Observable
49277  * This is the default implementation of a ColumnModel used by the Grid. It defines
49278  * the columns in the grid.
49279  * <br>Usage:<br>
49280  <pre><code>
49281  var colModel = new Roo.grid.ColumnModel([
49282         {header: "Ticker", width: 60, sortable: true, locked: true},
49283         {header: "Company Name", width: 150, sortable: true},
49284         {header: "Market Cap.", width: 100, sortable: true},
49285         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49286         {header: "Employees", width: 100, sortable: true, resizable: false}
49287  ]);
49288  </code></pre>
49289  * <p>
49290  
49291  * The config options listed for this class are options which may appear in each
49292  * individual column definition.
49293  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49294  * @constructor
49295  * @param {Object} config An Array of column config objects. See this class's
49296  * config objects for details.
49297 */
49298 Roo.grid.ColumnModel = function(config){
49299         /**
49300      * The config passed into the constructor
49301      */
49302     this.config = config;
49303     this.lookup = {};
49304
49305     // if no id, create one
49306     // if the column does not have a dataIndex mapping,
49307     // map it to the order it is in the config
49308     for(var i = 0, len = config.length; i < len; i++){
49309         var c = config[i];
49310         if(typeof c.dataIndex == "undefined"){
49311             c.dataIndex = i;
49312         }
49313         if(typeof c.renderer == "string"){
49314             c.renderer = Roo.util.Format[c.renderer];
49315         }
49316         if(typeof c.id == "undefined"){
49317             c.id = Roo.id();
49318         }
49319         if(c.editor && c.editor.xtype){
49320             c.editor  = Roo.factory(c.editor, Roo.grid);
49321         }
49322         if(c.editor && c.editor.isFormField){
49323             c.editor = new Roo.grid.GridEditor(c.editor);
49324         }
49325         this.lookup[c.id] = c;
49326     }
49327
49328     /**
49329      * The width of columns which have no width specified (defaults to 100)
49330      * @type Number
49331      */
49332     this.defaultWidth = 100;
49333
49334     /**
49335      * Default sortable of columns which have no sortable specified (defaults to false)
49336      * @type Boolean
49337      */
49338     this.defaultSortable = false;
49339
49340     this.addEvents({
49341         /**
49342              * @event widthchange
49343              * Fires when the width of a column changes.
49344              * @param {ColumnModel} this
49345              * @param {Number} columnIndex The column index
49346              * @param {Number} newWidth The new width
49347              */
49348             "widthchange": true,
49349         /**
49350              * @event headerchange
49351              * Fires when the text of a header changes.
49352              * @param {ColumnModel} this
49353              * @param {Number} columnIndex The column index
49354              * @param {Number} newText The new header text
49355              */
49356             "headerchange": true,
49357         /**
49358              * @event hiddenchange
49359              * Fires when a column is hidden or "unhidden".
49360              * @param {ColumnModel} this
49361              * @param {Number} columnIndex The column index
49362              * @param {Boolean} hidden true if hidden, false otherwise
49363              */
49364             "hiddenchange": true,
49365             /**
49366          * @event columnmoved
49367          * Fires when a column is moved.
49368          * @param {ColumnModel} this
49369          * @param {Number} oldIndex
49370          * @param {Number} newIndex
49371          */
49372         "columnmoved" : true,
49373         /**
49374          * @event columlockchange
49375          * Fires when a column's locked state is changed
49376          * @param {ColumnModel} this
49377          * @param {Number} colIndex
49378          * @param {Boolean} locked true if locked
49379          */
49380         "columnlockchange" : true
49381     });
49382     Roo.grid.ColumnModel.superclass.constructor.call(this);
49383 };
49384 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49385     /**
49386      * @cfg {String} header The header text to display in the Grid view.
49387      */
49388     /**
49389      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49390      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49391      * specified, the column's index is used as an index into the Record's data Array.
49392      */
49393     /**
49394      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49395      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49396      */
49397     /**
49398      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49399      * Defaults to the value of the {@link #defaultSortable} property.
49400      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49401      */
49402     /**
49403      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49404      */
49405     /**
49406      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49407      */
49408     /**
49409      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49410      */
49411     /**
49412      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49413      */
49414     /**
49415      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49416      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49417      * default renderer uses the raw data value.
49418      */
49419        /**
49420      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49421      */
49422     /**
49423      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49424      */
49425
49426     /**
49427      * Returns the id of the column at the specified index.
49428      * @param {Number} index The column index
49429      * @return {String} the id
49430      */
49431     getColumnId : function(index){
49432         return this.config[index].id;
49433     },
49434
49435     /**
49436      * Returns the column for a specified id.
49437      * @param {String} id The column id
49438      * @return {Object} the column
49439      */
49440     getColumnById : function(id){
49441         return this.lookup[id];
49442     },
49443
49444     
49445     /**
49446      * Returns the column for a specified dataIndex.
49447      * @param {String} dataIndex The column dataIndex
49448      * @return {Object|Boolean} the column or false if not found
49449      */
49450     getColumnByDataIndex: function(dataIndex){
49451         var index = this.findColumnIndex(dataIndex);
49452         return index > -1 ? this.config[index] : false;
49453     },
49454     
49455     /**
49456      * Returns the index for a specified column id.
49457      * @param {String} id The column id
49458      * @return {Number} the index, or -1 if not found
49459      */
49460     getIndexById : function(id){
49461         for(var i = 0, len = this.config.length; i < len; i++){
49462             if(this.config[i].id == id){
49463                 return i;
49464             }
49465         }
49466         return -1;
49467     },
49468     
49469     /**
49470      * Returns the index for a specified column dataIndex.
49471      * @param {String} dataIndex The column dataIndex
49472      * @return {Number} the index, or -1 if not found
49473      */
49474     
49475     findColumnIndex : function(dataIndex){
49476         for(var i = 0, len = this.config.length; i < len; i++){
49477             if(this.config[i].dataIndex == dataIndex){
49478                 return i;
49479             }
49480         }
49481         return -1;
49482     },
49483     
49484     
49485     moveColumn : function(oldIndex, newIndex){
49486         var c = this.config[oldIndex];
49487         this.config.splice(oldIndex, 1);
49488         this.config.splice(newIndex, 0, c);
49489         this.dataMap = null;
49490         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49491     },
49492
49493     isLocked : function(colIndex){
49494         return this.config[colIndex].locked === true;
49495     },
49496
49497     setLocked : function(colIndex, value, suppressEvent){
49498         if(this.isLocked(colIndex) == value){
49499             return;
49500         }
49501         this.config[colIndex].locked = value;
49502         if(!suppressEvent){
49503             this.fireEvent("columnlockchange", this, colIndex, value);
49504         }
49505     },
49506
49507     getTotalLockedWidth : function(){
49508         var totalWidth = 0;
49509         for(var i = 0; i < this.config.length; i++){
49510             if(this.isLocked(i) && !this.isHidden(i)){
49511                 this.totalWidth += this.getColumnWidth(i);
49512             }
49513         }
49514         return totalWidth;
49515     },
49516
49517     getLockedCount : function(){
49518         for(var i = 0, len = this.config.length; i < len; i++){
49519             if(!this.isLocked(i)){
49520                 return i;
49521             }
49522         }
49523     },
49524
49525     /**
49526      * Returns the number of columns.
49527      * @return {Number}
49528      */
49529     getColumnCount : function(visibleOnly){
49530         if(visibleOnly === true){
49531             var c = 0;
49532             for(var i = 0, len = this.config.length; i < len; i++){
49533                 if(!this.isHidden(i)){
49534                     c++;
49535                 }
49536             }
49537             return c;
49538         }
49539         return this.config.length;
49540     },
49541
49542     /**
49543      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49544      * @param {Function} fn
49545      * @param {Object} scope (optional)
49546      * @return {Array} result
49547      */
49548     getColumnsBy : function(fn, scope){
49549         var r = [];
49550         for(var i = 0, len = this.config.length; i < len; i++){
49551             var c = this.config[i];
49552             if(fn.call(scope||this, c, i) === true){
49553                 r[r.length] = c;
49554             }
49555         }
49556         return r;
49557     },
49558
49559     /**
49560      * Returns true if the specified column is sortable.
49561      * @param {Number} col The column index
49562      * @return {Boolean}
49563      */
49564     isSortable : function(col){
49565         if(typeof this.config[col].sortable == "undefined"){
49566             return this.defaultSortable;
49567         }
49568         return this.config[col].sortable;
49569     },
49570
49571     /**
49572      * Returns the rendering (formatting) function defined for the column.
49573      * @param {Number} col The column index.
49574      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49575      */
49576     getRenderer : function(col){
49577         if(!this.config[col].renderer){
49578             return Roo.grid.ColumnModel.defaultRenderer;
49579         }
49580         return this.config[col].renderer;
49581     },
49582
49583     /**
49584      * Sets the rendering (formatting) function for a column.
49585      * @param {Number} col The column index
49586      * @param {Function} fn The function to use to process the cell's raw data
49587      * to return HTML markup for the grid view. The render function is called with
49588      * the following parameters:<ul>
49589      * <li>Data value.</li>
49590      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49591      * <li>css A CSS style string to apply to the table cell.</li>
49592      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49593      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49594      * <li>Row index</li>
49595      * <li>Column index</li>
49596      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49597      */
49598     setRenderer : function(col, fn){
49599         this.config[col].renderer = fn;
49600     },
49601
49602     /**
49603      * Returns the width for the specified column.
49604      * @param {Number} col The column index
49605      * @return {Number}
49606      */
49607     getColumnWidth : function(col){
49608         return this.config[col].width * 1 || this.defaultWidth;
49609     },
49610
49611     /**
49612      * Sets the width for a column.
49613      * @param {Number} col The column index
49614      * @param {Number} width The new width
49615      */
49616     setColumnWidth : function(col, width, suppressEvent){
49617         this.config[col].width = width;
49618         this.totalWidth = null;
49619         if(!suppressEvent){
49620              this.fireEvent("widthchange", this, col, width);
49621         }
49622     },
49623
49624     /**
49625      * Returns the total width of all columns.
49626      * @param {Boolean} includeHidden True to include hidden column widths
49627      * @return {Number}
49628      */
49629     getTotalWidth : function(includeHidden){
49630         if(!this.totalWidth){
49631             this.totalWidth = 0;
49632             for(var i = 0, len = this.config.length; i < len; i++){
49633                 if(includeHidden || !this.isHidden(i)){
49634                     this.totalWidth += this.getColumnWidth(i);
49635                 }
49636             }
49637         }
49638         return this.totalWidth;
49639     },
49640
49641     /**
49642      * Returns the header for the specified column.
49643      * @param {Number} col The column index
49644      * @return {String}
49645      */
49646     getColumnHeader : function(col){
49647         return this.config[col].header;
49648     },
49649
49650     /**
49651      * Sets the header for a column.
49652      * @param {Number} col The column index
49653      * @param {String} header The new header
49654      */
49655     setColumnHeader : function(col, header){
49656         this.config[col].header = header;
49657         this.fireEvent("headerchange", this, col, header);
49658     },
49659
49660     /**
49661      * Returns the tooltip for the specified column.
49662      * @param {Number} col The column index
49663      * @return {String}
49664      */
49665     getColumnTooltip : function(col){
49666             return this.config[col].tooltip;
49667     },
49668     /**
49669      * Sets the tooltip for a column.
49670      * @param {Number} col The column index
49671      * @param {String} tooltip The new tooltip
49672      */
49673     setColumnTooltip : function(col, tooltip){
49674             this.config[col].tooltip = tooltip;
49675     },
49676
49677     /**
49678      * Returns the dataIndex for the specified column.
49679      * @param {Number} col The column index
49680      * @return {Number}
49681      */
49682     getDataIndex : function(col){
49683         return this.config[col].dataIndex;
49684     },
49685
49686     /**
49687      * Sets the dataIndex for a column.
49688      * @param {Number} col The column index
49689      * @param {Number} dataIndex The new dataIndex
49690      */
49691     setDataIndex : function(col, dataIndex){
49692         this.config[col].dataIndex = dataIndex;
49693     },
49694
49695     
49696     
49697     /**
49698      * Returns true if the cell is editable.
49699      * @param {Number} colIndex The column index
49700      * @param {Number} rowIndex The row index
49701      * @return {Boolean}
49702      */
49703     isCellEditable : function(colIndex, rowIndex){
49704         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49705     },
49706
49707     /**
49708      * Returns the editor defined for the cell/column.
49709      * return false or null to disable editing.
49710      * @param {Number} colIndex The column index
49711      * @param {Number} rowIndex The row index
49712      * @return {Object}
49713      */
49714     getCellEditor : function(colIndex, rowIndex){
49715         return this.config[colIndex].editor;
49716     },
49717
49718     /**
49719      * Sets if a column is editable.
49720      * @param {Number} col The column index
49721      * @param {Boolean} editable True if the column is editable
49722      */
49723     setEditable : function(col, editable){
49724         this.config[col].editable = editable;
49725     },
49726
49727
49728     /**
49729      * Returns true if the column is hidden.
49730      * @param {Number} colIndex The column index
49731      * @return {Boolean}
49732      */
49733     isHidden : function(colIndex){
49734         return this.config[colIndex].hidden;
49735     },
49736
49737
49738     /**
49739      * Returns true if the column width cannot be changed
49740      */
49741     isFixed : function(colIndex){
49742         return this.config[colIndex].fixed;
49743     },
49744
49745     /**
49746      * Returns true if the column can be resized
49747      * @return {Boolean}
49748      */
49749     isResizable : function(colIndex){
49750         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49751     },
49752     /**
49753      * Sets if a column is hidden.
49754      * @param {Number} colIndex The column index
49755      * @param {Boolean} hidden True if the column is hidden
49756      */
49757     setHidden : function(colIndex, hidden){
49758         this.config[colIndex].hidden = hidden;
49759         this.totalWidth = null;
49760         this.fireEvent("hiddenchange", this, colIndex, hidden);
49761     },
49762
49763     /**
49764      * Sets the editor for a column.
49765      * @param {Number} col The column index
49766      * @param {Object} editor The editor object
49767      */
49768     setEditor : function(col, editor){
49769         this.config[col].editor = editor;
49770     }
49771 });
49772
49773 Roo.grid.ColumnModel.defaultRenderer = function(value){
49774         if(typeof value == "string" && value.length < 1){
49775             return "&#160;";
49776         }
49777         return value;
49778 };
49779
49780 // Alias for backwards compatibility
49781 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49782 /*
49783  * Based on:
49784  * Ext JS Library 1.1.1
49785  * Copyright(c) 2006-2007, Ext JS, LLC.
49786  *
49787  * Originally Released Under LGPL - original licence link has changed is not relivant.
49788  *
49789  * Fork - LGPL
49790  * <script type="text/javascript">
49791  */
49792
49793 /**
49794  * @class Roo.grid.AbstractSelectionModel
49795  * @extends Roo.util.Observable
49796  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49797  * implemented by descendant classes.  This class should not be directly instantiated.
49798  * @constructor
49799  */
49800 Roo.grid.AbstractSelectionModel = function(){
49801     this.locked = false;
49802     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49803 };
49804
49805 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49806     /** @ignore Called by the grid automatically. Do not call directly. */
49807     init : function(grid){
49808         this.grid = grid;
49809         this.initEvents();
49810     },
49811
49812     /**
49813      * Locks the selections.
49814      */
49815     lock : function(){
49816         this.locked = true;
49817     },
49818
49819     /**
49820      * Unlocks the selections.
49821      */
49822     unlock : function(){
49823         this.locked = false;
49824     },
49825
49826     /**
49827      * Returns true if the selections are locked.
49828      * @return {Boolean}
49829      */
49830     isLocked : function(){
49831         return this.locked;
49832     }
49833 });/*
49834  * Based on:
49835  * Ext JS Library 1.1.1
49836  * Copyright(c) 2006-2007, Ext JS, LLC.
49837  *
49838  * Originally Released Under LGPL - original licence link has changed is not relivant.
49839  *
49840  * Fork - LGPL
49841  * <script type="text/javascript">
49842  */
49843 /**
49844  * @extends Roo.grid.AbstractSelectionModel
49845  * @class Roo.grid.RowSelectionModel
49846  * The default SelectionModel used by {@link Roo.grid.Grid}.
49847  * It supports multiple selections and keyboard selection/navigation. 
49848  * @constructor
49849  * @param {Object} config
49850  */
49851 Roo.grid.RowSelectionModel = function(config){
49852     Roo.apply(this, config);
49853     this.selections = new Roo.util.MixedCollection(false, function(o){
49854         return o.id;
49855     });
49856
49857     this.last = false;
49858     this.lastActive = false;
49859
49860     this.addEvents({
49861         /**
49862              * @event selectionchange
49863              * Fires when the selection changes
49864              * @param {SelectionModel} this
49865              */
49866             "selectionchange" : true,
49867         /**
49868              * @event afterselectionchange
49869              * Fires after the selection changes (eg. by key press or clicking)
49870              * @param {SelectionModel} this
49871              */
49872             "afterselectionchange" : true,
49873         /**
49874              * @event beforerowselect
49875              * Fires when a row is selected being selected, return false to cancel.
49876              * @param {SelectionModel} this
49877              * @param {Number} rowIndex The selected index
49878              * @param {Boolean} keepExisting False if other selections will be cleared
49879              */
49880             "beforerowselect" : true,
49881         /**
49882              * @event rowselect
49883              * Fires when a row is selected.
49884              * @param {SelectionModel} this
49885              * @param {Number} rowIndex The selected index
49886              * @param {Roo.data.Record} r The record
49887              */
49888             "rowselect" : true,
49889         /**
49890              * @event rowdeselect
49891              * Fires when a row is deselected.
49892              * @param {SelectionModel} this
49893              * @param {Number} rowIndex The selected index
49894              */
49895         "rowdeselect" : true
49896     });
49897     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49898     this.locked = false;
49899 };
49900
49901 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49902     /**
49903      * @cfg {Boolean} singleSelect
49904      * True to allow selection of only one row at a time (defaults to false)
49905      */
49906     singleSelect : false,
49907
49908     // private
49909     initEvents : function(){
49910
49911         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49912             this.grid.on("mousedown", this.handleMouseDown, this);
49913         }else{ // allow click to work like normal
49914             this.grid.on("rowclick", this.handleDragableRowClick, this);
49915         }
49916
49917         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49918             "up" : function(e){
49919                 if(!e.shiftKey){
49920                     this.selectPrevious(e.shiftKey);
49921                 }else if(this.last !== false && this.lastActive !== false){
49922                     var last = this.last;
49923                     this.selectRange(this.last,  this.lastActive-1);
49924                     this.grid.getView().focusRow(this.lastActive);
49925                     if(last !== false){
49926                         this.last = last;
49927                     }
49928                 }else{
49929                     this.selectFirstRow();
49930                 }
49931                 this.fireEvent("afterselectionchange", this);
49932             },
49933             "down" : function(e){
49934                 if(!e.shiftKey){
49935                     this.selectNext(e.shiftKey);
49936                 }else if(this.last !== false && this.lastActive !== false){
49937                     var last = this.last;
49938                     this.selectRange(this.last,  this.lastActive+1);
49939                     this.grid.getView().focusRow(this.lastActive);
49940                     if(last !== false){
49941                         this.last = last;
49942                     }
49943                 }else{
49944                     this.selectFirstRow();
49945                 }
49946                 this.fireEvent("afterselectionchange", this);
49947             },
49948             scope: this
49949         });
49950
49951         var view = this.grid.view;
49952         view.on("refresh", this.onRefresh, this);
49953         view.on("rowupdated", this.onRowUpdated, this);
49954         view.on("rowremoved", this.onRemove, this);
49955     },
49956
49957     // private
49958     onRefresh : function(){
49959         var ds = this.grid.dataSource, i, v = this.grid.view;
49960         var s = this.selections;
49961         s.each(function(r){
49962             if((i = ds.indexOfId(r.id)) != -1){
49963                 v.onRowSelect(i);
49964             }else{
49965                 s.remove(r);
49966             }
49967         });
49968     },
49969
49970     // private
49971     onRemove : function(v, index, r){
49972         this.selections.remove(r);
49973     },
49974
49975     // private
49976     onRowUpdated : function(v, index, r){
49977         if(this.isSelected(r)){
49978             v.onRowSelect(index);
49979         }
49980     },
49981
49982     /**
49983      * Select records.
49984      * @param {Array} records The records to select
49985      * @param {Boolean} keepExisting (optional) True to keep existing selections
49986      */
49987     selectRecords : function(records, keepExisting){
49988         if(!keepExisting){
49989             this.clearSelections();
49990         }
49991         var ds = this.grid.dataSource;
49992         for(var i = 0, len = records.length; i < len; i++){
49993             this.selectRow(ds.indexOf(records[i]), true);
49994         }
49995     },
49996
49997     /**
49998      * Gets the number of selected rows.
49999      * @return {Number}
50000      */
50001     getCount : function(){
50002         return this.selections.length;
50003     },
50004
50005     /**
50006      * Selects the first row in the grid.
50007      */
50008     selectFirstRow : function(){
50009         this.selectRow(0);
50010     },
50011
50012     /**
50013      * Select the last row.
50014      * @param {Boolean} keepExisting (optional) True to keep existing selections
50015      */
50016     selectLastRow : function(keepExisting){
50017         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50018     },
50019
50020     /**
50021      * Selects the row immediately following the last selected row.
50022      * @param {Boolean} keepExisting (optional) True to keep existing selections
50023      */
50024     selectNext : function(keepExisting){
50025         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50026             this.selectRow(this.last+1, keepExisting);
50027             this.grid.getView().focusRow(this.last);
50028         }
50029     },
50030
50031     /**
50032      * Selects the row that precedes the last selected row.
50033      * @param {Boolean} keepExisting (optional) True to keep existing selections
50034      */
50035     selectPrevious : function(keepExisting){
50036         if(this.last){
50037             this.selectRow(this.last-1, keepExisting);
50038             this.grid.getView().focusRow(this.last);
50039         }
50040     },
50041
50042     /**
50043      * Returns the selected records
50044      * @return {Array} Array of selected records
50045      */
50046     getSelections : function(){
50047         return [].concat(this.selections.items);
50048     },
50049
50050     /**
50051      * Returns the first selected record.
50052      * @return {Record}
50053      */
50054     getSelected : function(){
50055         return this.selections.itemAt(0);
50056     },
50057
50058
50059     /**
50060      * Clears all selections.
50061      */
50062     clearSelections : function(fast){
50063         if(this.locked) return;
50064         if(fast !== true){
50065             var ds = this.grid.dataSource;
50066             var s = this.selections;
50067             s.each(function(r){
50068                 this.deselectRow(ds.indexOfId(r.id));
50069             }, this);
50070             s.clear();
50071         }else{
50072             this.selections.clear();
50073         }
50074         this.last = false;
50075     },
50076
50077
50078     /**
50079      * Selects all rows.
50080      */
50081     selectAll : function(){
50082         if(this.locked) return;
50083         this.selections.clear();
50084         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50085             this.selectRow(i, true);
50086         }
50087     },
50088
50089     /**
50090      * Returns True if there is a selection.
50091      * @return {Boolean}
50092      */
50093     hasSelection : function(){
50094         return this.selections.length > 0;
50095     },
50096
50097     /**
50098      * Returns True if the specified row is selected.
50099      * @param {Number/Record} record The record or index of the record to check
50100      * @return {Boolean}
50101      */
50102     isSelected : function(index){
50103         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50104         return (r && this.selections.key(r.id) ? true : false);
50105     },
50106
50107     /**
50108      * Returns True if the specified record id is selected.
50109      * @param {String} id The id of record to check
50110      * @return {Boolean}
50111      */
50112     isIdSelected : function(id){
50113         return (this.selections.key(id) ? true : false);
50114     },
50115
50116     // private
50117     handleMouseDown : function(e, t){
50118         var view = this.grid.getView(), rowIndex;
50119         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50120             return;
50121         };
50122         if(e.shiftKey && this.last !== false){
50123             var last = this.last;
50124             this.selectRange(last, rowIndex, e.ctrlKey);
50125             this.last = last; // reset the last
50126             view.focusRow(rowIndex);
50127         }else{
50128             var isSelected = this.isSelected(rowIndex);
50129             if(e.button !== 0 && isSelected){
50130                 view.focusRow(rowIndex);
50131             }else if(e.ctrlKey && isSelected){
50132                 this.deselectRow(rowIndex);
50133             }else if(!isSelected){
50134                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50135                 view.focusRow(rowIndex);
50136             }
50137         }
50138         this.fireEvent("afterselectionchange", this);
50139     },
50140     // private
50141     handleDragableRowClick :  function(grid, rowIndex, e) 
50142     {
50143         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50144             this.selectRow(rowIndex, false);
50145             grid.view.focusRow(rowIndex);
50146              this.fireEvent("afterselectionchange", this);
50147         }
50148     },
50149     
50150     /**
50151      * Selects multiple rows.
50152      * @param {Array} rows Array of the indexes of the row to select
50153      * @param {Boolean} keepExisting (optional) True to keep existing selections
50154      */
50155     selectRows : function(rows, keepExisting){
50156         if(!keepExisting){
50157             this.clearSelections();
50158         }
50159         for(var i = 0, len = rows.length; i < len; i++){
50160             this.selectRow(rows[i], true);
50161         }
50162     },
50163
50164     /**
50165      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50166      * @param {Number} startRow The index of the first row in the range
50167      * @param {Number} endRow The index of the last row in the range
50168      * @param {Boolean} keepExisting (optional) True to retain existing selections
50169      */
50170     selectRange : function(startRow, endRow, keepExisting){
50171         if(this.locked) return;
50172         if(!keepExisting){
50173             this.clearSelections();
50174         }
50175         if(startRow <= endRow){
50176             for(var i = startRow; i <= endRow; i++){
50177                 this.selectRow(i, true);
50178             }
50179         }else{
50180             for(var i = startRow; i >= endRow; i--){
50181                 this.selectRow(i, true);
50182             }
50183         }
50184     },
50185
50186     /**
50187      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50188      * @param {Number} startRow The index of the first row in the range
50189      * @param {Number} endRow The index of the last row in the range
50190      */
50191     deselectRange : function(startRow, endRow, preventViewNotify){
50192         if(this.locked) return;
50193         for(var i = startRow; i <= endRow; i++){
50194             this.deselectRow(i, preventViewNotify);
50195         }
50196     },
50197
50198     /**
50199      * Selects a row.
50200      * @param {Number} row The index of the row to select
50201      * @param {Boolean} keepExisting (optional) True to keep existing selections
50202      */
50203     selectRow : function(index, keepExisting, preventViewNotify){
50204         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50205         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50206             if(!keepExisting || this.singleSelect){
50207                 this.clearSelections();
50208             }
50209             var r = this.grid.dataSource.getAt(index);
50210             this.selections.add(r);
50211             this.last = this.lastActive = index;
50212             if(!preventViewNotify){
50213                 this.grid.getView().onRowSelect(index);
50214             }
50215             this.fireEvent("rowselect", this, index, r);
50216             this.fireEvent("selectionchange", this);
50217         }
50218     },
50219
50220     /**
50221      * Deselects a row.
50222      * @param {Number} row The index of the row to deselect
50223      */
50224     deselectRow : function(index, preventViewNotify){
50225         if(this.locked) return;
50226         if(this.last == index){
50227             this.last = false;
50228         }
50229         if(this.lastActive == index){
50230             this.lastActive = false;
50231         }
50232         var r = this.grid.dataSource.getAt(index);
50233         this.selections.remove(r);
50234         if(!preventViewNotify){
50235             this.grid.getView().onRowDeselect(index);
50236         }
50237         this.fireEvent("rowdeselect", this, index);
50238         this.fireEvent("selectionchange", this);
50239     },
50240
50241     // private
50242     restoreLast : function(){
50243         if(this._last){
50244             this.last = this._last;
50245         }
50246     },
50247
50248     // private
50249     acceptsNav : function(row, col, cm){
50250         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50251     },
50252
50253     // private
50254     onEditorKey : function(field, e){
50255         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50256         if(k == e.TAB){
50257             e.stopEvent();
50258             ed.completeEdit();
50259             if(e.shiftKey){
50260                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50261             }else{
50262                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50263             }
50264         }else if(k == e.ENTER && !e.ctrlKey){
50265             e.stopEvent();
50266             ed.completeEdit();
50267             if(e.shiftKey){
50268                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50269             }else{
50270                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50271             }
50272         }else if(k == e.ESC){
50273             ed.cancelEdit();
50274         }
50275         if(newCell){
50276             g.startEditing(newCell[0], newCell[1]);
50277         }
50278     }
50279 });/*
50280  * Based on:
50281  * Ext JS Library 1.1.1
50282  * Copyright(c) 2006-2007, Ext JS, LLC.
50283  *
50284  * Originally Released Under LGPL - original licence link has changed is not relivant.
50285  *
50286  * Fork - LGPL
50287  * <script type="text/javascript">
50288  */
50289 /**
50290  * @class Roo.grid.CellSelectionModel
50291  * @extends Roo.grid.AbstractSelectionModel
50292  * This class provides the basic implementation for cell selection in a grid.
50293  * @constructor
50294  * @param {Object} config The object containing the configuration of this model.
50295  */
50296 Roo.grid.CellSelectionModel = function(config){
50297     Roo.apply(this, config);
50298
50299     this.selection = null;
50300
50301     this.addEvents({
50302         /**
50303              * @event beforerowselect
50304              * Fires before a cell is selected.
50305              * @param {SelectionModel} this
50306              * @param {Number} rowIndex The selected row index
50307              * @param {Number} colIndex The selected cell index
50308              */
50309             "beforecellselect" : true,
50310         /**
50311              * @event cellselect
50312              * Fires when a cell is selected.
50313              * @param {SelectionModel} this
50314              * @param {Number} rowIndex The selected row index
50315              * @param {Number} colIndex The selected cell index
50316              */
50317             "cellselect" : true,
50318         /**
50319              * @event selectionchange
50320              * Fires when the active selection changes.
50321              * @param {SelectionModel} this
50322              * @param {Object} selection null for no selection or an object (o) with two properties
50323                 <ul>
50324                 <li>o.record: the record object for the row the selection is in</li>
50325                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50326                 </ul>
50327              */
50328             "selectionchange" : true
50329     });
50330     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50331 };
50332
50333 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50334
50335     /** @ignore */
50336     initEvents : function(){
50337         this.grid.on("mousedown", this.handleMouseDown, this);
50338         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50339         var view = this.grid.view;
50340         view.on("refresh", this.onViewChange, this);
50341         view.on("rowupdated", this.onRowUpdated, this);
50342         view.on("beforerowremoved", this.clearSelections, this);
50343         view.on("beforerowsinserted", this.clearSelections, this);
50344         if(this.grid.isEditor){
50345             this.grid.on("beforeedit", this.beforeEdit,  this);
50346         }
50347     },
50348
50349         //private
50350     beforeEdit : function(e){
50351         this.select(e.row, e.column, false, true, e.record);
50352     },
50353
50354         //private
50355     onRowUpdated : function(v, index, r){
50356         if(this.selection && this.selection.record == r){
50357             v.onCellSelect(index, this.selection.cell[1]);
50358         }
50359     },
50360
50361         //private
50362     onViewChange : function(){
50363         this.clearSelections(true);
50364     },
50365
50366         /**
50367          * Returns the currently selected cell,.
50368          * @return {Array} The selected cell (row, column) or null if none selected.
50369          */
50370     getSelectedCell : function(){
50371         return this.selection ? this.selection.cell : null;
50372     },
50373
50374     /**
50375      * Clears all selections.
50376      * @param {Boolean} true to prevent the gridview from being notified about the change.
50377      */
50378     clearSelections : function(preventNotify){
50379         var s = this.selection;
50380         if(s){
50381             if(preventNotify !== true){
50382                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50383             }
50384             this.selection = null;
50385             this.fireEvent("selectionchange", this, null);
50386         }
50387     },
50388
50389     /**
50390      * Returns true if there is a selection.
50391      * @return {Boolean}
50392      */
50393     hasSelection : function(){
50394         return this.selection ? true : false;
50395     },
50396
50397     /** @ignore */
50398     handleMouseDown : function(e, t){
50399         var v = this.grid.getView();
50400         if(this.isLocked()){
50401             return;
50402         };
50403         var row = v.findRowIndex(t);
50404         var cell = v.findCellIndex(t);
50405         if(row !== false && cell !== false){
50406             this.select(row, cell);
50407         }
50408     },
50409
50410     /**
50411      * Selects a cell.
50412      * @param {Number} rowIndex
50413      * @param {Number} collIndex
50414      */
50415     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50416         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50417             this.clearSelections();
50418             r = r || this.grid.dataSource.getAt(rowIndex);
50419             this.selection = {
50420                 record : r,
50421                 cell : [rowIndex, colIndex]
50422             };
50423             if(!preventViewNotify){
50424                 var v = this.grid.getView();
50425                 v.onCellSelect(rowIndex, colIndex);
50426                 if(preventFocus !== true){
50427                     v.focusCell(rowIndex, colIndex);
50428                 }
50429             }
50430             this.fireEvent("cellselect", this, rowIndex, colIndex);
50431             this.fireEvent("selectionchange", this, this.selection);
50432         }
50433     },
50434
50435         //private
50436     isSelectable : function(rowIndex, colIndex, cm){
50437         return !cm.isHidden(colIndex);
50438     },
50439
50440     /** @ignore */
50441     handleKeyDown : function(e){
50442         Roo.log('Cell Sel Model handleKeyDown');
50443         if(!e.isNavKeyPress()){
50444             return;
50445         }
50446         var g = this.grid, s = this.selection;
50447         if(!s){
50448             e.stopEvent();
50449             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50450             if(cell){
50451                 this.select(cell[0], cell[1]);
50452             }
50453             return;
50454         }
50455         var sm = this;
50456         var walk = function(row, col, step){
50457             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50458         };
50459         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50460         var newCell;
50461
50462         switch(k){
50463             case e.TAB:
50464                 // handled by onEditorKey
50465                 if (g.isEditor && g.editing) {
50466                     return;
50467                 }
50468                 if(e.shiftKey){
50469                      newCell = walk(r, c-1, -1);
50470                 }else{
50471                      newCell = walk(r, c+1, 1);
50472                 }
50473              break;
50474              case e.DOWN:
50475                  newCell = walk(r+1, c, 1);
50476              break;
50477              case e.UP:
50478                  newCell = walk(r-1, c, -1);
50479              break;
50480              case e.RIGHT:
50481                  newCell = walk(r, c+1, 1);
50482              break;
50483              case e.LEFT:
50484                  newCell = walk(r, c-1, -1);
50485              break;
50486              case e.ENTER:
50487                  if(g.isEditor && !g.editing){
50488                     g.startEditing(r, c);
50489                     e.stopEvent();
50490                     return;
50491                 }
50492              break;
50493         };
50494         if(newCell){
50495             this.select(newCell[0], newCell[1]);
50496             e.stopEvent();
50497         }
50498     },
50499
50500     acceptsNav : function(row, col, cm){
50501         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50502     },
50503
50504     onEditorKey : function(field, e){
50505         
50506         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50507         ///Roo.log('onEditorKey' + k);
50508         
50509         if(k == e.TAB){
50510             if(e.shiftKey){
50511                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50512             }else{
50513                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50514             }
50515             e.stopEvent();
50516         }else if(k == e.ENTER && !e.ctrlKey){
50517             ed.completeEdit();
50518             e.stopEvent();
50519             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50520         }else if(k == e.ESC){
50521             ed.cancelEdit();
50522         }
50523         
50524         
50525         if(newCell){
50526             //Roo.log('next cell after edit');
50527             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50528         }
50529     }
50530 });/*
50531  * Based on:
50532  * Ext JS Library 1.1.1
50533  * Copyright(c) 2006-2007, Ext JS, LLC.
50534  *
50535  * Originally Released Under LGPL - original licence link has changed is not relivant.
50536  *
50537  * Fork - LGPL
50538  * <script type="text/javascript">
50539  */
50540  
50541 /**
50542  * @class Roo.grid.EditorGrid
50543  * @extends Roo.grid.Grid
50544  * Class for creating and editable grid.
50545  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50546  * The container MUST have some type of size defined for the grid to fill. The container will be 
50547  * automatically set to position relative if it isn't already.
50548  * @param {Object} dataSource The data model to bind to
50549  * @param {Object} colModel The column model with info about this grid's columns
50550  */
50551 Roo.grid.EditorGrid = function(container, config){
50552     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50553     this.getGridEl().addClass("xedit-grid");
50554
50555     if(!this.selModel){
50556         this.selModel = new Roo.grid.CellSelectionModel();
50557     }
50558
50559     this.activeEditor = null;
50560
50561         this.addEvents({
50562             /**
50563              * @event beforeedit
50564              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50565              * <ul style="padding:5px;padding-left:16px;">
50566              * <li>grid - This grid</li>
50567              * <li>record - The record being edited</li>
50568              * <li>field - The field name being edited</li>
50569              * <li>value - The value for the field being edited.</li>
50570              * <li>row - The grid row index</li>
50571              * <li>column - The grid column index</li>
50572              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50573              * </ul>
50574              * @param {Object} e An edit event (see above for description)
50575              */
50576             "beforeedit" : true,
50577             /**
50578              * @event afteredit
50579              * Fires after a cell is edited. <br />
50580              * <ul style="padding:5px;padding-left:16px;">
50581              * <li>grid - This grid</li>
50582              * <li>record - The record being edited</li>
50583              * <li>field - The field name being edited</li>
50584              * <li>value - The value being set</li>
50585              * <li>originalValue - The original value for the field, before the edit.</li>
50586              * <li>row - The grid row index</li>
50587              * <li>column - The grid column index</li>
50588              * </ul>
50589              * @param {Object} e An edit event (see above for description)
50590              */
50591             "afteredit" : true,
50592             /**
50593              * @event validateedit
50594              * Fires after a cell is edited, but before the value is set in the record. 
50595          * You can use this to modify the value being set in the field, Return false
50596              * to cancel the change. The edit event object has the following properties <br />
50597              * <ul style="padding:5px;padding-left:16px;">
50598          * <li>editor - This editor</li>
50599              * <li>grid - This grid</li>
50600              * <li>record - The record being edited</li>
50601              * <li>field - The field name being edited</li>
50602              * <li>value - The value being set</li>
50603              * <li>originalValue - The original value for the field, before the edit.</li>
50604              * <li>row - The grid row index</li>
50605              * <li>column - The grid column index</li>
50606              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50607              * </ul>
50608              * @param {Object} e An edit event (see above for description)
50609              */
50610             "validateedit" : true
50611         });
50612     this.on("bodyscroll", this.stopEditing,  this);
50613     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50614 };
50615
50616 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50617     /**
50618      * @cfg {Number} clicksToEdit
50619      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50620      */
50621     clicksToEdit: 2,
50622
50623     // private
50624     isEditor : true,
50625     // private
50626     trackMouseOver: false, // causes very odd FF errors
50627
50628     onCellDblClick : function(g, row, col){
50629         this.startEditing(row, col);
50630     },
50631
50632     onEditComplete : function(ed, value, startValue){
50633         this.editing = false;
50634         this.activeEditor = null;
50635         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50636         var r = ed.record;
50637         var field = this.colModel.getDataIndex(ed.col);
50638         var e = {
50639             grid: this,
50640             record: r,
50641             field: field,
50642             originalValue: startValue,
50643             value: value,
50644             row: ed.row,
50645             column: ed.col,
50646             cancel:false,
50647             editor: ed
50648         };
50649         if(String(value) !== String(startValue)){
50650             
50651             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50652                 r.set(field, e.value);
50653                 // if we are dealing with a combo box..
50654                 // then we also set the 'name' colum to be the displayField
50655                 if (ed.field.displayField && ed.field.name) {
50656                     r.set(ed.field.name, ed.field.el.dom.value);
50657                 }
50658                 
50659                 delete e.cancel; //?? why!!!
50660                 this.fireEvent("afteredit", e);
50661             }
50662         } else {
50663             this.fireEvent("afteredit", e); // always fire it!
50664         }
50665         this.view.focusCell(ed.row, ed.col);
50666     },
50667
50668     /**
50669      * Starts editing the specified for the specified row/column
50670      * @param {Number} rowIndex
50671      * @param {Number} colIndex
50672      */
50673     startEditing : function(row, col){
50674         this.stopEditing();
50675         if(this.colModel.isCellEditable(col, row)){
50676             this.view.ensureVisible(row, col, true);
50677             var r = this.dataSource.getAt(row);
50678             var field = this.colModel.getDataIndex(col);
50679             var e = {
50680                 grid: this,
50681                 record: r,
50682                 field: field,
50683                 value: r.data[field],
50684                 row: row,
50685                 column: col,
50686                 cancel:false
50687             };
50688             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50689                 this.editing = true;
50690                 var ed = this.colModel.getCellEditor(col, row);
50691                 
50692                 if (!ed) {
50693                     return;
50694                 }
50695                 if(!ed.rendered){
50696                     ed.render(ed.parentEl || document.body);
50697                 }
50698                 ed.field.reset();
50699                 (function(){ // complex but required for focus issues in safari, ie and opera
50700                     ed.row = row;
50701                     ed.col = col;
50702                     ed.record = r;
50703                     ed.on("complete", this.onEditComplete, this, {single: true});
50704                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50705                     this.activeEditor = ed;
50706                     var v = r.data[field];
50707                     ed.startEdit(this.view.getCell(row, col), v);
50708                     // combo's with 'displayField and name set
50709                     if (ed.field.displayField && ed.field.name) {
50710                         ed.field.el.dom.value = r.data[ed.field.name];
50711                     }
50712                     
50713                     
50714                 }).defer(50, this);
50715             }
50716         }
50717     },
50718         
50719     /**
50720      * Stops any active editing
50721      */
50722     stopEditing : function(){
50723         if(this.activeEditor){
50724             this.activeEditor.completeEdit();
50725         }
50726         this.activeEditor = null;
50727     }
50728 });/*
50729  * Based on:
50730  * Ext JS Library 1.1.1
50731  * Copyright(c) 2006-2007, Ext JS, LLC.
50732  *
50733  * Originally Released Under LGPL - original licence link has changed is not relivant.
50734  *
50735  * Fork - LGPL
50736  * <script type="text/javascript">
50737  */
50738
50739 // private - not really -- you end up using it !
50740 // This is a support class used internally by the Grid components
50741
50742 /**
50743  * @class Roo.grid.GridEditor
50744  * @extends Roo.Editor
50745  * Class for creating and editable grid elements.
50746  * @param {Object} config any settings (must include field)
50747  */
50748 Roo.grid.GridEditor = function(field, config){
50749     if (!config && field.field) {
50750         config = field;
50751         field = Roo.factory(config.field, Roo.form);
50752     }
50753     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50754     field.monitorTab = false;
50755 };
50756
50757 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50758     
50759     /**
50760      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50761      */
50762     
50763     alignment: "tl-tl",
50764     autoSize: "width",
50765     hideEl : false,
50766     cls: "x-small-editor x-grid-editor",
50767     shim:false,
50768     shadow:"frame"
50769 });/*
50770  * Based on:
50771  * Ext JS Library 1.1.1
50772  * Copyright(c) 2006-2007, Ext JS, LLC.
50773  *
50774  * Originally Released Under LGPL - original licence link has changed is not relivant.
50775  *
50776  * Fork - LGPL
50777  * <script type="text/javascript">
50778  */
50779   
50780
50781   
50782 Roo.grid.PropertyRecord = Roo.data.Record.create([
50783     {name:'name',type:'string'},  'value'
50784 ]);
50785
50786
50787 Roo.grid.PropertyStore = function(grid, source){
50788     this.grid = grid;
50789     this.store = new Roo.data.Store({
50790         recordType : Roo.grid.PropertyRecord
50791     });
50792     this.store.on('update', this.onUpdate,  this);
50793     if(source){
50794         this.setSource(source);
50795     }
50796     Roo.grid.PropertyStore.superclass.constructor.call(this);
50797 };
50798
50799
50800
50801 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50802     setSource : function(o){
50803         this.source = o;
50804         this.store.removeAll();
50805         var data = [];
50806         for(var k in o){
50807             if(this.isEditableValue(o[k])){
50808                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50809             }
50810         }
50811         this.store.loadRecords({records: data}, {}, true);
50812     },
50813
50814     onUpdate : function(ds, record, type){
50815         if(type == Roo.data.Record.EDIT){
50816             var v = record.data['value'];
50817             var oldValue = record.modified['value'];
50818             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50819                 this.source[record.id] = v;
50820                 record.commit();
50821                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50822             }else{
50823                 record.reject();
50824             }
50825         }
50826     },
50827
50828     getProperty : function(row){
50829        return this.store.getAt(row);
50830     },
50831
50832     isEditableValue: function(val){
50833         if(val && val instanceof Date){
50834             return true;
50835         }else if(typeof val == 'object' || typeof val == 'function'){
50836             return false;
50837         }
50838         return true;
50839     },
50840
50841     setValue : function(prop, value){
50842         this.source[prop] = value;
50843         this.store.getById(prop).set('value', value);
50844     },
50845
50846     getSource : function(){
50847         return this.source;
50848     }
50849 });
50850
50851 Roo.grid.PropertyColumnModel = function(grid, store){
50852     this.grid = grid;
50853     var g = Roo.grid;
50854     g.PropertyColumnModel.superclass.constructor.call(this, [
50855         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50856         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50857     ]);
50858     this.store = store;
50859     this.bselect = Roo.DomHelper.append(document.body, {
50860         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50861             {tag: 'option', value: 'true', html: 'true'},
50862             {tag: 'option', value: 'false', html: 'false'}
50863         ]
50864     });
50865     Roo.id(this.bselect);
50866     var f = Roo.form;
50867     this.editors = {
50868         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50869         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50870         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50871         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50872         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50873     };
50874     this.renderCellDelegate = this.renderCell.createDelegate(this);
50875     this.renderPropDelegate = this.renderProp.createDelegate(this);
50876 };
50877
50878 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50879     
50880     
50881     nameText : 'Name',
50882     valueText : 'Value',
50883     
50884     dateFormat : 'm/j/Y',
50885     
50886     
50887     renderDate : function(dateVal){
50888         return dateVal.dateFormat(this.dateFormat);
50889     },
50890
50891     renderBool : function(bVal){
50892         return bVal ? 'true' : 'false';
50893     },
50894
50895     isCellEditable : function(colIndex, rowIndex){
50896         return colIndex == 1;
50897     },
50898
50899     getRenderer : function(col){
50900         return col == 1 ?
50901             this.renderCellDelegate : this.renderPropDelegate;
50902     },
50903
50904     renderProp : function(v){
50905         return this.getPropertyName(v);
50906     },
50907
50908     renderCell : function(val){
50909         var rv = val;
50910         if(val instanceof Date){
50911             rv = this.renderDate(val);
50912         }else if(typeof val == 'boolean'){
50913             rv = this.renderBool(val);
50914         }
50915         return Roo.util.Format.htmlEncode(rv);
50916     },
50917
50918     getPropertyName : function(name){
50919         var pn = this.grid.propertyNames;
50920         return pn && pn[name] ? pn[name] : name;
50921     },
50922
50923     getCellEditor : function(colIndex, rowIndex){
50924         var p = this.store.getProperty(rowIndex);
50925         var n = p.data['name'], val = p.data['value'];
50926         
50927         if(typeof(this.grid.customEditors[n]) == 'string'){
50928             return this.editors[this.grid.customEditors[n]];
50929         }
50930         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50931             return this.grid.customEditors[n];
50932         }
50933         if(val instanceof Date){
50934             return this.editors['date'];
50935         }else if(typeof val == 'number'){
50936             return this.editors['number'];
50937         }else if(typeof val == 'boolean'){
50938             return this.editors['boolean'];
50939         }else{
50940             return this.editors['string'];
50941         }
50942     }
50943 });
50944
50945 /**
50946  * @class Roo.grid.PropertyGrid
50947  * @extends Roo.grid.EditorGrid
50948  * This class represents the  interface of a component based property grid control.
50949  * <br><br>Usage:<pre><code>
50950  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50951       
50952  });
50953  // set any options
50954  grid.render();
50955  * </code></pre>
50956   
50957  * @constructor
50958  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50959  * The container MUST have some type of size defined for the grid to fill. The container will be
50960  * automatically set to position relative if it isn't already.
50961  * @param {Object} config A config object that sets properties on this grid.
50962  */
50963 Roo.grid.PropertyGrid = function(container, config){
50964     config = config || {};
50965     var store = new Roo.grid.PropertyStore(this);
50966     this.store = store;
50967     var cm = new Roo.grid.PropertyColumnModel(this, store);
50968     store.store.sort('name', 'ASC');
50969     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50970         ds: store.store,
50971         cm: cm,
50972         enableColLock:false,
50973         enableColumnMove:false,
50974         stripeRows:false,
50975         trackMouseOver: false,
50976         clicksToEdit:1
50977     }, config));
50978     this.getGridEl().addClass('x-props-grid');
50979     this.lastEditRow = null;
50980     this.on('columnresize', this.onColumnResize, this);
50981     this.addEvents({
50982          /**
50983              * @event beforepropertychange
50984              * Fires before a property changes (return false to stop?)
50985              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50986              * @param {String} id Record Id
50987              * @param {String} newval New Value
50988          * @param {String} oldval Old Value
50989              */
50990         "beforepropertychange": true,
50991         /**
50992              * @event propertychange
50993              * Fires after a property changes
50994              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50995              * @param {String} id Record Id
50996              * @param {String} newval New Value
50997          * @param {String} oldval Old Value
50998              */
50999         "propertychange": true
51000     });
51001     this.customEditors = this.customEditors || {};
51002 };
51003 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51004     
51005      /**
51006      * @cfg {Object} customEditors map of colnames=> custom editors.
51007      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51008      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51009      * false disables editing of the field.
51010          */
51011     
51012       /**
51013      * @cfg {Object} propertyNames map of property Names to their displayed value
51014          */
51015     
51016     render : function(){
51017         Roo.grid.PropertyGrid.superclass.render.call(this);
51018         this.autoSize.defer(100, this);
51019     },
51020
51021     autoSize : function(){
51022         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51023         if(this.view){
51024             this.view.fitColumns();
51025         }
51026     },
51027
51028     onColumnResize : function(){
51029         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51030         this.autoSize();
51031     },
51032     /**
51033      * Sets the data for the Grid
51034      * accepts a Key => Value object of all the elements avaiable.
51035      * @param {Object} data  to appear in grid.
51036      */
51037     setSource : function(source){
51038         this.store.setSource(source);
51039         //this.autoSize();
51040     },
51041     /**
51042      * Gets all the data from the grid.
51043      * @return {Object} data  data stored in grid
51044      */
51045     getSource : function(){
51046         return this.store.getSource();
51047     }
51048 });/*
51049  * Based on:
51050  * Ext JS Library 1.1.1
51051  * Copyright(c) 2006-2007, Ext JS, LLC.
51052  *
51053  * Originally Released Under LGPL - original licence link has changed is not relivant.
51054  *
51055  * Fork - LGPL
51056  * <script type="text/javascript">
51057  */
51058  
51059 /**
51060  * @class Roo.LoadMask
51061  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51062  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51063  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51064  * element's UpdateManager load indicator and will be destroyed after the initial load.
51065  * @constructor
51066  * Create a new LoadMask
51067  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51068  * @param {Object} config The config object
51069  */
51070 Roo.LoadMask = function(el, config){
51071     this.el = Roo.get(el);
51072     Roo.apply(this, config);
51073     if(this.store){
51074         this.store.on('beforeload', this.onBeforeLoad, this);
51075         this.store.on('load', this.onLoad, this);
51076         this.store.on('loadexception', this.onLoad, this);
51077         this.removeMask = false;
51078     }else{
51079         var um = this.el.getUpdateManager();
51080         um.showLoadIndicator = false; // disable the default indicator
51081         um.on('beforeupdate', this.onBeforeLoad, this);
51082         um.on('update', this.onLoad, this);
51083         um.on('failure', this.onLoad, this);
51084         this.removeMask = true;
51085     }
51086 };
51087
51088 Roo.LoadMask.prototype = {
51089     /**
51090      * @cfg {Boolean} removeMask
51091      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51092      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51093      */
51094     /**
51095      * @cfg {String} msg
51096      * The text to display in a centered loading message box (defaults to 'Loading...')
51097      */
51098     msg : 'Loading...',
51099     /**
51100      * @cfg {String} msgCls
51101      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51102      */
51103     msgCls : 'x-mask-loading',
51104
51105     /**
51106      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51107      * @type Boolean
51108      */
51109     disabled: false,
51110
51111     /**
51112      * Disables the mask to prevent it from being displayed
51113      */
51114     disable : function(){
51115        this.disabled = true;
51116     },
51117
51118     /**
51119      * Enables the mask so that it can be displayed
51120      */
51121     enable : function(){
51122         this.disabled = false;
51123     },
51124
51125     // private
51126     onLoad : function(){
51127         this.el.unmask(this.removeMask);
51128     },
51129
51130     // private
51131     onBeforeLoad : function(){
51132         if(!this.disabled){
51133             this.el.mask(this.msg, this.msgCls);
51134         }
51135     },
51136
51137     // private
51138     destroy : function(){
51139         if(this.store){
51140             this.store.un('beforeload', this.onBeforeLoad, this);
51141             this.store.un('load', this.onLoad, this);
51142             this.store.un('loadexception', this.onLoad, this);
51143         }else{
51144             var um = this.el.getUpdateManager();
51145             um.un('beforeupdate', this.onBeforeLoad, this);
51146             um.un('update', this.onLoad, this);
51147             um.un('failure', this.onLoad, this);
51148         }
51149     }
51150 };/*
51151  * Based on:
51152  * Ext JS Library 1.1.1
51153  * Copyright(c) 2006-2007, Ext JS, LLC.
51154  *
51155  * Originally Released Under LGPL - original licence link has changed is not relivant.
51156  *
51157  * Fork - LGPL
51158  * <script type="text/javascript">
51159  */
51160 Roo.XTemplate = function(){
51161     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51162     var s = this.html;
51163
51164     s = ['<tpl>', s, '</tpl>'].join('');
51165
51166     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51167
51168     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51169     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51170     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51171     var m, id = 0;
51172     var tpls = [];
51173
51174     while(m = s.match(re)){
51175        var m2 = m[0].match(nameRe);
51176        var m3 = m[0].match(ifRe);
51177        var m4 = m[0].match(execRe);
51178        var exp = null, fn = null, exec = null;
51179        var name = m2 && m2[1] ? m2[1] : '';
51180        if(m3){
51181            exp = m3 && m3[1] ? m3[1] : null;
51182            if(exp){
51183                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51184            }
51185        }
51186        if(m4){
51187            exp = m4 && m4[1] ? m4[1] : null;
51188            if(exp){
51189                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51190            }
51191        }
51192        if(name){
51193            switch(name){
51194                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51195                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51196                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51197            }
51198        }
51199        tpls.push({
51200             id: id,
51201             target: name,
51202             exec: exec,
51203             test: fn,
51204             body: m[1]||''
51205         });
51206        s = s.replace(m[0], '{xtpl'+ id + '}');
51207        ++id;
51208     }
51209     for(var i = tpls.length-1; i >= 0; --i){
51210         this.compileTpl(tpls[i]);
51211     }
51212     this.master = tpls[tpls.length-1];
51213     this.tpls = tpls;
51214 };
51215 Roo.extend(Roo.XTemplate, Roo.Template, {
51216
51217     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51218
51219     applySubTemplate : function(id, values, parent){
51220         var t = this.tpls[id];
51221         if(t.test && !t.test.call(this, values, parent)){
51222             return '';
51223         }
51224         if(t.exec && t.exec.call(this, values, parent)){
51225             return '';
51226         }
51227         var vs = t.target ? t.target.call(this, values, parent) : values;
51228         parent = t.target ? values : parent;
51229         if(t.target && vs instanceof Array){
51230             var buf = [];
51231             for(var i = 0, len = vs.length; i < len; i++){
51232                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51233             }
51234             return buf.join('');
51235         }
51236         return t.compiled.call(this, vs, parent);
51237     },
51238
51239     compileTpl : function(tpl){
51240         var fm = Roo.util.Format;
51241         var useF = this.disableFormats !== true;
51242         var sep = Roo.isGecko ? "+" : ",";
51243         var fn = function(m, name, format, args){
51244             if(name.substr(0, 4) == 'xtpl'){
51245                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51246             }
51247             var v;
51248             if(name.indexOf('.') != -1){
51249                 v = name;
51250             }else{
51251                 v = "values['" + name + "']";
51252             }
51253             if(format && useF){
51254                 args = args ? ',' + args : "";
51255                 if(format.substr(0, 5) != "this."){
51256                     format = "fm." + format + '(';
51257                 }else{
51258                     format = 'this.call("'+ format.substr(5) + '", ';
51259                     args = ", values";
51260                 }
51261             }else{
51262                 args= ''; format = "("+v+" === undefined ? '' : ";
51263             }
51264             return "'"+ sep + format + v + args + ")"+sep+"'";
51265         };
51266         var body;
51267         // branched to use + in gecko and [].join() in others
51268         if(Roo.isGecko){
51269             body = "tpl.compiled = function(values, parent){ return '" +
51270                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51271                     "';};";
51272         }else{
51273             body = ["tpl.compiled = function(values, parent){ return ['"];
51274             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51275             body.push("'].join('');};");
51276             body = body.join('');
51277         }
51278         /** eval:var:zzzzzzz */
51279         eval(body);
51280         return this;
51281     },
51282
51283     applyTemplate : function(values){
51284         return this.master.compiled.call(this, values, {});
51285         var s = this.subs;
51286     },
51287
51288     apply : function(){
51289         return this.applyTemplate.apply(this, arguments);
51290     },
51291
51292     compile : function(){return this;}
51293 });
51294
51295 Roo.XTemplate.from = function(el){
51296     el = Roo.getDom(el);
51297     return new Roo.XTemplate(el.value || el.innerHTML);
51298 };/*
51299  * Original code for Roojs - LGPL
51300  * <script type="text/javascript">
51301  */
51302  
51303 /**
51304  * @class Roo.XComponent
51305  * A delayed Element creator...
51306  * Or a way to group chunks of interface together.
51307  * 
51308  * Mypart.xyx = new Roo.XComponent({
51309
51310     parent : 'Mypart.xyz', // empty == document.element.!!
51311     order : '001',
51312     name : 'xxxx'
51313     region : 'xxxx'
51314     disabled : function() {} 
51315      
51316     tree : function() { // return an tree of xtype declared components
51317         var MODULE = this;
51318         return 
51319         {
51320             xtype : 'NestedLayoutPanel',
51321             // technicall
51322         }
51323      ]
51324  *})
51325  *
51326  *
51327  * It can be used to build a big heiracy, with parent etc.
51328  * or you can just use this to render a single compoent to a dom element
51329  * MYPART.render(Roo.Element | String(id) | dom_element )
51330  * 
51331  * @extends Roo.util.Observable
51332  * @constructor
51333  * @param cfg {Object} configuration of component
51334  * 
51335  */
51336 Roo.XComponent = function(cfg) {
51337     Roo.apply(this, cfg);
51338     this.addEvents({ 
51339         /**
51340              * @event built
51341              * Fires when this the componnt is built
51342              * @param {Roo.XComponent} c the component
51343              */
51344         'built' : true,
51345         /**
51346              * @event buildcomplete
51347              * Fires on the top level element when all elements have been built
51348              * @param {Roo.XComponent} c the top level component.
51349          */
51350         'buildcomplete' : true
51351         
51352     });
51353     this.region = this.region || 'center'; // default..
51354     Roo.XComponent.register(this);
51355     this.modules = false;
51356     this.el = false; // where the layout goes..
51357     
51358     
51359 }
51360 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51361     /**
51362      * @property el
51363      * The created element (with Roo.factory())
51364      * @type {Roo.Layout}
51365      */
51366     el  : false,
51367     
51368     /**
51369      * @property el
51370      * for BC  - use el in new code
51371      * @type {Roo.Layout}
51372      */
51373     panel : false,
51374     
51375     /**
51376      * @property layout
51377      * for BC  - use el in new code
51378      * @type {Roo.Layout}
51379      */
51380     layout : false,
51381     
51382      /**
51383      * @cfg {Function|boolean} disabled
51384      * If this module is disabled by some rule, return true from the funtion
51385      */
51386     disabled : false,
51387     
51388     /**
51389      * @cfg {String} parent 
51390      * Name of parent element which it get xtype added to..
51391      */
51392     parent: false,
51393     
51394     /**
51395      * @cfg {String} order
51396      * Used to set the order in which elements are created (usefull for multiple tabs)
51397      */
51398     
51399     order : false,
51400     /**
51401      * @cfg {String} name
51402      * String to display while loading.
51403      */
51404     name : false,
51405     /**
51406      * @cfg {String} region
51407      * Region to render component to (defaults to center)
51408      */
51409     region : 'center',
51410     
51411     /**
51412      * @cfg {Array} items
51413      * A single item array - the first element is the root of the tree..
51414      * It's done this way to stay compatible with the Xtype system...
51415      */
51416     items : false,
51417     
51418     
51419      /**
51420      * render
51421      * render element to dom or tree
51422      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51423      */
51424     
51425     render : function(el)
51426     {
51427         
51428         el = el || false;
51429         var hp = this.parent ? 1 : 0;
51430         
51431         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
51432             // if parent is a '#.....' string, then let's use that..
51433             var ename = this.parent.substr(1)
51434             this.parent = false;
51435             el = Roo.get(ename);
51436             if (!el) {
51437                 Roo.log("Warning - element can not be found :#" + ename );
51438                 return;
51439             }
51440         }
51441         
51442         
51443         if (!this.parent) {
51444             
51445             el = el ? Roo.get(el) : false;
51446             
51447             // it's a top level one..
51448             this.parent =  {
51449                 el : new Roo.BorderLayout(el || document.body, {
51450                 
51451                      center: {
51452                          titlebar: false,
51453                          autoScroll:false,
51454                          closeOnTab: true,
51455                          tabPosition: 'top',
51456                           //resizeTabs: true,
51457                          alwaysShowTabs: el && hp? false :  true,
51458                          hideTabs: el || !hp ? true :  false,
51459                          minTabWidth: 140
51460                      }
51461                  })
51462             }
51463         }
51464         
51465         
51466             
51467         var tree = this.tree();
51468         tree.region = tree.region || this.region;
51469         this.el = this.parent.el.addxtype(tree);
51470         this.fireEvent('built', this);
51471         
51472         this.panel = this.el;
51473         this.layout = this.panel.layout;    
51474          
51475     }
51476     
51477 });
51478
51479 Roo.apply(Roo.XComponent, {
51480     
51481     /**
51482      * @property  buildCompleted
51483      * True when the builder has completed building the interface.
51484      * @type Boolean
51485      */
51486     buildCompleted : false,
51487      
51488     /**
51489      * @property  topModule
51490      * the upper most module - uses document.element as it's constructor.
51491      * @type Object
51492      */
51493      
51494     topModule  : false,
51495       
51496     /**
51497      * @property  modules
51498      * array of modules to be created by registration system.
51499      * @type {Array} of Roo.XComponent
51500      */
51501     
51502     modules : [],
51503     /**
51504      * @property  elmodules
51505      * array of modules to be created by which use #ID 
51506      * @type {Array} of Roo.XComponent
51507      */
51508      
51509     elmodules : [],
51510
51511     
51512     /**
51513      * Register components to be built later.
51514      *
51515      * This solves the following issues
51516      * - Building is not done on page load, but after an authentication process has occured.
51517      * - Interface elements are registered on page load
51518      * - Parent Interface elements may not be loaded before child, so this handles that..
51519      * 
51520      *
51521      * example:
51522      * 
51523      * MyApp.register({
51524           order : '000001',
51525           module : 'Pman.Tab.projectMgr',
51526           region : 'center',
51527           parent : 'Pman.layout',
51528           disabled : false,  // or use a function..
51529         })
51530      
51531      * * @param {Object} details about module
51532      */
51533     register : function(obj) {
51534         this.modules.push(obj);
51535          
51536     },
51537     /**
51538      * convert a string to an object..
51539      * eg. 'AAA.BBB' -> finds AAA.BBB
51540
51541      */
51542     
51543     toObject : function(str)
51544     {
51545         if (!str || typeof(str) == 'object') {
51546             return str;
51547         }
51548         if (str[0]=='#') {
51549             return str;
51550         }
51551
51552         var ar = str.split('.');
51553         var rt, o;
51554         rt = ar.shift();
51555             /** eval:var:o */
51556         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51557         if (o === false) {
51558             throw "Module not found : " + str;
51559         }
51560         Roo.each(ar, function(e) {
51561             if (typeof(o[e]) == 'undefined') {
51562                 throw "Module not found : " + str;
51563             }
51564             o = o[e];
51565         });
51566         
51567         return o;
51568         
51569     },
51570     
51571     
51572     /**
51573      * move modules into their correct place in the tree..
51574      * 
51575      */
51576     preBuild : function ()
51577     {
51578         var _t = this;
51579         Roo.each(this.modules , function (obj)
51580         {
51581             var opar = obj.parent;
51582             obj.parent = this.toObject(opar);
51583             
51584             if (!obj.parent) {
51585                 this.topModule = obj;
51586                 return;
51587             }
51588             if (typeof(obj.parent) == 'string') {
51589                 this.elmodules.push(obj);
51590                 return;
51591             }
51592             if (obj.parent.constructor != Roo.XComponent) {
51593                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
51594             }
51595             if (!obj.parent.modules) {
51596                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51597                     function(o) { return o.order + '' }
51598                 );
51599             }
51600             
51601             obj.parent.modules.add(obj);
51602         }, this);
51603     },
51604     
51605      /**
51606      * make a list of modules to build.
51607      * @return {Array} list of modules. 
51608      */ 
51609     
51610     buildOrder : function()
51611     {
51612         var _this = this;
51613         var cmp = function(a,b) {   
51614             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51615         };
51616         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51617             throw "No top level modules to build";
51618         }
51619         
51620         // make a flat list in order of modules to build.
51621         var mods = this.topModule ? [ this.topModule ] : [];
51622         Roo.each(this.elmodules,function(e) { mods.push(e) });
51623
51624         
51625         // add modules to their parents..
51626         var addMod = function(m) {
51627            // Roo.debug && Roo.log(m.modKey);
51628             
51629             mods.push(m);
51630             if (m.modules) {
51631                 m.modules.keySort('ASC',  cmp );
51632                 m.modules.each(addMod);
51633             }
51634             // not sure if this is used any more..
51635             if (m.finalize) {
51636                 m.finalize.name = m.name + " (clean up) ";
51637                 mods.push(m.finalize);
51638             }
51639             
51640         }
51641         if (this.topModule) { 
51642             this.topModule.modules.keySort('ASC',  cmp );
51643             this.topModule.modules.each(addMod);
51644         }
51645         return mods;
51646     },
51647     
51648      /**
51649      * Build the registered modules.
51650      * @param {Object} parent element.
51651      * @param {Function} optional method to call after module has been added.
51652      * 
51653      */ 
51654    
51655     build : function() 
51656     {
51657         
51658         this.preBuild();
51659         var mods = this.buildOrder();
51660       
51661         //this.allmods = mods;
51662         //Roo.debug && Roo.log(mods);
51663         //return;
51664         if (!mods.length) { // should not happen
51665             throw "NO modules!!!";
51666         }
51667         
51668         
51669         
51670         // flash it up as modal - so we store the mask!?
51671         Roo.MessageBox.show({ title: 'loading' });
51672         Roo.MessageBox.show({
51673            title: "Please wait...",
51674            msg: "Building Interface...",
51675            width:450,
51676            progress:true,
51677            closable:false,
51678            modal: false
51679           
51680         });
51681         var total = mods.length;
51682         
51683         var _this = this;
51684         var progressRun = function() {
51685             if (!mods.length) {
51686                 Roo.debug && Roo.log('hide?');
51687                 Roo.MessageBox.hide();
51688                 if (_this.topModule) { 
51689                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51690                 }
51691                 // THE END...
51692                 return false;   
51693             }
51694             
51695             var m = mods.shift();
51696             
51697             
51698             Roo.debug && Roo.log(m);
51699             // not sure if this is supported any more.. - modules that are are just function
51700             if (typeof(m) == 'function') { 
51701                 m.call(this);
51702                 return progressRun.defer(10, _this);
51703             } 
51704             
51705             
51706             
51707             Roo.MessageBox.updateProgress(
51708                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51709                     " of " + total + 
51710                     (m.name ? (' - ' + m.name) : '')
51711                     );
51712             
51713          
51714             // is the module disabled?
51715             var disabled = (typeof(m.disabled) == 'function') ?
51716                 m.disabled.call(m.module.disabled) : m.disabled;    
51717             
51718             
51719             if (disabled) {
51720                 return progressRun(); // we do not update the display!
51721             }
51722             
51723             // now build 
51724             
51725             m.render();
51726             // it's 10 on top level, and 1 on others??? why...
51727             return progressRun.defer(10, _this);
51728              
51729         }
51730         progressRun.defer(1, _this);
51731      
51732         
51733         
51734     }
51735     
51736      
51737    
51738     
51739     
51740 });
51741  //<script type="text/javascript">
51742
51743
51744 /**
51745  * @class Roo.Login
51746  * @extends Roo.LayoutDialog
51747  * A generic Login Dialog..... - only one needed in theory!?!?
51748  *
51749  * Fires XComponent builder on success...
51750  * 
51751  * Sends 
51752  *    username,password, lang = for login actions.
51753  *    check = 1 for periodic checking that sesion is valid.
51754  *    passwordRequest = email request password
51755  *    logout = 1 = to logout
51756  * 
51757  * Affects: (this id="????" elements)
51758  *   loading  (removed) (used to indicate application is loading)
51759  *   loading-mask (hides) (used to hide application when it's building loading)
51760  *   
51761  * 
51762  * Usage: 
51763  *    
51764  * 
51765  * Myapp.login = Roo.Login({
51766      url: xxxx,
51767    
51768      realm : 'Myapp', 
51769      
51770      
51771      method : 'POST',
51772      
51773      
51774      * 
51775  })
51776  * 
51777  * 
51778  * 
51779  **/
51780  
51781 Roo.Login = function(cfg)
51782 {
51783     this.addEvents({
51784         'refreshed' : true
51785     });
51786     
51787     Roo.apply(this,cfg);
51788     
51789     Roo.onReady(function() {
51790         this.onLoad();
51791     }, this);
51792     // call parent..
51793     
51794    
51795     Roo.Login.superclass.constructor.call(this, this);
51796     //this.addxtype(this.items[0]);
51797     
51798     
51799 }
51800
51801
51802 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51803     
51804     /**
51805      * @cfg {String} method
51806      * Method used to query for login details.
51807      */
51808     
51809     method : 'POST',
51810     /**
51811      * @cfg {String} url
51812      * URL to query login data. - eg. baseURL + '/Login.php'
51813      */
51814     url : '',
51815     
51816     /**
51817      * @property user
51818      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51819      * @type {Object} 
51820      */
51821     user : false,
51822     /**
51823      * @property checkFails
51824      * Number of times we have attempted to get authentication check, and failed.
51825      * @type {Number} 
51826      */
51827     checkFails : 0,
51828       /**
51829      * @property intervalID
51830      * The window interval that does the constant login checking.
51831      * @type {Number} 
51832      */
51833     intervalID : 0,
51834     
51835     
51836     onLoad : function() // called on page load...
51837     {
51838         // load 
51839          
51840         if (Roo.get('loading')) { // clear any loading indicator..
51841             Roo.get('loading').remove();
51842         }
51843         
51844         //this.switchLang('en'); // set the language to english..
51845        
51846         this.check({
51847             success:  function(response, opts)  {  // check successfull...
51848             
51849                 var res = this.processResponse(response);
51850                 this.checkFails =0;
51851                 if (!res.success) { // error!
51852                     this.checkFails = 5;
51853                     //console.log('call failure');
51854                     return this.failure(response,opts);
51855                 }
51856                 
51857                 if (!res.data.id) { // id=0 == login failure.
51858                     return this.show();
51859                 }
51860                 
51861                               
51862                         //console.log(success);
51863                 this.fillAuth(res.data);   
51864                 this.checkFails =0;
51865                 Roo.XComponent.build();
51866             },
51867             failure : this.show
51868         });
51869         
51870     }, 
51871     
51872     
51873     check: function(cfg) // called every so often to refresh cookie etc..
51874     {
51875         if (cfg.again) { // could be undefined..
51876             this.checkFails++;
51877         } else {
51878             this.checkFails = 0;
51879         }
51880         var _this = this;
51881         if (this.sending) {
51882             if ( this.checkFails > 4) {
51883                 Roo.MessageBox.alert("Error",  
51884                     "Error getting authentication status. - try reloading, or wait a while", function() {
51885                         _this.sending = false;
51886                     }); 
51887                 return;
51888             }
51889             cfg.again = true;
51890             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51891             return;
51892         }
51893         this.sending = true;
51894         
51895         Roo.Ajax.request({  
51896             url: this.url,
51897             params: {
51898                 getAuthUser: true
51899             },  
51900             method: this.method,
51901             success:  cfg.success || this.success,
51902             failure : cfg.failure || this.failure,
51903             scope : this,
51904             callCfg : cfg
51905               
51906         });  
51907     }, 
51908     
51909     
51910     logout: function()
51911     {
51912         window.onbeforeunload = function() { }; // false does not work for IE..
51913         this.user = false;
51914         var _this = this;
51915         
51916         Roo.Ajax.request({  
51917             url: this.url,
51918             params: {
51919                 logout: 1
51920             },  
51921             method: 'GET',
51922             failure : function() {
51923                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51924                     document.location = document.location.toString() + '?ts=' + Math.random();
51925                 });
51926                 
51927             },
51928             success : function() {
51929                 _this.user = false;
51930                 this.checkFails =0;
51931                 // fixme..
51932                 document.location = document.location.toString() + '?ts=' + Math.random();
51933             }
51934               
51935               
51936         }); 
51937     },
51938     
51939     processResponse : function (response)
51940     {
51941         var res = '';
51942         try {
51943             res = Roo.decode(response.responseText);
51944             // oops...
51945             if (typeof(res) != 'object') {
51946                 res = { success : false, errorMsg : res, errors : true };
51947             }
51948             if (typeof(res.success) == 'undefined') {
51949                 res.success = false;
51950             }
51951             
51952         } catch(e) {
51953             res = { success : false,  errorMsg : response.responseText, errors : true };
51954         }
51955         return res;
51956     },
51957     
51958     success : function(response, opts)  // check successfull...
51959     {  
51960         this.sending = false;
51961         var res = this.processResponse(response);
51962         if (!res.success) {
51963             return this.failure(response, opts);
51964         }
51965         if (!res.data || !res.data.id) {
51966             return this.failure(response,opts);
51967         }
51968         //console.log(res);
51969         this.fillAuth(res.data);
51970         
51971         this.checkFails =0;
51972         
51973     },
51974     
51975     
51976     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51977     {
51978         this.authUser = -1;
51979         this.sending = false;
51980         var res = this.processResponse(response);
51981         //console.log(res);
51982         if ( this.checkFails > 2) {
51983         
51984             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51985                 "Error getting authentication status. - try reloading"); 
51986             return;
51987         }
51988         opts.callCfg.again = true;
51989         this.check.defer(1000, this, [ opts.callCfg ]);
51990         return;  
51991     },
51992     
51993     
51994     
51995     fillAuth: function(au) {
51996         this.startAuthCheck();
51997         this.authUserId = au.id;
51998         this.authUser = au;
51999         this.lastChecked = new Date();
52000         this.fireEvent('refreshed', au);
52001         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52002         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52003         au.lang = au.lang || 'en';
52004         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52005         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52006         this.switchLang(au.lang );
52007         
52008      
52009         // open system... - -on setyp..
52010         if (this.authUserId  < 0) {
52011             Roo.MessageBox.alert("Warning", 
52012                 "This is an open system - please set up a admin user with a password.");  
52013         }
52014          
52015         //Pman.onload(); // which should do nothing if it's a re-auth result...
52016         
52017              
52018     },
52019     
52020     startAuthCheck : function() // starter for timeout checking..
52021     {
52022         if (this.intervalID) { // timer already in place...
52023             return false;
52024         }
52025         var _this = this;
52026         this.intervalID =  window.setInterval(function() {
52027               _this.check(false);
52028             }, 120000); // every 120 secs = 2mins..
52029         
52030         
52031     },
52032          
52033     
52034     switchLang : function (lang) 
52035     {
52036         _T = typeof(_T) == 'undefined' ? false : _T;
52037           if (!_T || !lang.length) {
52038             return;
52039         }
52040         
52041         if (!_T && lang != 'en') {
52042             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52043             return;
52044         }
52045         
52046         if (typeof(_T.en) == 'undefined') {
52047             _T.en = {};
52048             Roo.apply(_T.en, _T);
52049         }
52050         
52051         if (typeof(_T[lang]) == 'undefined') {
52052             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52053             return;
52054         }
52055         
52056         
52057         Roo.apply(_T, _T[lang]);
52058         // just need to set the text values for everything...
52059         var _this = this;
52060         /* this will not work ...
52061         if (this.form) { 
52062             
52063                
52064             function formLabel(name, val) {
52065                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52066             }
52067             
52068             formLabel('password', "Password"+':');
52069             formLabel('username', "Email Address"+':');
52070             formLabel('lang', "Language"+':');
52071             this.dialog.setTitle("Login");
52072             this.dialog.buttons[0].setText("Forgot Password");
52073             this.dialog.buttons[1].setText("Login");
52074         }
52075         */
52076         
52077         
52078     },
52079     
52080     
52081     title: "Login",
52082     modal: true,
52083     width:  350,
52084     //height: 230,
52085     height: 180,
52086     shadow: true,
52087     minWidth:200,
52088     minHeight:180,
52089     //proxyDrag: true,
52090     closable: false,
52091     draggable: false,
52092     collapsible: false,
52093     resizable: false,
52094     center: {  // needed??
52095         autoScroll:false,
52096         titlebar: false,
52097        // tabPosition: 'top',
52098         hideTabs: true,
52099         closeOnTab: true,
52100         alwaysShowTabs: false
52101     } ,
52102     listeners : {
52103         
52104         show  : function(dlg)
52105         {
52106             //console.log(this);
52107             this.form = this.layout.getRegion('center').activePanel.form;
52108             this.form.dialog = dlg;
52109             this.buttons[0].form = this.form;
52110             this.buttons[0].dialog = dlg;
52111             this.buttons[1].form = this.form;
52112             this.buttons[1].dialog = dlg;
52113            
52114            //this.resizeToLogo.defer(1000,this);
52115             // this is all related to resizing for logos..
52116             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52117            //// if (!sz) {
52118              //   this.resizeToLogo.defer(1000,this);
52119              //   return;
52120            // }
52121             //var w = Ext.lib.Dom.getViewWidth() - 100;
52122             //var h = Ext.lib.Dom.getViewHeight() - 100;
52123             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52124             //this.center();
52125             if (this.disabled) {
52126                 this.hide();
52127                 return;
52128             }
52129             
52130             if (this.user.id < 0) { // used for inital setup situations.
52131                 return;
52132             }
52133             
52134             if (this.intervalID) {
52135                 // remove the timer
52136                 window.clearInterval(this.intervalID);
52137                 this.intervalID = false;
52138             }
52139             
52140             
52141             if (Roo.get('loading')) {
52142                 Roo.get('loading').remove();
52143             }
52144             if (Roo.get('loading-mask')) {
52145                 Roo.get('loading-mask').hide();
52146             }
52147             
52148             //incomming._node = tnode;
52149             this.form.reset();
52150             //this.dialog.modal = !modal;
52151             //this.dialog.show();
52152             this.el.unmask(); 
52153             
52154             
52155             this.form.setValues({
52156                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52157                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52158             });
52159             
52160             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52161             if (this.form.findField('username').getValue().length > 0 ){
52162                 this.form.findField('password').focus();
52163             } else {
52164                this.form.findField('username').focus();
52165             }
52166     
52167         }
52168     },
52169     items : [
52170          {
52171        
52172             xtype : 'ContentPanel',
52173             xns : Roo,
52174             region: 'center',
52175             fitToFrame : true,
52176             
52177             items : [
52178     
52179                 {
52180                
52181                     xtype : 'Form',
52182                     xns : Roo.form,
52183                     labelWidth: 100,
52184                     style : 'margin: 10px;',
52185                     
52186                     listeners : {
52187                         actionfailed : function(f, act) {
52188                             // form can return { errors: .... }
52189                                 
52190                             //act.result.errors // invalid form element list...
52191                             //act.result.errorMsg// invalid form element list...
52192                             
52193                             this.dialog.el.unmask();
52194                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52195                                         "Login failed - communication error - try again.");
52196                                       
52197                         },
52198                         actioncomplete: function(re, act) {
52199                              
52200                             Roo.state.Manager.set(
52201                                 this.dialog.realm + '.username',  
52202                                     this.findField('username').getValue()
52203                             );
52204                             Roo.state.Manager.set(
52205                                 this.dialog.realm + '.lang',  
52206                                 this.findField('lang').getValue() 
52207                             );
52208                             
52209                             this.dialog.fillAuth(act.result.data);
52210                               
52211                             this.dialog.hide();
52212                             
52213                             if (Roo.get('loading-mask')) {
52214                                 Roo.get('loading-mask').show();
52215                             }
52216                             Roo.XComponent.build();
52217                             
52218                              
52219                             
52220                         }
52221                     },
52222                     items : [
52223                         {
52224                             xtype : 'TextField',
52225                             xns : Roo.form,
52226                             fieldLabel: "Email Address",
52227                             name: 'username',
52228                             width:200,
52229                             autoCreate : {tag: "input", type: "text", size: "20"}
52230                         },
52231                         {
52232                             xtype : 'TextField',
52233                             xns : Roo.form,
52234                             fieldLabel: "Password",
52235                             inputType: 'password',
52236                             name: 'password',
52237                             width:200,
52238                             autoCreate : {tag: "input", type: "text", size: "20"},
52239                             listeners : {
52240                                 specialkey : function(e,ev) {
52241                                     if (ev.keyCode == 13) {
52242                                         this.form.dialog.el.mask("Logging in");
52243                                         this.form.doAction('submit', {
52244                                             url: this.form.dialog.url,
52245                                             method: this.form.dialog.method
52246                                         });
52247                                     }
52248                                 }
52249                             }  
52250                         },
52251                         {
52252                             xtype : 'ComboBox',
52253                             xns : Roo.form,
52254                             fieldLabel: "Language",
52255                             name : 'langdisp',
52256                             store: {
52257                                 xtype : 'SimpleStore',
52258                                 fields: ['lang', 'ldisp'],
52259                                 data : [
52260                                     [ 'en', 'English' ],
52261                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52262                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52263                                 ]
52264                             },
52265                             
52266                             valueField : 'lang',
52267                             hiddenName:  'lang',
52268                             width: 200,
52269                             displayField:'ldisp',
52270                             typeAhead: false,
52271                             editable: false,
52272                             mode: 'local',
52273                             triggerAction: 'all',
52274                             emptyText:'Select a Language...',
52275                             selectOnFocus:true,
52276                             listeners : {
52277                                 select :  function(cb, rec, ix) {
52278                                     this.form.switchLang(rec.data.lang);
52279                                 }
52280                             }
52281                         
52282                         }
52283                     ]
52284                 }
52285                   
52286                 
52287             ]
52288         }
52289     ],
52290     buttons : [
52291         {
52292             xtype : 'Button',
52293             xns : 'Roo',
52294             text : "Forgot Password",
52295             listeners : {
52296                 click : function() {
52297                     //console.log(this);
52298                     var n = this.form.findField('username').getValue();
52299                     if (!n.length) {
52300                         Roo.MessageBox.alert("Error", "Fill in your email address");
52301                         return;
52302                     }
52303                     Roo.Ajax.request({
52304                         url: this.dialog.url,
52305                         params: {
52306                             passwordRequest: n
52307                         },
52308                         method: this.dialog.method,
52309                         success:  function(response, opts)  {  // check successfull...
52310                         
52311                             var res = this.dialog.processResponse(response);
52312                             if (!res.success) { // error!
52313                                Roo.MessageBox.alert("Error" ,
52314                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52315                                return;
52316                             }
52317                             Roo.MessageBox.alert("Notice" ,
52318                                 "Please check you email for the Password Reset message");
52319                         },
52320                         failure : function() {
52321                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52322                         }
52323                         
52324                     });
52325                 }
52326             }
52327         },
52328         {
52329             xtype : 'Button',
52330             xns : 'Roo',
52331             text : "Login",
52332             listeners : {
52333                 
52334                 click : function () {
52335                         
52336                     this.dialog.el.mask("Logging in");
52337                     this.form.doAction('submit', {
52338                             url: this.dialog.url,
52339                             method: this.dialog.method
52340                     });
52341                 }
52342             }
52343         }
52344     ]
52345   
52346   
52347 })
52348  
52349
52350
52351