roojs-ui-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19557             return;
19558         }
19559         var r = o.records, t = o.totalRecords || r.length;
19560         if(!options || options.add !== true){
19561             if(this.pruneModifiedRecords){
19562                 this.modified = [];
19563             }
19564             for(var i = 0, len = r.length; i < len; i++){
19565                 r[i].join(this);
19566             }
19567             if(this.snapshot){
19568                 this.data = this.snapshot;
19569                 delete this.snapshot;
19570             }
19571             this.data.clear();
19572             this.data.addAll(r);
19573             this.totalLength = t;
19574             this.applySort();
19575             this.fireEvent("datachanged", this);
19576         }else{
19577             this.totalLength = Math.max(t, this.data.length+r.length);
19578             this.add(r);
19579         }
19580         this.fireEvent("load", this, r, options);
19581         if(options.callback){
19582             options.callback.call(options.scope || this, r, options, true);
19583         }
19584     },
19585
19586     /**
19587      * Loads data from a passed data block. A Reader which understands the format of the data
19588      * must have been configured in the constructor.
19589      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19590      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19591      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19592      */
19593     loadData : function(o, append){
19594         var r = this.reader.readRecords(o);
19595         this.loadRecords(r, {add: append}, true);
19596     },
19597
19598     /**
19599      * Gets the number of cached records.
19600      * <p>
19601      * <em>If using paging, this may not be the total size of the dataset. If the data object
19602      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19603      * the data set size</em>
19604      */
19605     getCount : function(){
19606         return this.data.length || 0;
19607     },
19608
19609     /**
19610      * Gets the total number of records in the dataset as returned by the server.
19611      * <p>
19612      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19613      * the dataset size</em>
19614      */
19615     getTotalCount : function(){
19616         return this.totalLength || 0;
19617     },
19618
19619     /**
19620      * Returns the sort state of the Store as an object with two properties:
19621      * <pre><code>
19622  field {String} The name of the field by which the Records are sorted
19623  direction {String} The sort order, "ASC" or "DESC"
19624      * </code></pre>
19625      */
19626     getSortState : function(){
19627         return this.sortInfo;
19628     },
19629
19630     // private
19631     applySort : function(){
19632         if(this.sortInfo && !this.remoteSort){
19633             var s = this.sortInfo, f = s.field;
19634             var st = this.fields.get(f).sortType;
19635             var fn = function(r1, r2){
19636                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19637                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19638             };
19639             this.data.sort(s.direction, fn);
19640             if(this.snapshot && this.snapshot != this.data){
19641                 this.snapshot.sort(s.direction, fn);
19642             }
19643         }
19644     },
19645
19646     /**
19647      * Sets the default sort column and order to be used by the next load operation.
19648      * @param {String} fieldName The name of the field to sort by.
19649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19650      */
19651     setDefaultSort : function(field, dir){
19652         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19653     },
19654
19655     /**
19656      * Sort the Records.
19657      * If remote sorting is used, the sort is performed on the server, and the cache is
19658      * reloaded. If local sorting is used, the cache is sorted internally.
19659      * @param {String} fieldName The name of the field to sort by.
19660      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19661      */
19662     sort : function(fieldName, dir){
19663         var f = this.fields.get(fieldName);
19664         if(!dir){
19665             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19666             
19667             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19668                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19669             }else{
19670                 dir = f.sortDir;
19671             }
19672         }
19673         this.sortToggle[f.name] = dir;
19674         this.sortInfo = {field: f.name, direction: dir};
19675         if(!this.remoteSort){
19676             this.applySort();
19677             this.fireEvent("datachanged", this);
19678         }else{
19679             this.load(this.lastOptions);
19680         }
19681     },
19682
19683     /**
19684      * Calls the specified function for each of the Records in the cache.
19685      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19686      * Returning <em>false</em> aborts and exits the iteration.
19687      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19688      */
19689     each : function(fn, scope){
19690         this.data.each(fn, scope);
19691     },
19692
19693     /**
19694      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19695      * (e.g., during paging).
19696      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19697      */
19698     getModifiedRecords : function(){
19699         return this.modified;
19700     },
19701
19702     // private
19703     createFilterFn : function(property, value, anyMatch){
19704         if(!value.exec){ // not a regex
19705             value = String(value);
19706             if(value.length == 0){
19707                 return false;
19708             }
19709             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19710         }
19711         return function(r){
19712             return value.test(r.data[property]);
19713         };
19714     },
19715
19716     /**
19717      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19718      * @param {String} property A field on your records
19719      * @param {Number} start The record index to start at (defaults to 0)
19720      * @param {Number} end The last record index to include (defaults to length - 1)
19721      * @return {Number} The sum
19722      */
19723     sum : function(property, start, end){
19724         var rs = this.data.items, v = 0;
19725         start = start || 0;
19726         end = (end || end === 0) ? end : rs.length-1;
19727
19728         for(var i = start; i <= end; i++){
19729             v += (rs[i].data[property] || 0);
19730         }
19731         return v;
19732     },
19733
19734     /**
19735      * Filter the records by a specified property.
19736      * @param {String} field A field on your records
19737      * @param {String/RegExp} value Either a string that the field
19738      * should start with or a RegExp to test against the field
19739      * @param {Boolean} anyMatch True to match any part not just the beginning
19740      */
19741     filter : function(property, value, anyMatch){
19742         var fn = this.createFilterFn(property, value, anyMatch);
19743         return fn ? this.filterBy(fn) : this.clearFilter();
19744     },
19745
19746     /**
19747      * Filter by a function. The specified function will be called with each
19748      * record in this data source. If the function returns true the record is included,
19749      * otherwise it is filtered.
19750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19751      * @param {Object} scope (optional) The scope of the function (defaults to this)
19752      */
19753     filterBy : function(fn, scope){
19754         this.snapshot = this.snapshot || this.data;
19755         this.data = this.queryBy(fn, scope||this);
19756         this.fireEvent("datachanged", this);
19757     },
19758
19759     /**
19760      * Query the records by a specified property.
19761      * @param {String} field A field on your records
19762      * @param {String/RegExp} value Either a string that the field
19763      * should start with or a RegExp to test against the field
19764      * @param {Boolean} anyMatch True to match any part not just the beginning
19765      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19766      */
19767     query : function(property, value, anyMatch){
19768         var fn = this.createFilterFn(property, value, anyMatch);
19769         return fn ? this.queryBy(fn) : this.data.clone();
19770     },
19771
19772     /**
19773      * Query by a function. The specified function will be called with each
19774      * record in this data source. If the function returns true the record is included
19775      * in the results.
19776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19777      * @param {Object} scope (optional) The scope of the function (defaults to this)
19778       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19779      **/
19780     queryBy : function(fn, scope){
19781         var data = this.snapshot || this.data;
19782         return data.filterBy(fn, scope||this);
19783     },
19784
19785     /**
19786      * Collects unique values for a particular dataIndex from this store.
19787      * @param {String} dataIndex The property to collect
19788      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19789      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19790      * @return {Array} An array of the unique values
19791      **/
19792     collect : function(dataIndex, allowNull, bypassFilter){
19793         var d = (bypassFilter === true && this.snapshot) ?
19794                 this.snapshot.items : this.data.items;
19795         var v, sv, r = [], l = {};
19796         for(var i = 0, len = d.length; i < len; i++){
19797             v = d[i].data[dataIndex];
19798             sv = String(v);
19799             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19800                 l[sv] = true;
19801                 r[r.length] = v;
19802             }
19803         }
19804         return r;
19805     },
19806
19807     /**
19808      * Revert to a view of the Record cache with no filtering applied.
19809      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19810      */
19811     clearFilter : function(suppressEvent){
19812         if(this.snapshot && this.snapshot != this.data){
19813             this.data = this.snapshot;
19814             delete this.snapshot;
19815             if(suppressEvent !== true){
19816                 this.fireEvent("datachanged", this);
19817             }
19818         }
19819     },
19820
19821     // private
19822     afterEdit : function(record){
19823         if(this.modified.indexOf(record) == -1){
19824             this.modified.push(record);
19825         }
19826         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19827     },
19828
19829     // private
19830     afterReject : function(record){
19831         this.modified.remove(record);
19832         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19833     },
19834
19835     // private
19836     afterCommit : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19839     },
19840
19841     /**
19842      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19843      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19844      */
19845     commitChanges : function(){
19846         var m = this.modified.slice(0);
19847         this.modified = [];
19848         for(var i = 0, len = m.length; i < len; i++){
19849             m[i].commit();
19850         }
19851     },
19852
19853     /**
19854      * Cancel outstanding changes on all changed records.
19855      */
19856     rejectChanges : function(){
19857         var m = this.modified.slice(0);
19858         this.modified = [];
19859         for(var i = 0, len = m.length; i < len; i++){
19860             m[i].reject();
19861         }
19862     },
19863
19864     onMetaChange : function(meta, rtype, o){
19865         this.recordType = rtype;
19866         this.fields = rtype.prototype.fields;
19867         delete this.snapshot;
19868         this.sortInfo = meta.sortInfo || this.sortInfo;
19869         this.modified = [];
19870         this.fireEvent('metachange', this, this.reader.meta);
19871     }
19872 });/*
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  * @class Roo.data.SimpleStore
19885  * @extends Roo.data.Store
19886  * Small helper class to make creating Stores from Array data easier.
19887  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19888  * @cfg {Array} fields An array of field definition objects, or field name strings.
19889  * @cfg {Array} data The multi-dimensional array of data
19890  * @constructor
19891  * @param {Object} config
19892  */
19893 Roo.data.SimpleStore = function(config){
19894     Roo.data.SimpleStore.superclass.constructor.call(this, {
19895         isLocal : true,
19896         reader: new Roo.data.ArrayReader({
19897                 id: config.id
19898             },
19899             Roo.data.Record.create(config.fields)
19900         ),
19901         proxy : new Roo.data.MemoryProxy(config.data)
19902     });
19903     this.load();
19904 };
19905 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19906  * Based on:
19907  * Ext JS Library 1.1.1
19908  * Copyright(c) 2006-2007, Ext JS, LLC.
19909  *
19910  * Originally Released Under LGPL - original licence link has changed is not relivant.
19911  *
19912  * Fork - LGPL
19913  * <script type="text/javascript">
19914  */
19915
19916 /**
19917 /**
19918  * @extends Roo.data.Store
19919  * @class Roo.data.JsonStore
19920  * Small helper class to make creating Stores for JSON data easier. <br/>
19921 <pre><code>
19922 var store = new Roo.data.JsonStore({
19923     url: 'get-images.php',
19924     root: 'images',
19925     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19926 });
19927 </code></pre>
19928  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19929  * JsonReader and HttpProxy (unless inline data is provided).</b>
19930  * @cfg {Array} fields An array of field definition objects, or field name strings.
19931  * @constructor
19932  * @param {Object} config
19933  */
19934 Roo.data.JsonStore = function(c){
19935     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19936         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19937         reader: new Roo.data.JsonReader(c, c.fields)
19938     }));
19939 };
19940 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19941  * Based on:
19942  * Ext JS Library 1.1.1
19943  * Copyright(c) 2006-2007, Ext JS, LLC.
19944  *
19945  * Originally Released Under LGPL - original licence link has changed is not relivant.
19946  *
19947  * Fork - LGPL
19948  * <script type="text/javascript">
19949  */
19950
19951  
19952 Roo.data.Field = function(config){
19953     if(typeof config == "string"){
19954         config = {name: config};
19955     }
19956     Roo.apply(this, config);
19957     
19958     if(!this.type){
19959         this.type = "auto";
19960     }
19961     
19962     var st = Roo.data.SortTypes;
19963     // named sortTypes are supported, here we look them up
19964     if(typeof this.sortType == "string"){
19965         this.sortType = st[this.sortType];
19966     }
19967     
19968     // set default sortType for strings and dates
19969     if(!this.sortType){
19970         switch(this.type){
19971             case "string":
19972                 this.sortType = st.asUCString;
19973                 break;
19974             case "date":
19975                 this.sortType = st.asDate;
19976                 break;
19977             default:
19978                 this.sortType = st.none;
19979         }
19980     }
19981
19982     // define once
19983     var stripRe = /[\$,%]/g;
19984
19985     // prebuilt conversion function for this field, instead of
19986     // switching every time we're reading a value
19987     if(!this.convert){
19988         var cv, dateFormat = this.dateFormat;
19989         switch(this.type){
19990             case "":
19991             case "auto":
19992             case undefined:
19993                 cv = function(v){ return v; };
19994                 break;
19995             case "string":
19996                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19997                 break;
19998             case "int":
19999                 cv = function(v){
20000                     return v !== undefined && v !== null && v !== '' ?
20001                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20002                     };
20003                 break;
20004             case "float":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20008                     };
20009                 break;
20010             case "bool":
20011             case "boolean":
20012                 cv = function(v){ return v === true || v === "true" || v == 1; };
20013                 break;
20014             case "date":
20015                 cv = function(v){
20016                     if(!v){
20017                         return '';
20018                     }
20019                     if(v instanceof Date){
20020                         return v;
20021                     }
20022                     if(dateFormat){
20023                         if(dateFormat == "timestamp"){
20024                             return new Date(v*1000);
20025                         }
20026                         return Date.parseDate(v, dateFormat);
20027                     }
20028                     var parsed = Date.parse(v);
20029                     return parsed ? new Date(parsed) : null;
20030                 };
20031              break;
20032             
20033         }
20034         this.convert = cv;
20035     }
20036 };
20037
20038 Roo.data.Field.prototype = {
20039     dateFormat: null,
20040     defaultValue: "",
20041     mapping: null,
20042     sortType : null,
20043     sortDir : "ASC"
20044 };/*
20045  * Based on:
20046  * Ext JS Library 1.1.1
20047  * Copyright(c) 2006-2007, Ext JS, LLC.
20048  *
20049  * Originally Released Under LGPL - original licence link has changed is not relivant.
20050  *
20051  * Fork - LGPL
20052  * <script type="text/javascript">
20053  */
20054  
20055 // Base class for reading structured data from a data source.  This class is intended to be
20056 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20057
20058 /**
20059  * @class Roo.data.DataReader
20060  * Base class for reading structured data from a data source.  This class is intended to be
20061  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20062  */
20063
20064 Roo.data.DataReader = function(meta, recordType){
20065     
20066     this.meta = meta;
20067     
20068     this.recordType = recordType instanceof Array ? 
20069         Roo.data.Record.create(recordType) : recordType;
20070 };
20071
20072 Roo.data.DataReader.prototype = {
20073      /**
20074      * Create an empty record
20075      * @param {Object} data (optional) - overlay some values
20076      * @return {Roo.data.Record} record created.
20077      */
20078     newRow :  function(d) {
20079         var da =  {};
20080         this.recordType.prototype.fields.each(function(c) {
20081             switch( c.type) {
20082                 case 'int' : da[c.name] = 0; break;
20083                 case 'date' : da[c.name] = new Date(); break;
20084                 case 'float' : da[c.name] = 0.0; break;
20085                 case 'boolean' : da[c.name] = false; break;
20086                 default : da[c.name] = ""; break;
20087             }
20088             
20089         });
20090         return new this.recordType(Roo.apply(da, d));
20091     }
20092     
20093 };/*
20094  * Based on:
20095  * Ext JS Library 1.1.1
20096  * Copyright(c) 2006-2007, Ext JS, LLC.
20097  *
20098  * Originally Released Under LGPL - original licence link has changed is not relivant.
20099  *
20100  * Fork - LGPL
20101  * <script type="text/javascript">
20102  */
20103
20104 /**
20105  * @class Roo.data.DataProxy
20106  * @extends Roo.data.Observable
20107  * This class is an abstract base class for implementations which provide retrieval of
20108  * unformatted data objects.<br>
20109  * <p>
20110  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20111  * (of the appropriate type which knows how to parse the data object) to provide a block of
20112  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20113  * <p>
20114  * Custom implementations must implement the load method as described in
20115  * {@link Roo.data.HttpProxy#load}.
20116  */
20117 Roo.data.DataProxy = function(){
20118     this.addEvents({
20119         /**
20120          * @event beforeload
20121          * Fires before a network request is made to retrieve a data object.
20122          * @param {Object} This DataProxy object.
20123          * @param {Object} params The params parameter to the load function.
20124          */
20125         beforeload : true,
20126         /**
20127          * @event load
20128          * Fires before the load method's callback is called.
20129          * @param {Object} This DataProxy object.
20130          * @param {Object} o The data object.
20131          * @param {Object} arg The callback argument object passed to the load function.
20132          */
20133         load : true,
20134         /**
20135          * @event loadexception
20136          * Fires if an Exception occurs during data retrieval.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} o The data object.
20139          * @param {Object} arg The callback argument object passed to the load function.
20140          * @param {Object} e The Exception.
20141          */
20142         loadexception : true
20143     });
20144     Roo.data.DataProxy.superclass.constructor.call(this);
20145 };
20146
20147 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20148
20149     /**
20150      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20151      */
20152 /*
20153  * Based on:
20154  * Ext JS Library 1.1.1
20155  * Copyright(c) 2006-2007, Ext JS, LLC.
20156  *
20157  * Originally Released Under LGPL - original licence link has changed is not relivant.
20158  *
20159  * Fork - LGPL
20160  * <script type="text/javascript">
20161  */
20162 /**
20163  * @class Roo.data.MemoryProxy
20164  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20165  * to the Reader when its load method is called.
20166  * @constructor
20167  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20168  */
20169 Roo.data.MemoryProxy = function(data){
20170     if (data.data) {
20171         data = data.data;
20172     }
20173     Roo.data.MemoryProxy.superclass.constructor.call(this);
20174     this.data = data;
20175 };
20176
20177 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20178     /**
20179      * Load data from the requested source (in this case an in-memory
20180      * data object passed to the constructor), read the data object into
20181      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20182      * process that block using the passed callback.
20183      * @param {Object} params This parameter is not used by the MemoryProxy class.
20184      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20185      * object into a block of Roo.data.Records.
20186      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20187      * The function must be passed <ul>
20188      * <li>The Record block object</li>
20189      * <li>The "arg" argument from the load function</li>
20190      * <li>A boolean success indicator</li>
20191      * </ul>
20192      * @param {Object} scope The scope in which to call the callback
20193      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20194      */
20195     load : function(params, reader, callback, scope, arg){
20196         params = params || {};
20197         var result;
20198         try {
20199             result = reader.readRecords(this.data);
20200         }catch(e){
20201             this.fireEvent("loadexception", this, arg, null, e);
20202             callback.call(scope, null, arg, false);
20203             return;
20204         }
20205         callback.call(scope, result, arg, true);
20206     },
20207     
20208     // private
20209     update : function(params, records){
20210         
20211     }
20212 });/*
20213  * Based on:
20214  * Ext JS Library 1.1.1
20215  * Copyright(c) 2006-2007, Ext JS, LLC.
20216  *
20217  * Originally Released Under LGPL - original licence link has changed is not relivant.
20218  *
20219  * Fork - LGPL
20220  * <script type="text/javascript">
20221  */
20222 /**
20223  * @class Roo.data.HttpProxy
20224  * @extends Roo.data.DataProxy
20225  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20226  * configured to reference a certain URL.<br><br>
20227  * <p>
20228  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20229  * from which the running page was served.<br><br>
20230  * <p>
20231  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20232  * <p>
20233  * Be aware that to enable the browser to parse an XML document, the server must set
20234  * the Content-Type header in the HTTP response to "text/xml".
20235  * @constructor
20236  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20237  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20238  * will be used to make the request.
20239  */
20240 Roo.data.HttpProxy = function(conn){
20241     Roo.data.HttpProxy.superclass.constructor.call(this);
20242     // is conn a conn config or a real conn?
20243     this.conn = conn;
20244     this.useAjax = !conn || !conn.events;
20245   
20246 };
20247
20248 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20249     // thse are take from connection...
20250     
20251     /**
20252      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20253      */
20254     /**
20255      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20256      * extra parameters to each request made by this object. (defaults to undefined)
20257      */
20258     /**
20259      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20260      *  to each request made by this object. (defaults to undefined)
20261      */
20262     /**
20263      * @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)
20264      */
20265     /**
20266      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20267      */
20268      /**
20269      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20270      * @type Boolean
20271      */
20272   
20273
20274     /**
20275      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20276      * @type Boolean
20277      */
20278     /**
20279      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20280      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20281      * a finer-grained basis than the DataProxy events.
20282      */
20283     getConnection : function(){
20284         return this.useAjax ? Roo.Ajax : this.conn;
20285     },
20286
20287     /**
20288      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20289      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20290      * process that block using the passed callback.
20291      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20292      * for the request to the remote server.
20293      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20294      * object into a block of Roo.data.Records.
20295      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20296      * The function must be passed <ul>
20297      * <li>The Record block object</li>
20298      * <li>The "arg" argument from the load function</li>
20299      * <li>A boolean success indicator</li>
20300      * </ul>
20301      * @param {Object} scope The scope in which to call the callback
20302      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20303      */
20304     load : function(params, reader, callback, scope, arg){
20305         if(this.fireEvent("beforeload", this, params) !== false){
20306             var  o = {
20307                 params : params || {},
20308                 request: {
20309                     callback : callback,
20310                     scope : scope,
20311                     arg : arg
20312                 },
20313                 reader: reader,
20314                 callback : this.loadResponse,
20315                 scope: this
20316             };
20317             if(this.useAjax){
20318                 Roo.applyIf(o, this.conn);
20319                 if(this.activeRequest){
20320                     Roo.Ajax.abort(this.activeRequest);
20321                 }
20322                 this.activeRequest = Roo.Ajax.request(o);
20323             }else{
20324                 this.conn.request(o);
20325             }
20326         }else{
20327             callback.call(scope||this, null, arg, false);
20328         }
20329     },
20330
20331     // private
20332     loadResponse : function(o, success, response){
20333         delete this.activeRequest;
20334         if(!success){
20335             this.fireEvent("loadexception", this, o, response);
20336             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20337             return;
20338         }
20339         var result;
20340         try {
20341             result = o.reader.read(response);
20342         }catch(e){
20343             this.fireEvent("loadexception", this, o, response, e);
20344             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20345             return;
20346         }
20347         
20348         this.fireEvent("load", this, o, o.request.arg);
20349         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20350     },
20351
20352     // private
20353     update : function(dataSet){
20354
20355     },
20356
20357     // private
20358     updateResponse : function(dataSet){
20359
20360     }
20361 });/*
20362  * Based on:
20363  * Ext JS Library 1.1.1
20364  * Copyright(c) 2006-2007, Ext JS, LLC.
20365  *
20366  * Originally Released Under LGPL - original licence link has changed is not relivant.
20367  *
20368  * Fork - LGPL
20369  * <script type="text/javascript">
20370  */
20371
20372 /**
20373  * @class Roo.data.ScriptTagProxy
20374  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20375  * other than the originating domain of the running page.<br><br>
20376  * <p>
20377  * <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
20378  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20379  * <p>
20380  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20381  * source code that is used as the source inside a &lt;script> tag.<br><br>
20382  * <p>
20383  * In order for the browser to process the returned data, the server must wrap the data object
20384  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20385  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20386  * depending on whether the callback name was passed:
20387  * <p>
20388  * <pre><code>
20389 boolean scriptTag = false;
20390 String cb = request.getParameter("callback");
20391 if (cb != null) {
20392     scriptTag = true;
20393     response.setContentType("text/javascript");
20394 } else {
20395     response.setContentType("application/x-json");
20396 }
20397 Writer out = response.getWriter();
20398 if (scriptTag) {
20399     out.write(cb + "(");
20400 }
20401 out.print(dataBlock.toJsonString());
20402 if (scriptTag) {
20403     out.write(");");
20404 }
20405 </pre></code>
20406  *
20407  * @constructor
20408  * @param {Object} config A configuration object.
20409  */
20410 Roo.data.ScriptTagProxy = function(config){
20411     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20412     Roo.apply(this, config);
20413     this.head = document.getElementsByTagName("head")[0];
20414 };
20415
20416 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20417
20418 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20419     /**
20420      * @cfg {String} url The URL from which to request the data object.
20421      */
20422     /**
20423      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20424      */
20425     timeout : 30000,
20426     /**
20427      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20428      * the server the name of the callback function set up by the load call to process the returned data object.
20429      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20430      * javascript output which calls this named function passing the data object as its only parameter.
20431      */
20432     callbackParam : "callback",
20433     /**
20434      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20435      * name to the request.
20436      */
20437     nocache : true,
20438
20439     /**
20440      * Load data from the configured URL, read the data object into
20441      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20442      * process that block using the passed callback.
20443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20444      * for the request to the remote server.
20445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20446      * object into a block of Roo.data.Records.
20447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20448      * The function must be passed <ul>
20449      * <li>The Record block object</li>
20450      * <li>The "arg" argument from the load function</li>
20451      * <li>A boolean success indicator</li>
20452      * </ul>
20453      * @param {Object} scope The scope in which to call the callback
20454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20455      */
20456     load : function(params, reader, callback, scope, arg){
20457         if(this.fireEvent("beforeload", this, params) !== false){
20458
20459             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20460
20461             var url = this.url;
20462             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20463             if(this.nocache){
20464                 url += "&_dc=" + (new Date().getTime());
20465             }
20466             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20467             var trans = {
20468                 id : transId,
20469                 cb : "stcCallback"+transId,
20470                 scriptId : "stcScript"+transId,
20471                 params : params,
20472                 arg : arg,
20473                 url : url,
20474                 callback : callback,
20475                 scope : scope,
20476                 reader : reader
20477             };
20478             var conn = this;
20479
20480             window[trans.cb] = function(o){
20481                 conn.handleResponse(o, trans);
20482             };
20483
20484             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20485
20486             if(this.autoAbort !== false){
20487                 this.abort();
20488             }
20489
20490             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20491
20492             var script = document.createElement("script");
20493             script.setAttribute("src", url);
20494             script.setAttribute("type", "text/javascript");
20495             script.setAttribute("id", trans.scriptId);
20496             this.head.appendChild(script);
20497
20498             this.trans = trans;
20499         }else{
20500             callback.call(scope||this, null, arg, false);
20501         }
20502     },
20503
20504     // private
20505     isLoading : function(){
20506         return this.trans ? true : false;
20507     },
20508
20509     /**
20510      * Abort the current server request.
20511      */
20512     abort : function(){
20513         if(this.isLoading()){
20514             this.destroyTrans(this.trans);
20515         }
20516     },
20517
20518     // private
20519     destroyTrans : function(trans, isLoaded){
20520         this.head.removeChild(document.getElementById(trans.scriptId));
20521         clearTimeout(trans.timeoutId);
20522         if(isLoaded){
20523             window[trans.cb] = undefined;
20524             try{
20525                 delete window[trans.cb];
20526             }catch(e){}
20527         }else{
20528             // if hasn't been loaded, wait for load to remove it to prevent script error
20529             window[trans.cb] = function(){
20530                 window[trans.cb] = undefined;
20531                 try{
20532                     delete window[trans.cb];
20533                 }catch(e){}
20534             };
20535         }
20536     },
20537
20538     // private
20539     handleResponse : function(o, trans){
20540         this.trans = false;
20541         this.destroyTrans(trans, true);
20542         var result;
20543         try {
20544             result = trans.reader.readRecords(o);
20545         }catch(e){
20546             this.fireEvent("loadexception", this, o, trans.arg, e);
20547             trans.callback.call(trans.scope||window, null, trans.arg, false);
20548             return;
20549         }
20550         this.fireEvent("load", this, o, trans.arg);
20551         trans.callback.call(trans.scope||window, result, trans.arg, true);
20552     },
20553
20554     // private
20555     handleFailure : function(trans){
20556         this.trans = false;
20557         this.destroyTrans(trans, false);
20558         this.fireEvent("loadexception", this, null, trans.arg);
20559         trans.callback.call(trans.scope||window, null, trans.arg, false);
20560     }
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571
20572 /**
20573  * @class Roo.data.JsonReader
20574  * @extends Roo.data.DataReader
20575  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20576  * based on mappings in a provided Roo.data.Record constructor.
20577  * 
20578  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20579  * in the reply previously. 
20580  * 
20581  * <p>
20582  * Example code:
20583  * <pre><code>
20584 var RecordDef = Roo.data.Record.create([
20585     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20586     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20587 ]);
20588 var myReader = new Roo.data.JsonReader({
20589     totalProperty: "results",    // The property which contains the total dataset size (optional)
20590     root: "rows",                // The property which contains an Array of row objects
20591     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20592 }, RecordDef);
20593 </code></pre>
20594  * <p>
20595  * This would consume a JSON file like this:
20596  * <pre><code>
20597 { 'results': 2, 'rows': [
20598     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20599     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20600 }
20601 </code></pre>
20602  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20603  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20604  * paged from the remote server.
20605  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20606  * @cfg {String} root name of the property which contains the Array of row objects.
20607  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20608  * @constructor
20609  * Create a new JsonReader
20610  * @param {Object} meta Metadata configuration options
20611  * @param {Object} recordType Either an Array of field definition objects,
20612  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20613  */
20614 Roo.data.JsonReader = function(meta, recordType){
20615     
20616     meta = meta || {};
20617     // set some defaults:
20618     Roo.applyIf(meta, {
20619         totalProperty: 'total',
20620         successProperty : 'success',
20621         root : 'data',
20622         id : 'id'
20623     });
20624     
20625     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20626 };
20627 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20628     
20629     /**
20630      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20631      * Used by Store query builder to append _requestMeta to params.
20632      * 
20633      */
20634     metaFromRemote : false,
20635     /**
20636      * This method is only used by a DataProxy which has retrieved data from a remote server.
20637      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20638      * @return {Object} data A data block which is used by an Roo.data.Store object as
20639      * a cache of Roo.data.Records.
20640      */
20641     read : function(response){
20642         var json = response.responseText;
20643        
20644         var o = /* eval:var:o */ eval("("+json+")");
20645         if(!o) {
20646             throw {message: "JsonReader.read: Json object not found"};
20647         }
20648         
20649         if(o.metaData){
20650             
20651             delete this.ef;
20652             this.metaFromRemote = true;
20653             this.meta = o.metaData;
20654             this.recordType = Roo.data.Record.create(o.metaData.fields);
20655             this.onMetaChange(this.meta, this.recordType, o);
20656         }
20657         return this.readRecords(o);
20658     },
20659
20660     // private function a store will implement
20661     onMetaChange : function(meta, recordType, o){
20662
20663     },
20664
20665     /**
20666          * @ignore
20667          */
20668     simpleAccess: function(obj, subsc) {
20669         return obj[subsc];
20670     },
20671
20672         /**
20673          * @ignore
20674          */
20675     getJsonAccessor: function(){
20676         var re = /[\[\.]/;
20677         return function(expr) {
20678             try {
20679                 return(re.test(expr))
20680                     ? new Function("obj", "return obj." + expr)
20681                     : function(obj){
20682                         return obj[expr];
20683                     };
20684             } catch(e){}
20685             return Roo.emptyFn;
20686         };
20687     }(),
20688
20689     /**
20690      * Create a data block containing Roo.data.Records from an XML document.
20691      * @param {Object} o An object which contains an Array of row objects in the property specified
20692      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20693      * which contains the total size of the dataset.
20694      * @return {Object} data A data block which is used by an Roo.data.Store object as
20695      * a cache of Roo.data.Records.
20696      */
20697     readRecords : function(o){
20698         /**
20699          * After any data loads, the raw JSON data is available for further custom processing.
20700          * @type Object
20701          */
20702         this.jsonData = o;
20703         var s = this.meta, Record = this.recordType,
20704             f = Record.prototype.fields, fi = f.items, fl = f.length;
20705
20706 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20707         if (!this.ef) {
20708             if(s.totalProperty) {
20709                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20710                 }
20711                 if(s.successProperty) {
20712                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20713                 }
20714                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20715                 if (s.id) {
20716                         var g = this.getJsonAccessor(s.id);
20717                         this.getId = function(rec) {
20718                                 var r = g(rec);
20719                                 return (r === undefined || r === "") ? null : r;
20720                         };
20721                 } else {
20722                         this.getId = function(){return null;};
20723                 }
20724             this.ef = [];
20725             for(var jj = 0; jj < fl; jj++){
20726                 f = fi[jj];
20727                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20728                 this.ef[jj] = this.getJsonAccessor(map);
20729             }
20730         }
20731
20732         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20733         if(s.totalProperty){
20734             var vt = parseInt(this.getTotal(o), 10);
20735             if(!isNaN(vt)){
20736                 totalRecords = vt;
20737             }
20738         }
20739         if(s.successProperty){
20740             var vs = this.getSuccess(o);
20741             if(vs === false || vs === 'false'){
20742                 success = false;
20743             }
20744         }
20745         var records = [];
20746             for(var i = 0; i < c; i++){
20747                     var n = root[i];
20748                 var values = {};
20749                 var id = this.getId(n);
20750                 for(var j = 0; j < fl; j++){
20751                     f = fi[j];
20752                 var v = this.ef[j](n);
20753                 if (!f.convert) {
20754                     Roo.log('missing convert for ' + f.name);
20755                     Roo.log(f);
20756                     continue;
20757                 }
20758                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20759                 }
20760                 var record = new Record(values, id);
20761                 record.json = n;
20762                 records[i] = record;
20763             }
20764             return {
20765                 success : success,
20766                 records : records,
20767                 totalRecords : totalRecords
20768             };
20769     }
20770 });/*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781 /**
20782  * @class Roo.data.XmlReader
20783  * @extends Roo.data.DataReader
20784  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20785  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20786  * <p>
20787  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20788  * header in the HTTP response must be set to "text/xml".</em>
20789  * <p>
20790  * Example code:
20791  * <pre><code>
20792 var RecordDef = Roo.data.Record.create([
20793    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20794    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20795 ]);
20796 var myReader = new Roo.data.XmlReader({
20797    totalRecords: "results", // The element which contains the total dataset size (optional)
20798    record: "row",           // The repeated element which contains row information
20799    id: "id"                 // The element within the row that provides an ID for the record (optional)
20800 }, RecordDef);
20801 </code></pre>
20802  * <p>
20803  * This would consume an XML file like this:
20804  * <pre><code>
20805 &lt;?xml?>
20806 &lt;dataset>
20807  &lt;results>2&lt;/results>
20808  &lt;row>
20809    &lt;id>1&lt;/id>
20810    &lt;name>Bill&lt;/name>
20811    &lt;occupation>Gardener&lt;/occupation>
20812  &lt;/row>
20813  &lt;row>
20814    &lt;id>2&lt;/id>
20815    &lt;name>Ben&lt;/name>
20816    &lt;occupation>Horticulturalist&lt;/occupation>
20817  &lt;/row>
20818 &lt;/dataset>
20819 </code></pre>
20820  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20821  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20822  * paged from the remote server.
20823  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20824  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20825  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20826  * a record identifier value.
20827  * @constructor
20828  * Create a new XmlReader
20829  * @param {Object} meta Metadata configuration options
20830  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20831  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20832  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20833  */
20834 Roo.data.XmlReader = function(meta, recordType){
20835     meta = meta || {};
20836     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20837 };
20838 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20839     /**
20840      * This method is only used by a DataProxy which has retrieved data from a remote server.
20841          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20842          * to contain a method called 'responseXML' that returns an XML document object.
20843      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20844      * a cache of Roo.data.Records.
20845      */
20846     read : function(response){
20847         var doc = response.responseXML;
20848         if(!doc) {
20849             throw {message: "XmlReader.read: XML Document not available"};
20850         }
20851         return this.readRecords(doc);
20852     },
20853
20854     /**
20855      * Create a data block containing Roo.data.Records from an XML document.
20856          * @param {Object} doc A parsed XML document.
20857      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20858      * a cache of Roo.data.Records.
20859      */
20860     readRecords : function(doc){
20861         /**
20862          * After any data loads/reads, the raw XML Document is available for further custom processing.
20863          * @type XMLDocument
20864          */
20865         this.xmlData = doc;
20866         var root = doc.documentElement || doc;
20867         var q = Roo.DomQuery;
20868         var recordType = this.recordType, fields = recordType.prototype.fields;
20869         var sid = this.meta.id;
20870         var totalRecords = 0, success = true;
20871         if(this.meta.totalRecords){
20872             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20873         }
20874         
20875         if(this.meta.success){
20876             var sv = q.selectValue(this.meta.success, root, true);
20877             success = sv !== false && sv !== 'false';
20878         }
20879         var records = [];
20880         var ns = q.select(this.meta.record, root);
20881         for(var i = 0, len = ns.length; i < len; i++) {
20882                 var n = ns[i];
20883                 var values = {};
20884                 var id = sid ? q.selectValue(sid, n) : undefined;
20885                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20886                     var f = fields.items[j];
20887                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20888                     v = f.convert(v);
20889                     values[f.name] = v;
20890                 }
20891                 var record = new recordType(values, id);
20892                 record.node = n;
20893                 records[records.length] = record;
20894             }
20895
20896             return {
20897                 success : success,
20898                 records : records,
20899                 totalRecords : totalRecords || records.length
20900             };
20901     }
20902 });/*
20903  * Based on:
20904  * Ext JS Library 1.1.1
20905  * Copyright(c) 2006-2007, Ext JS, LLC.
20906  *
20907  * Originally Released Under LGPL - original licence link has changed is not relivant.
20908  *
20909  * Fork - LGPL
20910  * <script type="text/javascript">
20911  */
20912
20913 /**
20914  * @class Roo.data.ArrayReader
20915  * @extends Roo.data.DataReader
20916  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20917  * Each element of that Array represents a row of data fields. The
20918  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20919  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20920  * <p>
20921  * Example code:.
20922  * <pre><code>
20923 var RecordDef = Roo.data.Record.create([
20924     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20925     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20926 ]);
20927 var myReader = new Roo.data.ArrayReader({
20928     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20929 }, RecordDef);
20930 </code></pre>
20931  * <p>
20932  * This would consume an Array like this:
20933  * <pre><code>
20934 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20935   </code></pre>
20936  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20937  * @constructor
20938  * Create a new JsonReader
20939  * @param {Object} meta Metadata configuration options.
20940  * @param {Object} recordType Either an Array of field definition objects
20941  * as specified to {@link Roo.data.Record#create},
20942  * or an {@link Roo.data.Record} object
20943  * created using {@link Roo.data.Record#create}.
20944  */
20945 Roo.data.ArrayReader = function(meta, recordType){
20946     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20947 };
20948
20949 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20950     /**
20951      * Create a data block containing Roo.data.Records from an XML document.
20952      * @param {Object} o An Array of row objects which represents the dataset.
20953      * @return {Object} data A data block which is used by an Roo.data.Store object as
20954      * a cache of Roo.data.Records.
20955      */
20956     readRecords : function(o){
20957         var sid = this.meta ? this.meta.id : null;
20958         var recordType = this.recordType, fields = recordType.prototype.fields;
20959         var records = [];
20960         var root = o;
20961             for(var i = 0; i < root.length; i++){
20962                     var n = root[i];
20963                 var values = {};
20964                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20965                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20966                 var f = fields.items[j];
20967                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20968                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20969                 v = f.convert(v);
20970                 values[f.name] = v;
20971             }
20972                 var record = new recordType(values, id);
20973                 record.json = n;
20974                 records[records.length] = record;
20975             }
20976             return {
20977                 records : records,
20978                 totalRecords : records.length
20979             };
20980     }
20981 });/*
20982  * Based on:
20983  * Ext JS Library 1.1.1
20984  * Copyright(c) 2006-2007, Ext JS, LLC.
20985  *
20986  * Originally Released Under LGPL - original licence link has changed is not relivant.
20987  *
20988  * Fork - LGPL
20989  * <script type="text/javascript">
20990  */
20991
20992
20993 /**
20994  * @class Roo.data.Tree
20995  * @extends Roo.util.Observable
20996  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20997  * in the tree have most standard DOM functionality.
20998  * @constructor
20999  * @param {Node} root (optional) The root node
21000  */
21001 Roo.data.Tree = function(root){
21002    this.nodeHash = {};
21003    /**
21004     * The root node for this tree
21005     * @type Node
21006     */
21007    this.root = null;
21008    if(root){
21009        this.setRootNode(root);
21010    }
21011    this.addEvents({
21012        /**
21013         * @event append
21014         * Fires when a new child node is appended to a node in this tree.
21015         * @param {Tree} tree The owner tree
21016         * @param {Node} parent The parent node
21017         * @param {Node} node The newly appended node
21018         * @param {Number} index The index of the newly appended node
21019         */
21020        "append" : true,
21021        /**
21022         * @event remove
21023         * Fires when a child node is removed from a node in this tree.
21024         * @param {Tree} tree The owner tree
21025         * @param {Node} parent The parent node
21026         * @param {Node} node The child node removed
21027         */
21028        "remove" : true,
21029        /**
21030         * @event move
21031         * Fires when a node is moved to a new location in the tree
21032         * @param {Tree} tree The owner tree
21033         * @param {Node} node The node moved
21034         * @param {Node} oldParent The old parent of this node
21035         * @param {Node} newParent The new parent of this node
21036         * @param {Number} index The index it was moved to
21037         */
21038        "move" : true,
21039        /**
21040         * @event insert
21041         * Fires when a new child node is inserted in a node in this tree.
21042         * @param {Tree} tree The owner tree
21043         * @param {Node} parent The parent node
21044         * @param {Node} node The child node inserted
21045         * @param {Node} refNode The child node the node was inserted before
21046         */
21047        "insert" : true,
21048        /**
21049         * @event beforeappend
21050         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node to be appended
21054         */
21055        "beforeappend" : true,
21056        /**
21057         * @event beforeremove
21058         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21059         * @param {Tree} tree The owner tree
21060         * @param {Node} parent The parent node
21061         * @param {Node} node The child node to be removed
21062         */
21063        "beforeremove" : true,
21064        /**
21065         * @event beforemove
21066         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21067         * @param {Tree} tree The owner tree
21068         * @param {Node} node The node being moved
21069         * @param {Node} oldParent The parent of the node
21070         * @param {Node} newParent The new parent the node is moving to
21071         * @param {Number} index The index it is being moved to
21072         */
21073        "beforemove" : true,
21074        /**
21075         * @event beforeinsert
21076         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21077         * @param {Tree} tree The owner tree
21078         * @param {Node} parent The parent node
21079         * @param {Node} node The child node to be inserted
21080         * @param {Node} refNode The child node the node is being inserted before
21081         */
21082        "beforeinsert" : true
21083    });
21084
21085     Roo.data.Tree.superclass.constructor.call(this);
21086 };
21087
21088 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21089     pathSeparator: "/",
21090
21091     proxyNodeEvent : function(){
21092         return this.fireEvent.apply(this, arguments);
21093     },
21094
21095     /**
21096      * Returns the root node for this tree.
21097      * @return {Node}
21098      */
21099     getRootNode : function(){
21100         return this.root;
21101     },
21102
21103     /**
21104      * Sets the root node for this tree.
21105      * @param {Node} node
21106      * @return {Node}
21107      */
21108     setRootNode : function(node){
21109         this.root = node;
21110         node.ownerTree = this;
21111         node.isRoot = true;
21112         this.registerNode(node);
21113         return node;
21114     },
21115
21116     /**
21117      * Gets a node in this tree by its id.
21118      * @param {String} id
21119      * @return {Node}
21120      */
21121     getNodeById : function(id){
21122         return this.nodeHash[id];
21123     },
21124
21125     registerNode : function(node){
21126         this.nodeHash[node.id] = node;
21127     },
21128
21129     unregisterNode : function(node){
21130         delete this.nodeHash[node.id];
21131     },
21132
21133     toString : function(){
21134         return "[Tree"+(this.id?" "+this.id:"")+"]";
21135     }
21136 });
21137
21138 /**
21139  * @class Roo.data.Node
21140  * @extends Roo.util.Observable
21141  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21142  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21143  * @constructor
21144  * @param {Object} attributes The attributes/config for the node
21145  */
21146 Roo.data.Node = function(attributes){
21147     /**
21148      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21149      * @type {Object}
21150      */
21151     this.attributes = attributes || {};
21152     this.leaf = this.attributes.leaf;
21153     /**
21154      * The node id. @type String
21155      */
21156     this.id = this.attributes.id;
21157     if(!this.id){
21158         this.id = Roo.id(null, "ynode-");
21159         this.attributes.id = this.id;
21160     }
21161     /**
21162      * All child nodes of this node. @type Array
21163      */
21164     this.childNodes = [];
21165     if(!this.childNodes.indexOf){ // indexOf is a must
21166         this.childNodes.indexOf = function(o){
21167             for(var i = 0, len = this.length; i < len; i++){
21168                 if(this[i] == o) {
21169                     return i;
21170                 }
21171             }
21172             return -1;
21173         };
21174     }
21175     /**
21176      * The parent node for this node. @type Node
21177      */
21178     this.parentNode = null;
21179     /**
21180      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21181      */
21182     this.firstChild = null;
21183     /**
21184      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21185      */
21186     this.lastChild = null;
21187     /**
21188      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21189      */
21190     this.previousSibling = null;
21191     /**
21192      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21193      */
21194     this.nextSibling = null;
21195
21196     this.addEvents({
21197        /**
21198         * @event append
21199         * Fires when a new child node is appended
21200         * @param {Tree} tree The owner tree
21201         * @param {Node} this This node
21202         * @param {Node} node The newly appended node
21203         * @param {Number} index The index of the newly appended node
21204         */
21205        "append" : true,
21206        /**
21207         * @event remove
21208         * Fires when a child node is removed
21209         * @param {Tree} tree The owner tree
21210         * @param {Node} this This node
21211         * @param {Node} node The removed node
21212         */
21213        "remove" : true,
21214        /**
21215         * @event move
21216         * Fires when this node is moved to a new location in the tree
21217         * @param {Tree} tree The owner tree
21218         * @param {Node} this This node
21219         * @param {Node} oldParent The old parent of this node
21220         * @param {Node} newParent The new parent of this node
21221         * @param {Number} index The index it was moved to
21222         */
21223        "move" : true,
21224        /**
21225         * @event insert
21226         * Fires when a new child node is inserted.
21227         * @param {Tree} tree The owner tree
21228         * @param {Node} this This node
21229         * @param {Node} node The child node inserted
21230         * @param {Node} refNode The child node the node was inserted before
21231         */
21232        "insert" : true,
21233        /**
21234         * @event beforeappend
21235         * Fires before a new child is appended, return false to cancel the append.
21236         * @param {Tree} tree The owner tree
21237         * @param {Node} this This node
21238         * @param {Node} node The child node to be appended
21239         */
21240        "beforeappend" : true,
21241        /**
21242         * @event beforeremove
21243         * Fires before a child is removed, return false to cancel the remove.
21244         * @param {Tree} tree The owner tree
21245         * @param {Node} this This node
21246         * @param {Node} node The child node to be removed
21247         */
21248        "beforeremove" : true,
21249        /**
21250         * @event beforemove
21251         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21252         * @param {Tree} tree The owner tree
21253         * @param {Node} this This node
21254         * @param {Node} oldParent The parent of this node
21255         * @param {Node} newParent The new parent this node is moving to
21256         * @param {Number} index The index it is being moved to
21257         */
21258        "beforemove" : true,
21259        /**
21260         * @event beforeinsert
21261         * Fires before a new child is inserted, return false to cancel the insert.
21262         * @param {Tree} tree The owner tree
21263         * @param {Node} this This node
21264         * @param {Node} node The child node to be inserted
21265         * @param {Node} refNode The child node the node is being inserted before
21266         */
21267        "beforeinsert" : true
21268    });
21269     this.listeners = this.attributes.listeners;
21270     Roo.data.Node.superclass.constructor.call(this);
21271 };
21272
21273 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21274     fireEvent : function(evtName){
21275         // first do standard event for this node
21276         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21277             return false;
21278         }
21279         // then bubble it up to the tree if the event wasn't cancelled
21280         var ot = this.getOwnerTree();
21281         if(ot){
21282             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21283                 return false;
21284             }
21285         }
21286         return true;
21287     },
21288
21289     /**
21290      * Returns true if this node is a leaf
21291      * @return {Boolean}
21292      */
21293     isLeaf : function(){
21294         return this.leaf === true;
21295     },
21296
21297     // private
21298     setFirstChild : function(node){
21299         this.firstChild = node;
21300     },
21301
21302     //private
21303     setLastChild : function(node){
21304         this.lastChild = node;
21305     },
21306
21307
21308     /**
21309      * Returns true if this node is the last child of its parent
21310      * @return {Boolean}
21311      */
21312     isLast : function(){
21313        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21314     },
21315
21316     /**
21317      * Returns true if this node is the first child of its parent
21318      * @return {Boolean}
21319      */
21320     isFirst : function(){
21321        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21322     },
21323
21324     hasChildNodes : function(){
21325         return !this.isLeaf() && this.childNodes.length > 0;
21326     },
21327
21328     /**
21329      * Insert node(s) as the last child node of this node.
21330      * @param {Node/Array} node The node or Array of nodes to append
21331      * @return {Node} The appended node if single append, or null if an array was passed
21332      */
21333     appendChild : function(node){
21334         var multi = false;
21335         if(node instanceof Array){
21336             multi = node;
21337         }else if(arguments.length > 1){
21338             multi = arguments;
21339         }
21340         // if passed an array or multiple args do them one by one
21341         if(multi){
21342             for(var i = 0, len = multi.length; i < len; i++) {
21343                 this.appendChild(multi[i]);
21344             }
21345         }else{
21346             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21347                 return false;
21348             }
21349             var index = this.childNodes.length;
21350             var oldParent = node.parentNode;
21351             // it's a move, make sure we move it cleanly
21352             if(oldParent){
21353                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21354                     return false;
21355                 }
21356                 oldParent.removeChild(node);
21357             }
21358             index = this.childNodes.length;
21359             if(index == 0){
21360                 this.setFirstChild(node);
21361             }
21362             this.childNodes.push(node);
21363             node.parentNode = this;
21364             var ps = this.childNodes[index-1];
21365             if(ps){
21366                 node.previousSibling = ps;
21367                 ps.nextSibling = node;
21368             }else{
21369                 node.previousSibling = null;
21370             }
21371             node.nextSibling = null;
21372             this.setLastChild(node);
21373             node.setOwnerTree(this.getOwnerTree());
21374             this.fireEvent("append", this.ownerTree, this, node, index);
21375             if(oldParent){
21376                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21377             }
21378             return node;
21379         }
21380     },
21381
21382     /**
21383      * Removes a child node from this node.
21384      * @param {Node} node The node to remove
21385      * @return {Node} The removed node
21386      */
21387     removeChild : function(node){
21388         var index = this.childNodes.indexOf(node);
21389         if(index == -1){
21390             return false;
21391         }
21392         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21393             return false;
21394         }
21395
21396         // remove it from childNodes collection
21397         this.childNodes.splice(index, 1);
21398
21399         // update siblings
21400         if(node.previousSibling){
21401             node.previousSibling.nextSibling = node.nextSibling;
21402         }
21403         if(node.nextSibling){
21404             node.nextSibling.previousSibling = node.previousSibling;
21405         }
21406
21407         // update child refs
21408         if(this.firstChild == node){
21409             this.setFirstChild(node.nextSibling);
21410         }
21411         if(this.lastChild == node){
21412             this.setLastChild(node.previousSibling);
21413         }
21414
21415         node.setOwnerTree(null);
21416         // clear any references from the node
21417         node.parentNode = null;
21418         node.previousSibling = null;
21419         node.nextSibling = null;
21420         this.fireEvent("remove", this.ownerTree, this, node);
21421         return node;
21422     },
21423
21424     /**
21425      * Inserts the first node before the second node in this nodes childNodes collection.
21426      * @param {Node} node The node to insert
21427      * @param {Node} refNode The node to insert before (if null the node is appended)
21428      * @return {Node} The inserted node
21429      */
21430     insertBefore : function(node, refNode){
21431         if(!refNode){ // like standard Dom, refNode can be null for append
21432             return this.appendChild(node);
21433         }
21434         // nothing to do
21435         if(node == refNode){
21436             return false;
21437         }
21438
21439         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21440             return false;
21441         }
21442         var index = this.childNodes.indexOf(refNode);
21443         var oldParent = node.parentNode;
21444         var refIndex = index;
21445
21446         // when moving internally, indexes will change after remove
21447         if(oldParent == this && this.childNodes.indexOf(node) < index){
21448             refIndex--;
21449         }
21450
21451         // it's a move, make sure we move it cleanly
21452         if(oldParent){
21453             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21454                 return false;
21455             }
21456             oldParent.removeChild(node);
21457         }
21458         if(refIndex == 0){
21459             this.setFirstChild(node);
21460         }
21461         this.childNodes.splice(refIndex, 0, node);
21462         node.parentNode = this;
21463         var ps = this.childNodes[refIndex-1];
21464         if(ps){
21465             node.previousSibling = ps;
21466             ps.nextSibling = node;
21467         }else{
21468             node.previousSibling = null;
21469         }
21470         node.nextSibling = refNode;
21471         refNode.previousSibling = node;
21472         node.setOwnerTree(this.getOwnerTree());
21473         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21474         if(oldParent){
21475             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21476         }
21477         return node;
21478     },
21479
21480     /**
21481      * Returns the child node at the specified index.
21482      * @param {Number} index
21483      * @return {Node}
21484      */
21485     item : function(index){
21486         return this.childNodes[index];
21487     },
21488
21489     /**
21490      * Replaces one child node in this node with another.
21491      * @param {Node} newChild The replacement node
21492      * @param {Node} oldChild The node to replace
21493      * @return {Node} The replaced node
21494      */
21495     replaceChild : function(newChild, oldChild){
21496         this.insertBefore(newChild, oldChild);
21497         this.removeChild(oldChild);
21498         return oldChild;
21499     },
21500
21501     /**
21502      * Returns the index of a child node
21503      * @param {Node} node
21504      * @return {Number} The index of the node or -1 if it was not found
21505      */
21506     indexOf : function(child){
21507         return this.childNodes.indexOf(child);
21508     },
21509
21510     /**
21511      * Returns the tree this node is in.
21512      * @return {Tree}
21513      */
21514     getOwnerTree : function(){
21515         // if it doesn't have one, look for one
21516         if(!this.ownerTree){
21517             var p = this;
21518             while(p){
21519                 if(p.ownerTree){
21520                     this.ownerTree = p.ownerTree;
21521                     break;
21522                 }
21523                 p = p.parentNode;
21524             }
21525         }
21526         return this.ownerTree;
21527     },
21528
21529     /**
21530      * Returns depth of this node (the root node has a depth of 0)
21531      * @return {Number}
21532      */
21533     getDepth : function(){
21534         var depth = 0;
21535         var p = this;
21536         while(p.parentNode){
21537             ++depth;
21538             p = p.parentNode;
21539         }
21540         return depth;
21541     },
21542
21543     // private
21544     setOwnerTree : function(tree){
21545         // if it's move, we need to update everyone
21546         if(tree != this.ownerTree){
21547             if(this.ownerTree){
21548                 this.ownerTree.unregisterNode(this);
21549             }
21550             this.ownerTree = tree;
21551             var cs = this.childNodes;
21552             for(var i = 0, len = cs.length; i < len; i++) {
21553                 cs[i].setOwnerTree(tree);
21554             }
21555             if(tree){
21556                 tree.registerNode(this);
21557             }
21558         }
21559     },
21560
21561     /**
21562      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21563      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21564      * @return {String} The path
21565      */
21566     getPath : function(attr){
21567         attr = attr || "id";
21568         var p = this.parentNode;
21569         var b = [this.attributes[attr]];
21570         while(p){
21571             b.unshift(p.attributes[attr]);
21572             p = p.parentNode;
21573         }
21574         var sep = this.getOwnerTree().pathSeparator;
21575         return sep + b.join(sep);
21576     },
21577
21578     /**
21579      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21580      * function call will be the scope provided or the current node. The arguments to the function
21581      * will be the args provided or the current node. If the function returns false at any point,
21582      * the bubble is stopped.
21583      * @param {Function} fn The function to call
21584      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21585      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21586      */
21587     bubble : function(fn, scope, args){
21588         var p = this;
21589         while(p){
21590             if(fn.call(scope || p, args || p) === false){
21591                 break;
21592             }
21593             p = p.parentNode;
21594         }
21595     },
21596
21597     /**
21598      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21599      * function call will be the scope provided or the current node. The arguments to the function
21600      * will be the args provided or the current node. If the function returns false at any point,
21601      * the cascade is stopped on that branch.
21602      * @param {Function} fn The function to call
21603      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21604      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21605      */
21606     cascade : function(fn, scope, args){
21607         if(fn.call(scope || this, args || this) !== false){
21608             var cs = this.childNodes;
21609             for(var i = 0, len = cs.length; i < len; i++) {
21610                 cs[i].cascade(fn, scope, args);
21611             }
21612         }
21613     },
21614
21615     /**
21616      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21617      * function call will be the scope provided or the current node. The arguments to the function
21618      * will be the args provided or the current node. If the function returns false at any point,
21619      * the iteration stops.
21620      * @param {Function} fn The function to call
21621      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21622      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21623      */
21624     eachChild : function(fn, scope, args){
21625         var cs = this.childNodes;
21626         for(var i = 0, len = cs.length; i < len; i++) {
21627                 if(fn.call(scope || this, args || cs[i]) === false){
21628                     break;
21629                 }
21630         }
21631     },
21632
21633     /**
21634      * Finds the first child that has the attribute with the specified value.
21635      * @param {String} attribute The attribute name
21636      * @param {Mixed} value The value to search for
21637      * @return {Node} The found child or null if none was found
21638      */
21639     findChild : function(attribute, value){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(cs[i].attributes[attribute] == value){
21643                     return cs[i];
21644                 }
21645         }
21646         return null;
21647     },
21648
21649     /**
21650      * Finds the first child by a custom function. The child matches if the function passed
21651      * returns true.
21652      * @param {Function} fn
21653      * @param {Object} scope (optional)
21654      * @return {Node} The found child or null if none was found
21655      */
21656     findChildBy : function(fn, scope){
21657         var cs = this.childNodes;
21658         for(var i = 0, len = cs.length; i < len; i++) {
21659                 if(fn.call(scope||cs[i], cs[i]) === true){
21660                     return cs[i];
21661                 }
21662         }
21663         return null;
21664     },
21665
21666     /**
21667      * Sorts this nodes children using the supplied sort function
21668      * @param {Function} fn
21669      * @param {Object} scope (optional)
21670      */
21671     sort : function(fn, scope){
21672         var cs = this.childNodes;
21673         var len = cs.length;
21674         if(len > 0){
21675             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21676             cs.sort(sortFn);
21677             for(var i = 0; i < len; i++){
21678                 var n = cs[i];
21679                 n.previousSibling = cs[i-1];
21680                 n.nextSibling = cs[i+1];
21681                 if(i == 0){
21682                     this.setFirstChild(n);
21683                 }
21684                 if(i == len-1){
21685                     this.setLastChild(n);
21686                 }
21687             }
21688         }
21689     },
21690
21691     /**
21692      * Returns true if this node is an ancestor (at any point) of the passed node.
21693      * @param {Node} node
21694      * @return {Boolean}
21695      */
21696     contains : function(node){
21697         return node.isAncestor(this);
21698     },
21699
21700     /**
21701      * Returns true if the passed node is an ancestor (at any point) of this node.
21702      * @param {Node} node
21703      * @return {Boolean}
21704      */
21705     isAncestor : function(node){
21706         var p = this.parentNode;
21707         while(p){
21708             if(p == node){
21709                 return true;
21710             }
21711             p = p.parentNode;
21712         }
21713         return false;
21714     },
21715
21716     toString : function(){
21717         return "[Node"+(this.id?" "+this.id:"")+"]";
21718     }
21719 });/*
21720  * Based on:
21721  * Ext JS Library 1.1.1
21722  * Copyright(c) 2006-2007, Ext JS, LLC.
21723  *
21724  * Originally Released Under LGPL - original licence link has changed is not relivant.
21725  *
21726  * Fork - LGPL
21727  * <script type="text/javascript">
21728  */
21729  
21730
21731 /**
21732  * @class Roo.ComponentMgr
21733  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21734  * @singleton
21735  */
21736 Roo.ComponentMgr = function(){
21737     var all = new Roo.util.MixedCollection();
21738
21739     return {
21740         /**
21741          * Registers a component.
21742          * @param {Roo.Component} c The component
21743          */
21744         register : function(c){
21745             all.add(c);
21746         },
21747
21748         /**
21749          * Unregisters a component.
21750          * @param {Roo.Component} c The component
21751          */
21752         unregister : function(c){
21753             all.remove(c);
21754         },
21755
21756         /**
21757          * Returns a component by id
21758          * @param {String} id The component id
21759          */
21760         get : function(id){
21761             return all.get(id);
21762         },
21763
21764         /**
21765          * Registers a function that will be called when a specified component is added to ComponentMgr
21766          * @param {String} id The component id
21767          * @param {Funtction} fn The callback function
21768          * @param {Object} scope The scope of the callback
21769          */
21770         onAvailable : function(id, fn, scope){
21771             all.on("add", function(index, o){
21772                 if(o.id == id){
21773                     fn.call(scope || o, o);
21774                     all.un("add", fn, scope);
21775                 }
21776             });
21777         }
21778     };
21779 }();/*
21780  * Based on:
21781  * Ext JS Library 1.1.1
21782  * Copyright(c) 2006-2007, Ext JS, LLC.
21783  *
21784  * Originally Released Under LGPL - original licence link has changed is not relivant.
21785  *
21786  * Fork - LGPL
21787  * <script type="text/javascript">
21788  */
21789  
21790 /**
21791  * @class Roo.Component
21792  * @extends Roo.util.Observable
21793  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21794  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21795  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21796  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21797  * All visual components (widgets) that require rendering into a layout should subclass Component.
21798  * @constructor
21799  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21800  * 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
21801  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21802  */
21803 Roo.Component = function(config){
21804     config = config || {};
21805     if(config.tagName || config.dom || typeof config == "string"){ // element object
21806         config = {el: config, id: config.id || config};
21807     }
21808     this.initialConfig = config;
21809
21810     Roo.apply(this, config);
21811     this.addEvents({
21812         /**
21813          * @event disable
21814          * Fires after the component is disabled.
21815              * @param {Roo.Component} this
21816              */
21817         disable : true,
21818         /**
21819          * @event enable
21820          * Fires after the component is enabled.
21821              * @param {Roo.Component} this
21822              */
21823         enable : true,
21824         /**
21825          * @event beforeshow
21826          * Fires before the component is shown.  Return false to stop the show.
21827              * @param {Roo.Component} this
21828              */
21829         beforeshow : true,
21830         /**
21831          * @event show
21832          * Fires after the component is shown.
21833              * @param {Roo.Component} this
21834              */
21835         show : true,
21836         /**
21837          * @event beforehide
21838          * Fires before the component is hidden. Return false to stop the hide.
21839              * @param {Roo.Component} this
21840              */
21841         beforehide : true,
21842         /**
21843          * @event hide
21844          * Fires after the component is hidden.
21845              * @param {Roo.Component} this
21846              */
21847         hide : true,
21848         /**
21849          * @event beforerender
21850          * Fires before the component is rendered. Return false to stop the render.
21851              * @param {Roo.Component} this
21852              */
21853         beforerender : true,
21854         /**
21855          * @event render
21856          * Fires after the component is rendered.
21857              * @param {Roo.Component} this
21858              */
21859         render : true,
21860         /**
21861          * @event beforedestroy
21862          * Fires before the component is destroyed. Return false to stop the destroy.
21863              * @param {Roo.Component} this
21864              */
21865         beforedestroy : true,
21866         /**
21867          * @event destroy
21868          * Fires after the component is destroyed.
21869              * @param {Roo.Component} this
21870              */
21871         destroy : true
21872     });
21873     if(!this.id){
21874         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21875     }
21876     Roo.ComponentMgr.register(this);
21877     Roo.Component.superclass.constructor.call(this);
21878     this.initComponent();
21879     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21880         this.render(this.renderTo);
21881         delete this.renderTo;
21882     }
21883 };
21884
21885 /** @private */
21886 Roo.Component.AUTO_ID = 1000;
21887
21888 Roo.extend(Roo.Component, Roo.util.Observable, {
21889     /**
21890      * @scope Roo.Component.prototype
21891      * @type {Boolean}
21892      * true if this component is hidden. Read-only.
21893      */
21894     hidden : false,
21895     /**
21896      * @type {Boolean}
21897      * true if this component is disabled. Read-only.
21898      */
21899     disabled : false,
21900     /**
21901      * @type {Boolean}
21902      * true if this component has been rendered. Read-only.
21903      */
21904     rendered : false,
21905     
21906     /** @cfg {String} disableClass
21907      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21908      */
21909     disabledClass : "x-item-disabled",
21910         /** @cfg {Boolean} allowDomMove
21911          * Whether the component can move the Dom node when rendering (defaults to true).
21912          */
21913     allowDomMove : true,
21914     /** @cfg {String} hideMode
21915      * How this component should hidden. Supported values are
21916      * "visibility" (css visibility), "offsets" (negative offset position) and
21917      * "display" (css display) - defaults to "display".
21918      */
21919     hideMode: 'display',
21920
21921     /** @private */
21922     ctype : "Roo.Component",
21923
21924     /**
21925      * @cfg {String} actionMode 
21926      * which property holds the element that used for  hide() / show() / disable() / enable()
21927      * default is 'el' 
21928      */
21929     actionMode : "el",
21930
21931     /** @private */
21932     getActionEl : function(){
21933         return this[this.actionMode];
21934     },
21935
21936     initComponent : Roo.emptyFn,
21937     /**
21938      * If this is a lazy rendering component, render it to its container element.
21939      * @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.
21940      */
21941     render : function(container, position){
21942         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21943             if(!container && this.el){
21944                 this.el = Roo.get(this.el);
21945                 container = this.el.dom.parentNode;
21946                 this.allowDomMove = false;
21947             }
21948             this.container = Roo.get(container);
21949             this.rendered = true;
21950             if(position !== undefined){
21951                 if(typeof position == 'number'){
21952                     position = this.container.dom.childNodes[position];
21953                 }else{
21954                     position = Roo.getDom(position);
21955                 }
21956             }
21957             this.onRender(this.container, position || null);
21958             if(this.cls){
21959                 this.el.addClass(this.cls);
21960                 delete this.cls;
21961             }
21962             if(this.style){
21963                 this.el.applyStyles(this.style);
21964                 delete this.style;
21965             }
21966             this.fireEvent("render", this);
21967             this.afterRender(this.container);
21968             if(this.hidden){
21969                 this.hide();
21970             }
21971             if(this.disabled){
21972                 this.disable();
21973             }
21974         }
21975         return this;
21976     },
21977
21978     /** @private */
21979     // default function is not really useful
21980     onRender : function(ct, position){
21981         if(this.el){
21982             this.el = Roo.get(this.el);
21983             if(this.allowDomMove !== false){
21984                 ct.dom.insertBefore(this.el.dom, position);
21985             }
21986         }
21987     },
21988
21989     /** @private */
21990     getAutoCreate : function(){
21991         var cfg = typeof this.autoCreate == "object" ?
21992                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21993         if(this.id && !cfg.id){
21994             cfg.id = this.id;
21995         }
21996         return cfg;
21997     },
21998
21999     /** @private */
22000     afterRender : Roo.emptyFn,
22001
22002     /**
22003      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22004      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22005      */
22006     destroy : function(){
22007         if(this.fireEvent("beforedestroy", this) !== false){
22008             this.purgeListeners();
22009             this.beforeDestroy();
22010             if(this.rendered){
22011                 this.el.removeAllListeners();
22012                 this.el.remove();
22013                 if(this.actionMode == "container"){
22014                     this.container.remove();
22015                 }
22016             }
22017             this.onDestroy();
22018             Roo.ComponentMgr.unregister(this);
22019             this.fireEvent("destroy", this);
22020         }
22021     },
22022
22023         /** @private */
22024     beforeDestroy : function(){
22025
22026     },
22027
22028         /** @private */
22029         onDestroy : function(){
22030
22031     },
22032
22033     /**
22034      * Returns the underlying {@link Roo.Element}.
22035      * @return {Roo.Element} The element
22036      */
22037     getEl : function(){
22038         return this.el;
22039     },
22040
22041     /**
22042      * Returns the id of this component.
22043      * @return {String}
22044      */
22045     getId : function(){
22046         return this.id;
22047     },
22048
22049     /**
22050      * Try to focus this component.
22051      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22052      * @return {Roo.Component} this
22053      */
22054     focus : function(selectText){
22055         if(this.rendered){
22056             this.el.focus();
22057             if(selectText === true){
22058                 this.el.dom.select();
22059             }
22060         }
22061         return this;
22062     },
22063
22064     /** @private */
22065     blur : function(){
22066         if(this.rendered){
22067             this.el.blur();
22068         }
22069         return this;
22070     },
22071
22072     /**
22073      * Disable this component.
22074      * @return {Roo.Component} this
22075      */
22076     disable : function(){
22077         if(this.rendered){
22078             this.onDisable();
22079         }
22080         this.disabled = true;
22081         this.fireEvent("disable", this);
22082         return this;
22083     },
22084
22085         // private
22086     onDisable : function(){
22087         this.getActionEl().addClass(this.disabledClass);
22088         this.el.dom.disabled = true;
22089     },
22090
22091     /**
22092      * Enable this component.
22093      * @return {Roo.Component} this
22094      */
22095     enable : function(){
22096         if(this.rendered){
22097             this.onEnable();
22098         }
22099         this.disabled = false;
22100         this.fireEvent("enable", this);
22101         return this;
22102     },
22103
22104         // private
22105     onEnable : function(){
22106         this.getActionEl().removeClass(this.disabledClass);
22107         this.el.dom.disabled = false;
22108     },
22109
22110     /**
22111      * Convenience function for setting disabled/enabled by boolean.
22112      * @param {Boolean} disabled
22113      */
22114     setDisabled : function(disabled){
22115         this[disabled ? "disable" : "enable"]();
22116     },
22117
22118     /**
22119      * Show this component.
22120      * @return {Roo.Component} this
22121      */
22122     show: function(){
22123         if(this.fireEvent("beforeshow", this) !== false){
22124             this.hidden = false;
22125             if(this.rendered){
22126                 this.onShow();
22127             }
22128             this.fireEvent("show", this);
22129         }
22130         return this;
22131     },
22132
22133     // private
22134     onShow : function(){
22135         var ae = this.getActionEl();
22136         if(this.hideMode == 'visibility'){
22137             ae.dom.style.visibility = "visible";
22138         }else if(this.hideMode == 'offsets'){
22139             ae.removeClass('x-hidden');
22140         }else{
22141             ae.dom.style.display = "";
22142         }
22143     },
22144
22145     /**
22146      * Hide this component.
22147      * @return {Roo.Component} this
22148      */
22149     hide: function(){
22150         if(this.fireEvent("beforehide", this) !== false){
22151             this.hidden = true;
22152             if(this.rendered){
22153                 this.onHide();
22154             }
22155             this.fireEvent("hide", this);
22156         }
22157         return this;
22158     },
22159
22160     // private
22161     onHide : function(){
22162         var ae = this.getActionEl();
22163         if(this.hideMode == 'visibility'){
22164             ae.dom.style.visibility = "hidden";
22165         }else if(this.hideMode == 'offsets'){
22166             ae.addClass('x-hidden');
22167         }else{
22168             ae.dom.style.display = "none";
22169         }
22170     },
22171
22172     /**
22173      * Convenience function to hide or show this component by boolean.
22174      * @param {Boolean} visible True to show, false to hide
22175      * @return {Roo.Component} this
22176      */
22177     setVisible: function(visible){
22178         if(visible) {
22179             this.show();
22180         }else{
22181             this.hide();
22182         }
22183         return this;
22184     },
22185
22186     /**
22187      * Returns true if this component is visible.
22188      */
22189     isVisible : function(){
22190         return this.getActionEl().isVisible();
22191     },
22192
22193     cloneConfig : function(overrides){
22194         overrides = overrides || {};
22195         var id = overrides.id || Roo.id();
22196         var cfg = Roo.applyIf(overrides, this.initialConfig);
22197         cfg.id = id; // prevent dup id
22198         return new this.constructor(cfg);
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210  (function(){ 
22211 /**
22212  * @class Roo.Layer
22213  * @extends Roo.Element
22214  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22215  * automatic maintaining of shadow/shim positions.
22216  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22217  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22218  * you can pass a string with a CSS class name. False turns off the shadow.
22219  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22220  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22221  * @cfg {String} cls CSS class to add to the element
22222  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22223  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22224  * @constructor
22225  * @param {Object} config An object with config options.
22226  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22227  */
22228
22229 Roo.Layer = function(config, existingEl){
22230     config = config || {};
22231     var dh = Roo.DomHelper;
22232     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22233     if(existingEl){
22234         this.dom = Roo.getDom(existingEl);
22235     }
22236     if(!this.dom){
22237         var o = config.dh || {tag: "div", cls: "x-layer"};
22238         this.dom = dh.append(pel, o);
22239     }
22240     if(config.cls){
22241         this.addClass(config.cls);
22242     }
22243     this.constrain = config.constrain !== false;
22244     this.visibilityMode = Roo.Element.VISIBILITY;
22245     if(config.id){
22246         this.id = this.dom.id = config.id;
22247     }else{
22248         this.id = Roo.id(this.dom);
22249     }
22250     this.zindex = config.zindex || this.getZIndex();
22251     this.position("absolute", this.zindex);
22252     if(config.shadow){
22253         this.shadowOffset = config.shadowOffset || 4;
22254         this.shadow = new Roo.Shadow({
22255             offset : this.shadowOffset,
22256             mode : config.shadow
22257         });
22258     }else{
22259         this.shadowOffset = 0;
22260     }
22261     this.useShim = config.shim !== false && Roo.useShims;
22262     this.useDisplay = config.useDisplay;
22263     this.hide();
22264 };
22265
22266 var supr = Roo.Element.prototype;
22267
22268 // shims are shared among layer to keep from having 100 iframes
22269 var shims = [];
22270
22271 Roo.extend(Roo.Layer, Roo.Element, {
22272
22273     getZIndex : function(){
22274         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22275     },
22276
22277     getShim : function(){
22278         if(!this.useShim){
22279             return null;
22280         }
22281         if(this.shim){
22282             return this.shim;
22283         }
22284         var shim = shims.shift();
22285         if(!shim){
22286             shim = this.createShim();
22287             shim.enableDisplayMode('block');
22288             shim.dom.style.display = 'none';
22289             shim.dom.style.visibility = 'visible';
22290         }
22291         var pn = this.dom.parentNode;
22292         if(shim.dom.parentNode != pn){
22293             pn.insertBefore(shim.dom, this.dom);
22294         }
22295         shim.setStyle('z-index', this.getZIndex()-2);
22296         this.shim = shim;
22297         return shim;
22298     },
22299
22300     hideShim : function(){
22301         if(this.shim){
22302             this.shim.setDisplayed(false);
22303             shims.push(this.shim);
22304             delete this.shim;
22305         }
22306     },
22307
22308     disableShadow : function(){
22309         if(this.shadow){
22310             this.shadowDisabled = true;
22311             this.shadow.hide();
22312             this.lastShadowOffset = this.shadowOffset;
22313             this.shadowOffset = 0;
22314         }
22315     },
22316
22317     enableShadow : function(show){
22318         if(this.shadow){
22319             this.shadowDisabled = false;
22320             this.shadowOffset = this.lastShadowOffset;
22321             delete this.lastShadowOffset;
22322             if(show){
22323                 this.sync(true);
22324             }
22325         }
22326     },
22327
22328     // private
22329     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22330     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22331     sync : function(doShow){
22332         var sw = this.shadow;
22333         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22334             var sh = this.getShim();
22335
22336             var w = this.getWidth(),
22337                 h = this.getHeight();
22338
22339             var l = this.getLeft(true),
22340                 t = this.getTop(true);
22341
22342             if(sw && !this.shadowDisabled){
22343                 if(doShow && !sw.isVisible()){
22344                     sw.show(this);
22345                 }else{
22346                     sw.realign(l, t, w, h);
22347                 }
22348                 if(sh){
22349                     if(doShow){
22350                        sh.show();
22351                     }
22352                     // fit the shim behind the shadow, so it is shimmed too
22353                     var a = sw.adjusts, s = sh.dom.style;
22354                     s.left = (Math.min(l, l+a.l))+"px";
22355                     s.top = (Math.min(t, t+a.t))+"px";
22356                     s.width = (w+a.w)+"px";
22357                     s.height = (h+a.h)+"px";
22358                 }
22359             }else if(sh){
22360                 if(doShow){
22361                    sh.show();
22362                 }
22363                 sh.setSize(w, h);
22364                 sh.setLeftTop(l, t);
22365             }
22366             
22367         }
22368     },
22369
22370     // private
22371     destroy : function(){
22372         this.hideShim();
22373         if(this.shadow){
22374             this.shadow.hide();
22375         }
22376         this.removeAllListeners();
22377         var pn = this.dom.parentNode;
22378         if(pn){
22379             pn.removeChild(this.dom);
22380         }
22381         Roo.Element.uncache(this.id);
22382     },
22383
22384     remove : function(){
22385         this.destroy();
22386     },
22387
22388     // private
22389     beginUpdate : function(){
22390         this.updating = true;
22391     },
22392
22393     // private
22394     endUpdate : function(){
22395         this.updating = false;
22396         this.sync(true);
22397     },
22398
22399     // private
22400     hideUnders : function(negOffset){
22401         if(this.shadow){
22402             this.shadow.hide();
22403         }
22404         this.hideShim();
22405     },
22406
22407     // private
22408     constrainXY : function(){
22409         if(this.constrain){
22410             var vw = Roo.lib.Dom.getViewWidth(),
22411                 vh = Roo.lib.Dom.getViewHeight();
22412             var s = Roo.get(document).getScroll();
22413
22414             var xy = this.getXY();
22415             var x = xy[0], y = xy[1];   
22416             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22417             // only move it if it needs it
22418             var moved = false;
22419             // first validate right/bottom
22420             if((x + w) > vw+s.left){
22421                 x = vw - w - this.shadowOffset;
22422                 moved = true;
22423             }
22424             if((y + h) > vh+s.top){
22425                 y = vh - h - this.shadowOffset;
22426                 moved = true;
22427             }
22428             // then make sure top/left isn't negative
22429             if(x < s.left){
22430                 x = s.left;
22431                 moved = true;
22432             }
22433             if(y < s.top){
22434                 y = s.top;
22435                 moved = true;
22436             }
22437             if(moved){
22438                 if(this.avoidY){
22439                     var ay = this.avoidY;
22440                     if(y <= ay && (y+h) >= ay){
22441                         y = ay-h-5;   
22442                     }
22443                 }
22444                 xy = [x, y];
22445                 this.storeXY(xy);
22446                 supr.setXY.call(this, xy);
22447                 this.sync();
22448             }
22449         }
22450     },
22451
22452     isVisible : function(){
22453         return this.visible;    
22454     },
22455
22456     // private
22457     showAction : function(){
22458         this.visible = true; // track visibility to prevent getStyle calls
22459         if(this.useDisplay === true){
22460             this.setDisplayed("");
22461         }else if(this.lastXY){
22462             supr.setXY.call(this, this.lastXY);
22463         }else if(this.lastLT){
22464             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22465         }
22466     },
22467
22468     // private
22469     hideAction : function(){
22470         this.visible = false;
22471         if(this.useDisplay === true){
22472             this.setDisplayed(false);
22473         }else{
22474             this.setLeftTop(-10000,-10000);
22475         }
22476     },
22477
22478     // overridden Element method
22479     setVisible : function(v, a, d, c, e){
22480         if(v){
22481             this.showAction();
22482         }
22483         if(a && v){
22484             var cb = function(){
22485                 this.sync(true);
22486                 if(c){
22487                     c();
22488                 }
22489             }.createDelegate(this);
22490             supr.setVisible.call(this, true, true, d, cb, e);
22491         }else{
22492             if(!v){
22493                 this.hideUnders(true);
22494             }
22495             var cb = c;
22496             if(a){
22497                 cb = function(){
22498                     this.hideAction();
22499                     if(c){
22500                         c();
22501                     }
22502                 }.createDelegate(this);
22503             }
22504             supr.setVisible.call(this, v, a, d, cb, e);
22505             if(v){
22506                 this.sync(true);
22507             }else if(!a){
22508                 this.hideAction();
22509             }
22510         }
22511     },
22512
22513     storeXY : function(xy){
22514         delete this.lastLT;
22515         this.lastXY = xy;
22516     },
22517
22518     storeLeftTop : function(left, top){
22519         delete this.lastXY;
22520         this.lastLT = [left, top];
22521     },
22522
22523     // private
22524     beforeFx : function(){
22525         this.beforeAction();
22526         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22527     },
22528
22529     // private
22530     afterFx : function(){
22531         Roo.Layer.superclass.afterFx.apply(this, arguments);
22532         this.sync(this.isVisible());
22533     },
22534
22535     // private
22536     beforeAction : function(){
22537         if(!this.updating && this.shadow){
22538             this.shadow.hide();
22539         }
22540     },
22541
22542     // overridden Element method
22543     setLeft : function(left){
22544         this.storeLeftTop(left, this.getTop(true));
22545         supr.setLeft.apply(this, arguments);
22546         this.sync();
22547     },
22548
22549     setTop : function(top){
22550         this.storeLeftTop(this.getLeft(true), top);
22551         supr.setTop.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setLeftTop : function(left, top){
22556         this.storeLeftTop(left, top);
22557         supr.setLeftTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setXY : function(xy, a, d, c, e){
22562         this.fixDisplay();
22563         this.beforeAction();
22564         this.storeXY(xy);
22565         var cb = this.createCB(c);
22566         supr.setXY.call(this, xy, a, d, cb, e);
22567         if(!a){
22568             cb();
22569         }
22570     },
22571
22572     // private
22573     createCB : function(c){
22574         var el = this;
22575         return function(){
22576             el.constrainXY();
22577             el.sync(true);
22578             if(c){
22579                 c();
22580             }
22581         };
22582     },
22583
22584     // overridden Element method
22585     setX : function(x, a, d, c, e){
22586         this.setXY([x, this.getY()], a, d, c, e);
22587     },
22588
22589     // overridden Element method
22590     setY : function(y, a, d, c, e){
22591         this.setXY([this.getX(), y], a, d, c, e);
22592     },
22593
22594     // overridden Element method
22595     setSize : function(w, h, a, d, c, e){
22596         this.beforeAction();
22597         var cb = this.createCB(c);
22598         supr.setSize.call(this, w, h, a, d, cb, e);
22599         if(!a){
22600             cb();
22601         }
22602     },
22603
22604     // overridden Element method
22605     setWidth : function(w, a, d, c, e){
22606         this.beforeAction();
22607         var cb = this.createCB(c);
22608         supr.setWidth.call(this, w, a, d, cb, e);
22609         if(!a){
22610             cb();
22611         }
22612     },
22613
22614     // overridden Element method
22615     setHeight : function(h, a, d, c, e){
22616         this.beforeAction();
22617         var cb = this.createCB(c);
22618         supr.setHeight.call(this, h, a, d, cb, e);
22619         if(!a){
22620             cb();
22621         }
22622     },
22623
22624     // overridden Element method
22625     setBounds : function(x, y, w, h, a, d, c, e){
22626         this.beforeAction();
22627         var cb = this.createCB(c);
22628         if(!a){
22629             this.storeXY([x, y]);
22630             supr.setXY.call(this, [x, y]);
22631             supr.setSize.call(this, w, h, a, d, cb, e);
22632             cb();
22633         }else{
22634             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22635         }
22636         return this;
22637     },
22638     
22639     /**
22640      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22641      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22642      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22643      * @param {Number} zindex The new z-index to set
22644      * @return {this} The Layer
22645      */
22646     setZIndex : function(zindex){
22647         this.zindex = zindex;
22648         this.setStyle("z-index", zindex + 2);
22649         if(this.shadow){
22650             this.shadow.setZIndex(zindex + 1);
22651         }
22652         if(this.shim){
22653             this.shim.setStyle("z-index", zindex);
22654         }
22655     }
22656 });
22657 })();/*
22658  * Based on:
22659  * Ext JS Library 1.1.1
22660  * Copyright(c) 2006-2007, Ext JS, LLC.
22661  *
22662  * Originally Released Under LGPL - original licence link has changed is not relivant.
22663  *
22664  * Fork - LGPL
22665  * <script type="text/javascript">
22666  */
22667
22668
22669 /**
22670  * @class Roo.Shadow
22671  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22672  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22673  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22674  * @constructor
22675  * Create a new Shadow
22676  * @param {Object} config The config object
22677  */
22678 Roo.Shadow = function(config){
22679     Roo.apply(this, config);
22680     if(typeof this.mode != "string"){
22681         this.mode = this.defaultMode;
22682     }
22683     var o = this.offset, a = {h: 0};
22684     var rad = Math.floor(this.offset/2);
22685     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22686         case "drop":
22687             a.w = 0;
22688             a.l = a.t = o;
22689             a.t -= 1;
22690             if(Roo.isIE){
22691                 a.l -= this.offset + rad;
22692                 a.t -= this.offset + rad;
22693                 a.w -= rad;
22694                 a.h -= rad;
22695                 a.t += 1;
22696             }
22697         break;
22698         case "sides":
22699             a.w = (o*2);
22700             a.l = -o;
22701             a.t = o-1;
22702             if(Roo.isIE){
22703                 a.l -= (this.offset - rad);
22704                 a.t -= this.offset + rad;
22705                 a.l += 1;
22706                 a.w -= (this.offset - rad)*2;
22707                 a.w -= rad + 1;
22708                 a.h -= 1;
22709             }
22710         break;
22711         case "frame":
22712             a.w = a.h = (o*2);
22713             a.l = a.t = -o;
22714             a.t += 1;
22715             a.h -= 2;
22716             if(Roo.isIE){
22717                 a.l -= (this.offset - rad);
22718                 a.t -= (this.offset - rad);
22719                 a.l += 1;
22720                 a.w -= (this.offset + rad + 1);
22721                 a.h -= (this.offset + rad);
22722                 a.h += 1;
22723             }
22724         break;
22725     };
22726
22727     this.adjusts = a;
22728 };
22729
22730 Roo.Shadow.prototype = {
22731     /**
22732      * @cfg {String} mode
22733      * The shadow display mode.  Supports the following options:<br />
22734      * sides: Shadow displays on both sides and bottom only<br />
22735      * frame: Shadow displays equally on all four sides<br />
22736      * drop: Traditional bottom-right drop shadow (default)
22737      */
22738     /**
22739      * @cfg {String} offset
22740      * The number of pixels to offset the shadow from the element (defaults to 4)
22741      */
22742     offset: 4,
22743
22744     // private
22745     defaultMode: "drop",
22746
22747     /**
22748      * Displays the shadow under the target element
22749      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22750      */
22751     show : function(target){
22752         target = Roo.get(target);
22753         if(!this.el){
22754             this.el = Roo.Shadow.Pool.pull();
22755             if(this.el.dom.nextSibling != target.dom){
22756                 this.el.insertBefore(target);
22757             }
22758         }
22759         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22760         if(Roo.isIE){
22761             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22762         }
22763         this.realign(
22764             target.getLeft(true),
22765             target.getTop(true),
22766             target.getWidth(),
22767             target.getHeight()
22768         );
22769         this.el.dom.style.display = "block";
22770     },
22771
22772     /**
22773      * Returns true if the shadow is visible, else false
22774      */
22775     isVisible : function(){
22776         return this.el ? true : false;  
22777     },
22778
22779     /**
22780      * Direct alignment when values are already available. Show must be called at least once before
22781      * calling this method to ensure it is initialized.
22782      * @param {Number} left The target element left position
22783      * @param {Number} top The target element top position
22784      * @param {Number} width The target element width
22785      * @param {Number} height The target element height
22786      */
22787     realign : function(l, t, w, h){
22788         if(!this.el){
22789             return;
22790         }
22791         var a = this.adjusts, d = this.el.dom, s = d.style;
22792         var iea = 0;
22793         s.left = (l+a.l)+"px";
22794         s.top = (t+a.t)+"px";
22795         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22796  
22797         if(s.width != sws || s.height != shs){
22798             s.width = sws;
22799             s.height = shs;
22800             if(!Roo.isIE){
22801                 var cn = d.childNodes;
22802                 var sww = Math.max(0, (sw-12))+"px";
22803                 cn[0].childNodes[1].style.width = sww;
22804                 cn[1].childNodes[1].style.width = sww;
22805                 cn[2].childNodes[1].style.width = sww;
22806                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22807             }
22808         }
22809     },
22810
22811     /**
22812      * Hides this shadow
22813      */
22814     hide : function(){
22815         if(this.el){
22816             this.el.dom.style.display = "none";
22817             Roo.Shadow.Pool.push(this.el);
22818             delete this.el;
22819         }
22820     },
22821
22822     /**
22823      * Adjust the z-index of this shadow
22824      * @param {Number} zindex The new z-index
22825      */
22826     setZIndex : function(z){
22827         this.zIndex = z;
22828         if(this.el){
22829             this.el.setStyle("z-index", z);
22830         }
22831     }
22832 };
22833
22834 // Private utility class that manages the internal Shadow cache
22835 Roo.Shadow.Pool = function(){
22836     var p = [];
22837     var markup = Roo.isIE ?
22838                  '<div class="x-ie-shadow"></div>' :
22839                  '<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>';
22840     return {
22841         pull : function(){
22842             var sh = p.shift();
22843             if(!sh){
22844                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22845                 sh.autoBoxAdjust = false;
22846             }
22847             return sh;
22848         },
22849
22850         push : function(sh){
22851             p.push(sh);
22852         }
22853     };
22854 }();/*
22855  * Based on:
22856  * Ext JS Library 1.1.1
22857  * Copyright(c) 2006-2007, Ext JS, LLC.
22858  *
22859  * Originally Released Under LGPL - original licence link has changed is not relivant.
22860  *
22861  * Fork - LGPL
22862  * <script type="text/javascript">
22863  */
22864
22865 /**
22866  * @class Roo.BoxComponent
22867  * @extends Roo.Component
22868  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22869  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22870  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22871  * layout containers.
22872  * @constructor
22873  * @param {Roo.Element/String/Object} config The configuration options.
22874  */
22875 Roo.BoxComponent = function(config){
22876     Roo.Component.call(this, config);
22877     this.addEvents({
22878         /**
22879          * @event resize
22880          * Fires after the component is resized.
22881              * @param {Roo.Component} this
22882              * @param {Number} adjWidth The box-adjusted width that was set
22883              * @param {Number} adjHeight The box-adjusted height that was set
22884              * @param {Number} rawWidth The width that was originally specified
22885              * @param {Number} rawHeight The height that was originally specified
22886              */
22887         resize : true,
22888         /**
22889          * @event move
22890          * Fires after the component is moved.
22891              * @param {Roo.Component} this
22892              * @param {Number} x The new x position
22893              * @param {Number} y The new y position
22894              */
22895         move : true
22896     });
22897 };
22898
22899 Roo.extend(Roo.BoxComponent, Roo.Component, {
22900     // private, set in afterRender to signify that the component has been rendered
22901     boxReady : false,
22902     // private, used to defer height settings to subclasses
22903     deferHeight: false,
22904     /** @cfg {Number} width
22905      * width (optional) size of component
22906      */
22907      /** @cfg {Number} height
22908      * height (optional) size of component
22909      */
22910      
22911     /**
22912      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22913      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22914      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22915      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22916      * @return {Roo.BoxComponent} this
22917      */
22918     setSize : function(w, h){
22919         // support for standard size objects
22920         if(typeof w == 'object'){
22921             h = w.height;
22922             w = w.width;
22923         }
22924         // not rendered
22925         if(!this.boxReady){
22926             this.width = w;
22927             this.height = h;
22928             return this;
22929         }
22930
22931         // prevent recalcs when not needed
22932         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22933             return this;
22934         }
22935         this.lastSize = {width: w, height: h};
22936
22937         var adj = this.adjustSize(w, h);
22938         var aw = adj.width, ah = adj.height;
22939         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22940             var rz = this.getResizeEl();
22941             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22942                 rz.setSize(aw, ah);
22943             }else if(!this.deferHeight && ah !== undefined){
22944                 rz.setHeight(ah);
22945             }else if(aw !== undefined){
22946                 rz.setWidth(aw);
22947             }
22948             this.onResize(aw, ah, w, h);
22949             this.fireEvent('resize', this, aw, ah, w, h);
22950         }
22951         return this;
22952     },
22953
22954     /**
22955      * Gets the current size of the component's underlying element.
22956      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22957      */
22958     getSize : function(){
22959         return this.el.getSize();
22960     },
22961
22962     /**
22963      * Gets the current XY position of the component's underlying element.
22964      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22965      * @return {Array} The XY position of the element (e.g., [100, 200])
22966      */
22967     getPosition : function(local){
22968         if(local === true){
22969             return [this.el.getLeft(true), this.el.getTop(true)];
22970         }
22971         return this.xy || this.el.getXY();
22972     },
22973
22974     /**
22975      * Gets the current box measurements of the component's underlying element.
22976      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22977      * @returns {Object} box An object in the format {x, y, width, height}
22978      */
22979     getBox : function(local){
22980         var s = this.el.getSize();
22981         if(local){
22982             s.x = this.el.getLeft(true);
22983             s.y = this.el.getTop(true);
22984         }else{
22985             var xy = this.xy || this.el.getXY();
22986             s.x = xy[0];
22987             s.y = xy[1];
22988         }
22989         return s;
22990     },
22991
22992     /**
22993      * Sets the current box measurements of the component's underlying element.
22994      * @param {Object} box An object in the format {x, y, width, height}
22995      * @returns {Roo.BoxComponent} this
22996      */
22997     updateBox : function(box){
22998         this.setSize(box.width, box.height);
22999         this.setPagePosition(box.x, box.y);
23000         return this;
23001     },
23002
23003     // protected
23004     getResizeEl : function(){
23005         return this.resizeEl || this.el;
23006     },
23007
23008     // protected
23009     getPositionEl : function(){
23010         return this.positionEl || this.el;
23011     },
23012
23013     /**
23014      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23015      * This method fires the move event.
23016      * @param {Number} left The new left
23017      * @param {Number} top The new top
23018      * @returns {Roo.BoxComponent} this
23019      */
23020     setPosition : function(x, y){
23021         this.x = x;
23022         this.y = y;
23023         if(!this.boxReady){
23024             return this;
23025         }
23026         var adj = this.adjustPosition(x, y);
23027         var ax = adj.x, ay = adj.y;
23028
23029         var el = this.getPositionEl();
23030         if(ax !== undefined || ay !== undefined){
23031             if(ax !== undefined && ay !== undefined){
23032                 el.setLeftTop(ax, ay);
23033             }else if(ax !== undefined){
23034                 el.setLeft(ax);
23035             }else if(ay !== undefined){
23036                 el.setTop(ay);
23037             }
23038             this.onPosition(ax, ay);
23039             this.fireEvent('move', this, ax, ay);
23040         }
23041         return this;
23042     },
23043
23044     /**
23045      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23046      * This method fires the move event.
23047      * @param {Number} x The new x position
23048      * @param {Number} y The new y position
23049      * @returns {Roo.BoxComponent} this
23050      */
23051     setPagePosition : function(x, y){
23052         this.pageX = x;
23053         this.pageY = y;
23054         if(!this.boxReady){
23055             return;
23056         }
23057         if(x === undefined || y === undefined){ // cannot translate undefined points
23058             return;
23059         }
23060         var p = this.el.translatePoints(x, y);
23061         this.setPosition(p.left, p.top);
23062         return this;
23063     },
23064
23065     // private
23066     onRender : function(ct, position){
23067         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23068         if(this.resizeEl){
23069             this.resizeEl = Roo.get(this.resizeEl);
23070         }
23071         if(this.positionEl){
23072             this.positionEl = Roo.get(this.positionEl);
23073         }
23074     },
23075
23076     // private
23077     afterRender : function(){
23078         Roo.BoxComponent.superclass.afterRender.call(this);
23079         this.boxReady = true;
23080         this.setSize(this.width, this.height);
23081         if(this.x || this.y){
23082             this.setPosition(this.x, this.y);
23083         }
23084         if(this.pageX || this.pageY){
23085             this.setPagePosition(this.pageX, this.pageY);
23086         }
23087     },
23088
23089     /**
23090      * Force the component's size to recalculate based on the underlying element's current height and width.
23091      * @returns {Roo.BoxComponent} this
23092      */
23093     syncSize : function(){
23094         delete this.lastSize;
23095         this.setSize(this.el.getWidth(), this.el.getHeight());
23096         return this;
23097     },
23098
23099     /**
23100      * Called after the component is resized, this method is empty by default but can be implemented by any
23101      * subclass that needs to perform custom logic after a resize occurs.
23102      * @param {Number} adjWidth The box-adjusted width that was set
23103      * @param {Number} adjHeight The box-adjusted height that was set
23104      * @param {Number} rawWidth The width that was originally specified
23105      * @param {Number} rawHeight The height that was originally specified
23106      */
23107     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23108
23109     },
23110
23111     /**
23112      * Called after the component is moved, this method is empty by default but can be implemented by any
23113      * subclass that needs to perform custom logic after a move occurs.
23114      * @param {Number} x The new x position
23115      * @param {Number} y The new y position
23116      */
23117     onPosition : function(x, y){
23118
23119     },
23120
23121     // private
23122     adjustSize : function(w, h){
23123         if(this.autoWidth){
23124             w = 'auto';
23125         }
23126         if(this.autoHeight){
23127             h = 'auto';
23128         }
23129         return {width : w, height: h};
23130     },
23131
23132     // private
23133     adjustPosition : function(x, y){
23134         return {x : x, y: y};
23135     }
23136 });/*
23137  * Based on:
23138  * Ext JS Library 1.1.1
23139  * Copyright(c) 2006-2007, Ext JS, LLC.
23140  *
23141  * Originally Released Under LGPL - original licence link has changed is not relivant.
23142  *
23143  * Fork - LGPL
23144  * <script type="text/javascript">
23145  */
23146
23147
23148 /**
23149  * @class Roo.SplitBar
23150  * @extends Roo.util.Observable
23151  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23152  * <br><br>
23153  * Usage:
23154  * <pre><code>
23155 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23156                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23157 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23158 split.minSize = 100;
23159 split.maxSize = 600;
23160 split.animate = true;
23161 split.on('moved', splitterMoved);
23162 </code></pre>
23163  * @constructor
23164  * Create a new SplitBar
23165  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23166  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23167  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23168  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23169                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23170                         position of the SplitBar).
23171  */
23172 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23173     
23174     /** @private */
23175     this.el = Roo.get(dragElement, true);
23176     this.el.dom.unselectable = "on";
23177     /** @private */
23178     this.resizingEl = Roo.get(resizingElement, true);
23179
23180     /**
23181      * @private
23182      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23184      * @type Number
23185      */
23186     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23187     
23188     /**
23189      * The minimum size of the resizing element. (Defaults to 0)
23190      * @type Number
23191      */
23192     this.minSize = 0;
23193     
23194     /**
23195      * The maximum size of the resizing element. (Defaults to 2000)
23196      * @type Number
23197      */
23198     this.maxSize = 2000;
23199     
23200     /**
23201      * Whether to animate the transition to the new size
23202      * @type Boolean
23203      */
23204     this.animate = false;
23205     
23206     /**
23207      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23208      * @type Boolean
23209      */
23210     this.useShim = false;
23211     
23212     /** @private */
23213     this.shim = null;
23214     
23215     if(!existingProxy){
23216         /** @private */
23217         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23218     }else{
23219         this.proxy = Roo.get(existingProxy).dom;
23220     }
23221     /** @private */
23222     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23223     
23224     /** @private */
23225     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23226     
23227     /** @private */
23228     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23229     
23230     /** @private */
23231     this.dragSpecs = {};
23232     
23233     /**
23234      * @private The adapter to use to positon and resize elements
23235      */
23236     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23237     this.adapter.init(this);
23238     
23239     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23240         /** @private */
23241         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23242         this.el.addClass("x-splitbar-h");
23243     }else{
23244         /** @private */
23245         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23246         this.el.addClass("x-splitbar-v");
23247     }
23248     
23249     this.addEvents({
23250         /**
23251          * @event resize
23252          * Fires when the splitter is moved (alias for {@link #event-moved})
23253          * @param {Roo.SplitBar} this
23254          * @param {Number} newSize the new width or height
23255          */
23256         "resize" : true,
23257         /**
23258          * @event moved
23259          * Fires when the splitter is moved
23260          * @param {Roo.SplitBar} this
23261          * @param {Number} newSize the new width or height
23262          */
23263         "moved" : true,
23264         /**
23265          * @event beforeresize
23266          * Fires before the splitter is dragged
23267          * @param {Roo.SplitBar} this
23268          */
23269         "beforeresize" : true,
23270
23271         "beforeapply" : true
23272     });
23273
23274     Roo.util.Observable.call(this);
23275 };
23276
23277 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23278     onStartProxyDrag : function(x, y){
23279         this.fireEvent("beforeresize", this);
23280         if(!this.overlay){
23281             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23282             o.unselectable();
23283             o.enableDisplayMode("block");
23284             // all splitbars share the same overlay
23285             Roo.SplitBar.prototype.overlay = o;
23286         }
23287         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23288         this.overlay.show();
23289         Roo.get(this.proxy).setDisplayed("block");
23290         var size = this.adapter.getElementSize(this);
23291         this.activeMinSize = this.getMinimumSize();;
23292         this.activeMaxSize = this.getMaximumSize();;
23293         var c1 = size - this.activeMinSize;
23294         var c2 = Math.max(this.activeMaxSize - size, 0);
23295         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23296             this.dd.resetConstraints();
23297             this.dd.setXConstraint(
23298                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23299                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23300             );
23301             this.dd.setYConstraint(0, 0);
23302         }else{
23303             this.dd.resetConstraints();
23304             this.dd.setXConstraint(0, 0);
23305             this.dd.setYConstraint(
23306                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23307                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23308             );
23309          }
23310         this.dragSpecs.startSize = size;
23311         this.dragSpecs.startPoint = [x, y];
23312         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23313     },
23314     
23315     /** 
23316      * @private Called after the drag operation by the DDProxy
23317      */
23318     onEndProxyDrag : function(e){
23319         Roo.get(this.proxy).setDisplayed(false);
23320         var endPoint = Roo.lib.Event.getXY(e);
23321         if(this.overlay){
23322             this.overlay.hide();
23323         }
23324         var newSize;
23325         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23326             newSize = this.dragSpecs.startSize + 
23327                 (this.placement == Roo.SplitBar.LEFT ?
23328                     endPoint[0] - this.dragSpecs.startPoint[0] :
23329                     this.dragSpecs.startPoint[0] - endPoint[0]
23330                 );
23331         }else{
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.TOP ?
23334                     endPoint[1] - this.dragSpecs.startPoint[1] :
23335                     this.dragSpecs.startPoint[1] - endPoint[1]
23336                 );
23337         }
23338         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23339         if(newSize != this.dragSpecs.startSize){
23340             if(this.fireEvent('beforeapply', this, newSize) !== false){
23341                 this.adapter.setElementSize(this, newSize);
23342                 this.fireEvent("moved", this, newSize);
23343                 this.fireEvent("resize", this, newSize);
23344             }
23345         }
23346     },
23347     
23348     /**
23349      * Get the adapter this SplitBar uses
23350      * @return The adapter object
23351      */
23352     getAdapter : function(){
23353         return this.adapter;
23354     },
23355     
23356     /**
23357      * Set the adapter this SplitBar uses
23358      * @param {Object} adapter A SplitBar adapter object
23359      */
23360     setAdapter : function(adapter){
23361         this.adapter = adapter;
23362         this.adapter.init(this);
23363     },
23364     
23365     /**
23366      * Gets the minimum size for the resizing element
23367      * @return {Number} The minimum size
23368      */
23369     getMinimumSize : function(){
23370         return this.minSize;
23371     },
23372     
23373     /**
23374      * Sets the minimum size for the resizing element
23375      * @param {Number} minSize The minimum size
23376      */
23377     setMinimumSize : function(minSize){
23378         this.minSize = minSize;
23379     },
23380     
23381     /**
23382      * Gets the maximum size for the resizing element
23383      * @return {Number} The maximum size
23384      */
23385     getMaximumSize : function(){
23386         return this.maxSize;
23387     },
23388     
23389     /**
23390      * Sets the maximum size for the resizing element
23391      * @param {Number} maxSize The maximum size
23392      */
23393     setMaximumSize : function(maxSize){
23394         this.maxSize = maxSize;
23395     },
23396     
23397     /**
23398      * Sets the initialize size for the resizing element
23399      * @param {Number} size The initial size
23400      */
23401     setCurrentSize : function(size){
23402         var oldAnimate = this.animate;
23403         this.animate = false;
23404         this.adapter.setElementSize(this, size);
23405         this.animate = oldAnimate;
23406     },
23407     
23408     /**
23409      * Destroy this splitbar. 
23410      * @param {Boolean} removeEl True to remove the element
23411      */
23412     destroy : function(removeEl){
23413         if(this.shim){
23414             this.shim.remove();
23415         }
23416         this.dd.unreg();
23417         this.proxy.parentNode.removeChild(this.proxy);
23418         if(removeEl){
23419             this.el.remove();
23420         }
23421     }
23422 });
23423
23424 /**
23425  * @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.
23426  */
23427 Roo.SplitBar.createProxy = function(dir){
23428     var proxy = new Roo.Element(document.createElement("div"));
23429     proxy.unselectable();
23430     var cls = 'x-splitbar-proxy';
23431     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23432     document.body.appendChild(proxy.dom);
23433     return proxy.dom;
23434 };
23435
23436 /** 
23437  * @class Roo.SplitBar.BasicLayoutAdapter
23438  * Default Adapter. It assumes the splitter and resizing element are not positioned
23439  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23440  */
23441 Roo.SplitBar.BasicLayoutAdapter = function(){
23442 };
23443
23444 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23445     // do nothing for now
23446     init : function(s){
23447     
23448     },
23449     /**
23450      * Called before drag operations to get the current size of the resizing element. 
23451      * @param {Roo.SplitBar} s The SplitBar using this adapter
23452      */
23453      getElementSize : function(s){
23454         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23455             return s.resizingEl.getWidth();
23456         }else{
23457             return s.resizingEl.getHeight();
23458         }
23459     },
23460     
23461     /**
23462      * Called after drag operations to set the size of the resizing element.
23463      * @param {Roo.SplitBar} s The SplitBar using this adapter
23464      * @param {Number} newSize The new size to set
23465      * @param {Function} onComplete A function to be invoked when resizing is complete
23466      */
23467     setElementSize : function(s, newSize, onComplete){
23468         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23469             if(!s.animate){
23470                 s.resizingEl.setWidth(newSize);
23471                 if(onComplete){
23472                     onComplete(s, newSize);
23473                 }
23474             }else{
23475                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23476             }
23477         }else{
23478             
23479             if(!s.animate){
23480                 s.resizingEl.setHeight(newSize);
23481                 if(onComplete){
23482                     onComplete(s, newSize);
23483                 }
23484             }else{
23485                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23486             }
23487         }
23488     }
23489 };
23490
23491 /** 
23492  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23493  * @extends Roo.SplitBar.BasicLayoutAdapter
23494  * Adapter that  moves the splitter element to align with the resized sizing element. 
23495  * Used with an absolute positioned SplitBar.
23496  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23497  * document.body, make sure you assign an id to the body element.
23498  */
23499 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23500     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23501     this.container = Roo.get(container);
23502 };
23503
23504 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23505     init : function(s){
23506         this.basic.init(s);
23507     },
23508     
23509     getElementSize : function(s){
23510         return this.basic.getElementSize(s);
23511     },
23512     
23513     setElementSize : function(s, newSize, onComplete){
23514         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23515     },
23516     
23517     moveSplitter : function(s){
23518         var yes = Roo.SplitBar;
23519         switch(s.placement){
23520             case yes.LEFT:
23521                 s.el.setX(s.resizingEl.getRight());
23522                 break;
23523             case yes.RIGHT:
23524                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23525                 break;
23526             case yes.TOP:
23527                 s.el.setY(s.resizingEl.getBottom());
23528                 break;
23529             case yes.BOTTOM:
23530                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23531                 break;
23532         }
23533     }
23534 };
23535
23536 /**
23537  * Orientation constant - Create a vertical SplitBar
23538  * @static
23539  * @type Number
23540  */
23541 Roo.SplitBar.VERTICAL = 1;
23542
23543 /**
23544  * Orientation constant - Create a horizontal SplitBar
23545  * @static
23546  * @type Number
23547  */
23548 Roo.SplitBar.HORIZONTAL = 2;
23549
23550 /**
23551  * Placement constant - The resizing element is to the left of the splitter element
23552  * @static
23553  * @type Number
23554  */
23555 Roo.SplitBar.LEFT = 1;
23556
23557 /**
23558  * Placement constant - The resizing element is to the right of the splitter element
23559  * @static
23560  * @type Number
23561  */
23562 Roo.SplitBar.RIGHT = 2;
23563
23564 /**
23565  * Placement constant - The resizing element is positioned above the splitter element
23566  * @static
23567  * @type Number
23568  */
23569 Roo.SplitBar.TOP = 3;
23570
23571 /**
23572  * Placement constant - The resizing element is positioned under splitter element
23573  * @static
23574  * @type Number
23575  */
23576 Roo.SplitBar.BOTTOM = 4;
23577 /*
23578  * Based on:
23579  * Ext JS Library 1.1.1
23580  * Copyright(c) 2006-2007, Ext JS, LLC.
23581  *
23582  * Originally Released Under LGPL - original licence link has changed is not relivant.
23583  *
23584  * Fork - LGPL
23585  * <script type="text/javascript">
23586  */
23587
23588 /**
23589  * @class Roo.View
23590  * @extends Roo.util.Observable
23591  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23592  * This class also supports single and multi selection modes. <br>
23593  * Create a data model bound view:
23594  <pre><code>
23595  var store = new Roo.data.Store(...);
23596
23597  var view = new Roo.View({
23598     el : "my-element",
23599     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23600  
23601     singleSelect: true,
23602     selectedClass: "ydataview-selected",
23603     store: store
23604  });
23605
23606  // listen for node click?
23607  view.on("click", function(vw, index, node, e){
23608  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23609  });
23610
23611  // load XML data
23612  dataModel.load("foobar.xml");
23613  </code></pre>
23614  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23615  * <br><br>
23616  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23617  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23618  * 
23619  * Note: old style constructor is still suported (container, template, config)
23620  * 
23621  * @constructor
23622  * Create a new View
23623  * @param {Object} config The config object
23624  * 
23625  */
23626 Roo.View = function(config, depreciated_tpl, depreciated_config){
23627     
23628     if (typeof(depreciated_tpl) == 'undefined') {
23629         // new way.. - universal constructor.
23630         Roo.apply(this, config);
23631         this.el  = Roo.get(this.el);
23632     } else {
23633         // old format..
23634         this.el  = Roo.get(config);
23635         this.tpl = depreciated_tpl;
23636         Roo.apply(this, depreciated_config);
23637     }
23638      
23639     
23640     if(typeof(this.tpl) == "string"){
23641         this.tpl = new Roo.Template(this.tpl);
23642     } else {
23643         // support xtype ctors..
23644         this.tpl = new Roo.factory(this.tpl, Roo);
23645     }
23646     
23647     
23648     this.tpl.compile();
23649    
23650
23651      
23652     /** @private */
23653     this.addEvents({
23654         /**
23655          * @event beforeclick
23656          * Fires before a click is processed. Returns false to cancel the default action.
23657          * @param {Roo.View} this
23658          * @param {Number} index The index of the target node
23659          * @param {HTMLElement} node The target node
23660          * @param {Roo.EventObject} e The raw event object
23661          */
23662             "beforeclick" : true,
23663         /**
23664          * @event click
23665          * Fires when a template node is clicked.
23666          * @param {Roo.View} this
23667          * @param {Number} index The index of the target node
23668          * @param {HTMLElement} node The target node
23669          * @param {Roo.EventObject} e The raw event object
23670          */
23671             "click" : true,
23672         /**
23673          * @event dblclick
23674          * Fires when a template node is double clicked.
23675          * @param {Roo.View} this
23676          * @param {Number} index The index of the target node
23677          * @param {HTMLElement} node The target node
23678          * @param {Roo.EventObject} e The raw event object
23679          */
23680             "dblclick" : true,
23681         /**
23682          * @event contextmenu
23683          * Fires when a template node is right clicked.
23684          * @param {Roo.View} this
23685          * @param {Number} index The index of the target node
23686          * @param {HTMLElement} node The target node
23687          * @param {Roo.EventObject} e The raw event object
23688          */
23689             "contextmenu" : true,
23690         /**
23691          * @event selectionchange
23692          * Fires when the selected nodes change.
23693          * @param {Roo.View} this
23694          * @param {Array} selections Array of the selected nodes
23695          */
23696             "selectionchange" : true,
23697     
23698         /**
23699          * @event beforeselect
23700          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23701          * @param {Roo.View} this
23702          * @param {HTMLElement} node The node to be selected
23703          * @param {Array} selections Array of currently selected nodes
23704          */
23705             "beforeselect" : true,
23706         /**
23707          * @event preparedata
23708          * Fires on every row to render, to allow you to change the data.
23709          * @param {Roo.View} this
23710          * @param {Object} data to be rendered (change this)
23711          */
23712           "preparedata" : true
23713         });
23714
23715     this.el.on({
23716         "click": this.onClick,
23717         "dblclick": this.onDblClick,
23718         "contextmenu": this.onContextMenu,
23719         scope:this
23720     });
23721
23722     this.selections = [];
23723     this.nodes = [];
23724     this.cmp = new Roo.CompositeElementLite([]);
23725     if(this.store){
23726         this.store = Roo.factory(this.store, Roo.data);
23727         this.setStore(this.store, true);
23728     }
23729     Roo.View.superclass.constructor.call(this);
23730 };
23731
23732 Roo.extend(Roo.View, Roo.util.Observable, {
23733     
23734      /**
23735      * @cfg {Roo.data.Store} store Data store to load data from.
23736      */
23737     store : false,
23738     
23739     /**
23740      * @cfg {String|Roo.Element} el The container element.
23741      */
23742     el : '',
23743     
23744     /**
23745      * @cfg {String|Roo.Template} tpl The template used by this View 
23746      */
23747     tpl : false,
23748     
23749     /**
23750      * @cfg {String} selectedClass The css class to add to selected nodes
23751      */
23752     selectedClass : "x-view-selected",
23753      /**
23754      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23755      */
23756     emptyText : "",
23757     /**
23758      * @cfg {Boolean} multiSelect Allow multiple selection
23759      */
23760     multiSelect : false,
23761     /**
23762      * @cfg {Boolean} singleSelect Allow single selection
23763      */
23764     singleSelect:  false,
23765     
23766     /**
23767      * @cfg {Boolean} toggleSelect - selecting 
23768      */
23769     toggleSelect : false,
23770     
23771     /**
23772      * Returns the element this view is bound to.
23773      * @return {Roo.Element}
23774      */
23775     getEl : function(){
23776         return this.el;
23777     },
23778
23779     /**
23780      * Refreshes the view.
23781      */
23782     refresh : function(){
23783         var t = this.tpl;
23784         this.clearSelections();
23785         this.el.update("");
23786         var html = [];
23787         var records = this.store.getRange();
23788         if(records.length < 1){
23789             this.el.update(this.emptyText);
23790             return;
23791         }
23792         for(var i = 0, len = records.length; i < len; i++){
23793             var data = this.prepareData(records[i].data, i, records[i]);
23794             this.fireEvent("preparedata", this, data, i, records[i]);
23795             html[html.length] = t.apply(data);
23796         }
23797         this.el.update(html.join(""));
23798         this.nodes = this.el.dom.childNodes;
23799         this.updateIndexes(0);
23800     },
23801
23802     /**
23803      * Function to override to reformat the data that is sent to
23804      * the template for each node.
23805      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23806      * a JSON object for an UpdateManager bound view).
23807      */
23808     prepareData : function(data){
23809         return data;
23810     },
23811
23812     onUpdate : function(ds, record){
23813         this.clearSelections();
23814         var index = this.store.indexOf(record);
23815         var n = this.nodes[index];
23816         this.tpl.insertBefore(n, this.prepareData(record.data));
23817         n.parentNode.removeChild(n);
23818         this.updateIndexes(index, index);
23819     },
23820
23821     onAdd : function(ds, records, index){
23822         this.clearSelections();
23823         if(this.nodes.length == 0){
23824             this.refresh();
23825             return;
23826         }
23827         var n = this.nodes[index];
23828         for(var i = 0, len = records.length; i < len; i++){
23829             var d = this.prepareData(records[i].data);
23830             if(n){
23831                 this.tpl.insertBefore(n, d);
23832             }else{
23833                 this.tpl.append(this.el, d);
23834             }
23835         }
23836         this.updateIndexes(index);
23837     },
23838
23839     onRemove : function(ds, record, index){
23840         this.clearSelections();
23841         this.el.dom.removeChild(this.nodes[index]);
23842         this.updateIndexes(index);
23843     },
23844
23845     /**
23846      * Refresh an individual node.
23847      * @param {Number} index
23848      */
23849     refreshNode : function(index){
23850         this.onUpdate(this.store, this.store.getAt(index));
23851     },
23852
23853     updateIndexes : function(startIndex, endIndex){
23854         var ns = this.nodes;
23855         startIndex = startIndex || 0;
23856         endIndex = endIndex || ns.length - 1;
23857         for(var i = startIndex; i <= endIndex; i++){
23858             ns[i].nodeIndex = i;
23859         }
23860     },
23861
23862     /**
23863      * Changes the data store this view uses and refresh the view.
23864      * @param {Store} store
23865      */
23866     setStore : function(store, initial){
23867         if(!initial && this.store){
23868             this.store.un("datachanged", this.refresh);
23869             this.store.un("add", this.onAdd);
23870             this.store.un("remove", this.onRemove);
23871             this.store.un("update", this.onUpdate);
23872             this.store.un("clear", this.refresh);
23873         }
23874         if(store){
23875           
23876             store.on("datachanged", this.refresh, this);
23877             store.on("add", this.onAdd, this);
23878             store.on("remove", this.onRemove, this);
23879             store.on("update", this.onUpdate, this);
23880             store.on("clear", this.refresh, this);
23881         }
23882         
23883         if(store){
23884             this.refresh();
23885         }
23886     },
23887
23888     /**
23889      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23890      * @param {HTMLElement} node
23891      * @return {HTMLElement} The template node
23892      */
23893     findItemFromChild : function(node){
23894         var el = this.el.dom;
23895         if(!node || node.parentNode == el){
23896                     return node;
23897             }
23898             var p = node.parentNode;
23899             while(p && p != el){
23900             if(p.parentNode == el){
23901                 return p;
23902             }
23903             p = p.parentNode;
23904         }
23905             return null;
23906     },
23907
23908     /** @ignore */
23909     onClick : function(e){
23910         var item = this.findItemFromChild(e.getTarget());
23911         if(item){
23912             var index = this.indexOf(item);
23913             if(this.onItemClick(item, index, e) !== false){
23914                 this.fireEvent("click", this, index, item, e);
23915             }
23916         }else{
23917             this.clearSelections();
23918         }
23919     },
23920
23921     /** @ignore */
23922     onContextMenu : function(e){
23923         var item = this.findItemFromChild(e.getTarget());
23924         if(item){
23925             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23926         }
23927     },
23928
23929     /** @ignore */
23930     onDblClick : function(e){
23931         var item = this.findItemFromChild(e.getTarget());
23932         if(item){
23933             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23934         }
23935     },
23936
23937     onItemClick : function(item, index, e)
23938     {
23939         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23940             return false;
23941         }
23942         if (this.toggleSelect) {
23943             var m = this.isSelected(item) ? 'unselect' : 'select';
23944             Roo.log(m);
23945             var _t = this;
23946             _t[m](item, true, false);
23947             return true;
23948         }
23949         if(this.multiSelect || this.singleSelect){
23950             if(this.multiSelect && e.shiftKey && this.lastSelection){
23951                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23952             }else{
23953                 this.select(item, this.multiSelect && e.ctrlKey);
23954                 this.lastSelection = item;
23955             }
23956             e.preventDefault();
23957         }
23958         return true;
23959     },
23960
23961     /**
23962      * Get the number of selected nodes.
23963      * @return {Number}
23964      */
23965     getSelectionCount : function(){
23966         return this.selections.length;
23967     },
23968
23969     /**
23970      * Get the currently selected nodes.
23971      * @return {Array} An array of HTMLElements
23972      */
23973     getSelectedNodes : function(){
23974         return this.selections;
23975     },
23976
23977     /**
23978      * Get the indexes of the selected nodes.
23979      * @return {Array}
23980      */
23981     getSelectedIndexes : function(){
23982         var indexes = [], s = this.selections;
23983         for(var i = 0, len = s.length; i < len; i++){
23984             indexes.push(s[i].nodeIndex);
23985         }
23986         return indexes;
23987     },
23988
23989     /**
23990      * Clear all selections
23991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23992      */
23993     clearSelections : function(suppressEvent){
23994         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23995             this.cmp.elements = this.selections;
23996             this.cmp.removeClass(this.selectedClass);
23997             this.selections = [];
23998             if(!suppressEvent){
23999                 this.fireEvent("selectionchange", this, this.selections);
24000             }
24001         }
24002     },
24003
24004     /**
24005      * Returns true if the passed node is selected
24006      * @param {HTMLElement/Number} node The node or node index
24007      * @return {Boolean}
24008      */
24009     isSelected : function(node){
24010         var s = this.selections;
24011         if(s.length < 1){
24012             return false;
24013         }
24014         node = this.getNode(node);
24015         return s.indexOf(node) !== -1;
24016     },
24017
24018     /**
24019      * Selects nodes.
24020      * @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
24021      * @param {Boolean} keepExisting (optional) true to keep existing selections
24022      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24023      */
24024     select : function(nodeInfo, keepExisting, suppressEvent){
24025         if(nodeInfo instanceof Array){
24026             if(!keepExisting){
24027                 this.clearSelections(true);
24028             }
24029             for(var i = 0, len = nodeInfo.length; i < len; i++){
24030                 this.select(nodeInfo[i], true, true);
24031             }
24032             return;
24033         } 
24034         var node = this.getNode(nodeInfo);
24035         if(!node || this.isSelected(node)){
24036             return; // already selected.
24037         }
24038         if(!keepExisting){
24039             this.clearSelections(true);
24040         }
24041         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24042             Roo.fly(node).addClass(this.selectedClass);
24043             this.selections.push(node);
24044             if(!suppressEvent){
24045                 this.fireEvent("selectionchange", this, this.selections);
24046             }
24047         }
24048         
24049         
24050     },
24051       /**
24052      * Unselects nodes.
24053      * @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
24054      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24055      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24056      */
24057     unselect : function(nodeInfo, keepExisting, suppressEvent)
24058     {
24059         if(nodeInfo instanceof Array){
24060             Roo.each(this.selections, function(s) {
24061                 this.unselect(s, nodeInfo);
24062             }, this);
24063             return;
24064         }
24065         var node = this.getNode(nodeInfo);
24066         if(!node || !this.isSelected(node)){
24067             Roo.log("not selected");
24068             return; // not selected.
24069         }
24070         // fireevent???
24071         var ns = [];
24072         Roo.each(this.selections, function(s) {
24073             if (s == node ) {
24074                 Roo.fly(node).removeClass(this.selectedClass);
24075
24076                 return;
24077             }
24078             ns.push(s);
24079         },this);
24080         
24081         this.selections= ns;
24082         this.fireEvent("selectionchange", this, this.selections);
24083     },
24084
24085     /**
24086      * Gets a template node.
24087      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24088      * @return {HTMLElement} The node or null if it wasn't found
24089      */
24090     getNode : function(nodeInfo){
24091         if(typeof nodeInfo == "string"){
24092             return document.getElementById(nodeInfo);
24093         }else if(typeof nodeInfo == "number"){
24094             return this.nodes[nodeInfo];
24095         }
24096         return nodeInfo;
24097     },
24098
24099     /**
24100      * Gets a range template nodes.
24101      * @param {Number} startIndex
24102      * @param {Number} endIndex
24103      * @return {Array} An array of nodes
24104      */
24105     getNodes : function(start, end){
24106         var ns = this.nodes;
24107         start = start || 0;
24108         end = typeof end == "undefined" ? ns.length - 1 : end;
24109         var nodes = [];
24110         if(start <= end){
24111             for(var i = start; i <= end; i++){
24112                 nodes.push(ns[i]);
24113             }
24114         } else{
24115             for(var i = start; i >= end; i--){
24116                 nodes.push(ns[i]);
24117             }
24118         }
24119         return nodes;
24120     },
24121
24122     /**
24123      * Finds the index of the passed node
24124      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24125      * @return {Number} The index of the node or -1
24126      */
24127     indexOf : function(node){
24128         node = this.getNode(node);
24129         if(typeof node.nodeIndex == "number"){
24130             return node.nodeIndex;
24131         }
24132         var ns = this.nodes;
24133         for(var i = 0, len = ns.length; i < len; i++){
24134             if(ns[i] == node){
24135                 return i;
24136             }
24137         }
24138         return -1;
24139     }
24140 });
24141 /*
24142  * Based on:
24143  * Ext JS Library 1.1.1
24144  * Copyright(c) 2006-2007, Ext JS, LLC.
24145  *
24146  * Originally Released Under LGPL - original licence link has changed is not relivant.
24147  *
24148  * Fork - LGPL
24149  * <script type="text/javascript">
24150  */
24151
24152 /**
24153  * @class Roo.JsonView
24154  * @extends Roo.View
24155  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24156 <pre><code>
24157 var view = new Roo.JsonView({
24158     container: "my-element",
24159     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24160     multiSelect: true, 
24161     jsonRoot: "data" 
24162 });
24163
24164 // listen for node click?
24165 view.on("click", function(vw, index, node, e){
24166     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24167 });
24168
24169 // direct load of JSON data
24170 view.load("foobar.php");
24171
24172 // Example from my blog list
24173 var tpl = new Roo.Template(
24174     '&lt;div class="entry"&gt;' +
24175     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24176     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24177     "&lt;/div&gt;&lt;hr /&gt;"
24178 );
24179
24180 var moreView = new Roo.JsonView({
24181     container :  "entry-list", 
24182     template : tpl,
24183     jsonRoot: "posts"
24184 });
24185 moreView.on("beforerender", this.sortEntries, this);
24186 moreView.load({
24187     url: "/blog/get-posts.php",
24188     params: "allposts=true",
24189     text: "Loading Blog Entries..."
24190 });
24191 </code></pre>
24192
24193 * Note: old code is supported with arguments : (container, template, config)
24194
24195
24196  * @constructor
24197  * Create a new JsonView
24198  * 
24199  * @param {Object} config The config object
24200  * 
24201  */
24202 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24203     
24204     
24205     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24206
24207     var um = this.el.getUpdateManager();
24208     um.setRenderer(this);
24209     um.on("update", this.onLoad, this);
24210     um.on("failure", this.onLoadException, this);
24211
24212     /**
24213      * @event beforerender
24214      * Fires before rendering of the downloaded JSON data.
24215      * @param {Roo.JsonView} this
24216      * @param {Object} data The JSON data loaded
24217      */
24218     /**
24219      * @event load
24220      * Fires when data is loaded.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      * @param {Object} response The raw Connect response object
24224      */
24225     /**
24226      * @event loadexception
24227      * Fires when loading fails.
24228      * @param {Roo.JsonView} this
24229      * @param {Object} response The raw Connect response object
24230      */
24231     this.addEvents({
24232         'beforerender' : true,
24233         'load' : true,
24234         'loadexception' : true
24235     });
24236 };
24237 Roo.extend(Roo.JsonView, Roo.View, {
24238     /**
24239      * @type {String} The root property in the loaded JSON object that contains the data
24240      */
24241     jsonRoot : "",
24242
24243     /**
24244      * Refreshes the view.
24245      */
24246     refresh : function(){
24247         this.clearSelections();
24248         this.el.update("");
24249         var html = [];
24250         var o = this.jsonData;
24251         if(o && o.length > 0){
24252             for(var i = 0, len = o.length; i < len; i++){
24253                 var data = this.prepareData(o[i], i, o);
24254                 html[html.length] = this.tpl.apply(data);
24255             }
24256         }else{
24257             html.push(this.emptyText);
24258         }
24259         this.el.update(html.join(""));
24260         this.nodes = this.el.dom.childNodes;
24261         this.updateIndexes(0);
24262     },
24263
24264     /**
24265      * 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.
24266      * @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:
24267      <pre><code>
24268      view.load({
24269          url: "your-url.php",
24270          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24271          callback: yourFunction,
24272          scope: yourObject, //(optional scope)
24273          discardUrl: false,
24274          nocache: false,
24275          text: "Loading...",
24276          timeout: 30,
24277          scripts: false
24278      });
24279      </code></pre>
24280      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24281      * 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.
24282      * @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}
24283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24284      * @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.
24285      */
24286     load : function(){
24287         var um = this.el.getUpdateManager();
24288         um.update.apply(um, arguments);
24289     },
24290
24291     render : function(el, response){
24292         this.clearSelections();
24293         this.el.update("");
24294         var o;
24295         try{
24296             o = Roo.util.JSON.decode(response.responseText);
24297             if(this.jsonRoot){
24298                 
24299                 o = o[this.jsonRoot];
24300             }
24301         } catch(e){
24302         }
24303         /**
24304          * The current JSON data or null
24305          */
24306         this.jsonData = o;
24307         this.beforeRender();
24308         this.refresh();
24309     },
24310
24311 /**
24312  * Get the number of records in the current JSON dataset
24313  * @return {Number}
24314  */
24315     getCount : function(){
24316         return this.jsonData ? this.jsonData.length : 0;
24317     },
24318
24319 /**
24320  * Returns the JSON object for the specified node(s)
24321  * @param {HTMLElement/Array} node The node or an array of nodes
24322  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24323  * you get the JSON object for the node
24324  */
24325     getNodeData : function(node){
24326         if(node instanceof Array){
24327             var data = [];
24328             for(var i = 0, len = node.length; i < len; i++){
24329                 data.push(this.getNodeData(node[i]));
24330             }
24331             return data;
24332         }
24333         return this.jsonData[this.indexOf(node)] || null;
24334     },
24335
24336     beforeRender : function(){
24337         this.snapshot = this.jsonData;
24338         if(this.sortInfo){
24339             this.sort.apply(this, this.sortInfo);
24340         }
24341         this.fireEvent("beforerender", this, this.jsonData);
24342     },
24343
24344     onLoad : function(el, o){
24345         this.fireEvent("load", this, this.jsonData, o);
24346     },
24347
24348     onLoadException : function(el, o){
24349         this.fireEvent("loadexception", this, o);
24350     },
24351
24352 /**
24353  * Filter the data by a specific property.
24354  * @param {String} property A property on your JSON objects
24355  * @param {String/RegExp} value Either string that the property values
24356  * should start with, or a RegExp to test against the property
24357  */
24358     filter : function(property, value){
24359         if(this.jsonData){
24360             var data = [];
24361             var ss = this.snapshot;
24362             if(typeof value == "string"){
24363                 var vlen = value.length;
24364                 if(vlen == 0){
24365                     this.clearFilter();
24366                     return;
24367                 }
24368                 value = value.toLowerCase();
24369                 for(var i = 0, len = ss.length; i < len; i++){
24370                     var o = ss[i];
24371                     if(o[property].substr(0, vlen).toLowerCase() == value){
24372                         data.push(o);
24373                     }
24374                 }
24375             } else if(value.exec){ // regex?
24376                 for(var i = 0, len = ss.length; i < len; i++){
24377                     var o = ss[i];
24378                     if(value.test(o[property])){
24379                         data.push(o);
24380                     }
24381                 }
24382             } else{
24383                 return;
24384             }
24385             this.jsonData = data;
24386             this.refresh();
24387         }
24388     },
24389
24390 /**
24391  * Filter by a function. The passed function will be called with each
24392  * object in the current dataset. If the function returns true the value is kept,
24393  * otherwise it is filtered.
24394  * @param {Function} fn
24395  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24396  */
24397     filterBy : function(fn, scope){
24398         if(this.jsonData){
24399             var data = [];
24400             var ss = this.snapshot;
24401             for(var i = 0, len = ss.length; i < len; i++){
24402                 var o = ss[i];
24403                 if(fn.call(scope || this, o)){
24404                     data.push(o);
24405                 }
24406             }
24407             this.jsonData = data;
24408             this.refresh();
24409         }
24410     },
24411
24412 /**
24413  * Clears the current filter.
24414  */
24415     clearFilter : function(){
24416         if(this.snapshot && this.jsonData != this.snapshot){
24417             this.jsonData = this.snapshot;
24418             this.refresh();
24419         }
24420     },
24421
24422
24423 /**
24424  * Sorts the data for this view and refreshes it.
24425  * @param {String} property A property on your JSON objects to sort on
24426  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24427  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24428  */
24429     sort : function(property, dir, sortType){
24430         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24431         if(this.jsonData){
24432             var p = property;
24433             var dsc = dir && dir.toLowerCase() == "desc";
24434             var f = function(o1, o2){
24435                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24436                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24437                 ;
24438                 if(v1 < v2){
24439                     return dsc ? +1 : -1;
24440                 } else if(v1 > v2){
24441                     return dsc ? -1 : +1;
24442                 } else{
24443                     return 0;
24444                 }
24445             };
24446             this.jsonData.sort(f);
24447             this.refresh();
24448             if(this.jsonData != this.snapshot){
24449                 this.snapshot.sort(f);
24450             }
24451         }
24452     }
24453 });/*
24454  * Based on:
24455  * Ext JS Library 1.1.1
24456  * Copyright(c) 2006-2007, Ext JS, LLC.
24457  *
24458  * Originally Released Under LGPL - original licence link has changed is not relivant.
24459  *
24460  * Fork - LGPL
24461  * <script type="text/javascript">
24462  */
24463  
24464
24465 /**
24466  * @class Roo.ColorPalette
24467  * @extends Roo.Component
24468  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24469  * Here's an example of typical usage:
24470  * <pre><code>
24471 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24472 cp.render('my-div');
24473
24474 cp.on('select', function(palette, selColor){
24475     // do something with selColor
24476 });
24477 </code></pre>
24478  * @constructor
24479  * Create a new ColorPalette
24480  * @param {Object} config The config object
24481  */
24482 Roo.ColorPalette = function(config){
24483     Roo.ColorPalette.superclass.constructor.call(this, config);
24484     this.addEvents({
24485         /**
24486              * @event select
24487              * Fires when a color is selected
24488              * @param {ColorPalette} this
24489              * @param {String} color The 6-digit color hex code (without the # symbol)
24490              */
24491         select: true
24492     });
24493
24494     if(this.handler){
24495         this.on("select", this.handler, this.scope, true);
24496     }
24497 };
24498 Roo.extend(Roo.ColorPalette, Roo.Component, {
24499     /**
24500      * @cfg {String} itemCls
24501      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24502      */
24503     itemCls : "x-color-palette",
24504     /**
24505      * @cfg {String} value
24506      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24507      * the hex codes are case-sensitive.
24508      */
24509     value : null,
24510     clickEvent:'click',
24511     // private
24512     ctype: "Roo.ColorPalette",
24513
24514     /**
24515      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24516      */
24517     allowReselect : false,
24518
24519     /**
24520      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24521      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24522      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24523      * of colors with the width setting until the box is symmetrical.</p>
24524      * <p>You can override individual colors if needed:</p>
24525      * <pre><code>
24526 var cp = new Roo.ColorPalette();
24527 cp.colors[0] = "FF0000";  // change the first box to red
24528 </code></pre>
24529
24530 Or you can provide a custom array of your own for complete control:
24531 <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors = ["000000", "993300", "333300"];
24534 </code></pre>
24535      * @type Array
24536      */
24537     colors : [
24538         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24539         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24540         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24541         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24542         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24543     ],
24544
24545     // private
24546     onRender : function(container, position){
24547         var t = new Roo.MasterTemplate(
24548             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24549         );
24550         var c = this.colors;
24551         for(var i = 0, len = c.length; i < len; i++){
24552             t.add([c[i]]);
24553         }
24554         var el = document.createElement("div");
24555         el.className = this.itemCls;
24556         t.overwrite(el);
24557         container.dom.insertBefore(el, position);
24558         this.el = Roo.get(el);
24559         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24560         if(this.clickEvent != 'click'){
24561             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24562         }
24563     },
24564
24565     // private
24566     afterRender : function(){
24567         Roo.ColorPalette.superclass.afterRender.call(this);
24568         if(this.value){
24569             var s = this.value;
24570             this.value = null;
24571             this.select(s);
24572         }
24573     },
24574
24575     // private
24576     handleClick : function(e, t){
24577         e.preventDefault();
24578         if(!this.disabled){
24579             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24580             this.select(c.toUpperCase());
24581         }
24582     },
24583
24584     /**
24585      * Selects the specified color in the palette (fires the select event)
24586      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24587      */
24588     select : function(color){
24589         color = color.replace("#", "");
24590         if(color != this.value || this.allowReselect){
24591             var el = this.el;
24592             if(this.value){
24593                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24594             }
24595             el.child("a.color-"+color).addClass("x-color-palette-sel");
24596             this.value = color;
24597             this.fireEvent("select", this, color);
24598         }
24599     }
24600 });/*
24601  * Based on:
24602  * Ext JS Library 1.1.1
24603  * Copyright(c) 2006-2007, Ext JS, LLC.
24604  *
24605  * Originally Released Under LGPL - original licence link has changed is not relivant.
24606  *
24607  * Fork - LGPL
24608  * <script type="text/javascript">
24609  */
24610  
24611 /**
24612  * @class Roo.DatePicker
24613  * @extends Roo.Component
24614  * Simple date picker class.
24615  * @constructor
24616  * Create a new DatePicker
24617  * @param {Object} config The config object
24618  */
24619 Roo.DatePicker = function(config){
24620     Roo.DatePicker.superclass.constructor.call(this, config);
24621
24622     this.value = config && config.value ?
24623                  config.value.clearTime() : new Date().clearTime();
24624
24625     this.addEvents({
24626         /**
24627              * @event select
24628              * Fires when a date is selected
24629              * @param {DatePicker} this
24630              * @param {Date} date The selected date
24631              */
24632         'select': true,
24633         /**
24634              * @event monthchange
24635              * Fires when the displayed month changes 
24636              * @param {DatePicker} this
24637              * @param {Date} date The selected month
24638              */
24639         'monthchange': true
24640     });
24641
24642     if(this.handler){
24643         this.on("select", this.handler,  this.scope || this);
24644     }
24645     // build the disabledDatesRE
24646     if(!this.disabledDatesRE && this.disabledDates){
24647         var dd = this.disabledDates;
24648         var re = "(?:";
24649         for(var i = 0; i < dd.length; i++){
24650             re += dd[i];
24651             if(i != dd.length-1) re += "|";
24652         }
24653         this.disabledDatesRE = new RegExp(re + ")");
24654     }
24655 };
24656
24657 Roo.extend(Roo.DatePicker, Roo.Component, {
24658     /**
24659      * @cfg {String} todayText
24660      * The text to display on the button that selects the current date (defaults to "Today")
24661      */
24662     todayText : "Today",
24663     /**
24664      * @cfg {String} okText
24665      * The text to display on the ok button
24666      */
24667     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24668     /**
24669      * @cfg {String} cancelText
24670      * The text to display on the cancel button
24671      */
24672     cancelText : "Cancel",
24673     /**
24674      * @cfg {String} todayTip
24675      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24676      */
24677     todayTip : "{0} (Spacebar)",
24678     /**
24679      * @cfg {Date} minDate
24680      * Minimum allowable date (JavaScript date object, defaults to null)
24681      */
24682     minDate : null,
24683     /**
24684      * @cfg {Date} maxDate
24685      * Maximum allowable date (JavaScript date object, defaults to null)
24686      */
24687     maxDate : null,
24688     /**
24689      * @cfg {String} minText
24690      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24691      */
24692     minText : "This date is before the minimum date",
24693     /**
24694      * @cfg {String} maxText
24695      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24696      */
24697     maxText : "This date is after the maximum date",
24698     /**
24699      * @cfg {String} format
24700      * The default date format string which can be overriden for localization support.  The format must be
24701      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24702      */
24703     format : "m/d/y",
24704     /**
24705      * @cfg {Array} disabledDays
24706      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24707      */
24708     disabledDays : null,
24709     /**
24710      * @cfg {String} disabledDaysText
24711      * The tooltip to display when the date falls on a disabled day (defaults to "")
24712      */
24713     disabledDaysText : "",
24714     /**
24715      * @cfg {RegExp} disabledDatesRE
24716      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24717      */
24718     disabledDatesRE : null,
24719     /**
24720      * @cfg {String} disabledDatesText
24721      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24722      */
24723     disabledDatesText : "",
24724     /**
24725      * @cfg {Boolean} constrainToViewport
24726      * True to constrain the date picker to the viewport (defaults to true)
24727      */
24728     constrainToViewport : true,
24729     /**
24730      * @cfg {Array} monthNames
24731      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24732      */
24733     monthNames : Date.monthNames,
24734     /**
24735      * @cfg {Array} dayNames
24736      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24737      */
24738     dayNames : Date.dayNames,
24739     /**
24740      * @cfg {String} nextText
24741      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24742      */
24743     nextText: 'Next Month (Control+Right)',
24744     /**
24745      * @cfg {String} prevText
24746      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24747      */
24748     prevText: 'Previous Month (Control+Left)',
24749     /**
24750      * @cfg {String} monthYearText
24751      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24752      */
24753     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24754     /**
24755      * @cfg {Number} startDay
24756      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24757      */
24758     startDay : 0,
24759     /**
24760      * @cfg {Bool} showClear
24761      * Show a clear button (usefull for date form elements that can be blank.)
24762      */
24763     
24764     showClear: false,
24765     
24766     /**
24767      * Sets the value of the date field
24768      * @param {Date} value The date to set
24769      */
24770     setValue : function(value){
24771         var old = this.value;
24772         this.value = value.clearTime(true);
24773         if(this.el){
24774             this.update(this.value);
24775         }
24776     },
24777
24778     /**
24779      * Gets the current selected value of the date field
24780      * @return {Date} The selected date
24781      */
24782     getValue : function(){
24783         return this.value;
24784     },
24785
24786     // private
24787     focus : function(){
24788         if(this.el){
24789             this.update(this.activeDate);
24790         }
24791     },
24792
24793     // private
24794     onRender : function(container, position){
24795         var m = [
24796              '<table cellspacing="0">',
24797                 '<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>',
24798                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24799         var dn = this.dayNames;
24800         for(var i = 0; i < 7; i++){
24801             var d = this.startDay+i;
24802             if(d > 6){
24803                 d = d-7;
24804             }
24805             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24806         }
24807         m[m.length] = "</tr></thead><tbody><tr>";
24808         for(var i = 0; i < 42; i++) {
24809             if(i % 7 == 0 && i != 0){
24810                 m[m.length] = "</tr><tr>";
24811             }
24812             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24813         }
24814         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24815             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24816
24817         var el = document.createElement("div");
24818         el.className = "x-date-picker";
24819         el.innerHTML = m.join("");
24820
24821         container.dom.insertBefore(el, position);
24822
24823         this.el = Roo.get(el);
24824         this.eventEl = Roo.get(el.firstChild);
24825
24826         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24827             handler: this.showPrevMonth,
24828             scope: this,
24829             preventDefault:true,
24830             stopDefault:true
24831         });
24832
24833         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24834             handler: this.showNextMonth,
24835             scope: this,
24836             preventDefault:true,
24837             stopDefault:true
24838         });
24839
24840         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24841
24842         this.monthPicker = this.el.down('div.x-date-mp');
24843         this.monthPicker.enableDisplayMode('block');
24844         
24845         var kn = new Roo.KeyNav(this.eventEl, {
24846             "left" : function(e){
24847                 e.ctrlKey ?
24848                     this.showPrevMonth() :
24849                     this.update(this.activeDate.add("d", -1));
24850             },
24851
24852             "right" : function(e){
24853                 e.ctrlKey ?
24854                     this.showNextMonth() :
24855                     this.update(this.activeDate.add("d", 1));
24856             },
24857
24858             "up" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextYear() :
24861                     this.update(this.activeDate.add("d", -7));
24862             },
24863
24864             "down" : function(e){
24865                 e.ctrlKey ?
24866                     this.showPrevYear() :
24867                     this.update(this.activeDate.add("d", 7));
24868             },
24869
24870             "pageUp" : function(e){
24871                 this.showNextMonth();
24872             },
24873
24874             "pageDown" : function(e){
24875                 this.showPrevMonth();
24876             },
24877
24878             "enter" : function(e){
24879                 e.stopPropagation();
24880                 return true;
24881             },
24882
24883             scope : this
24884         });
24885
24886         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24887
24888         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24889
24890         this.el.unselectable();
24891         
24892         this.cells = this.el.select("table.x-date-inner tbody td");
24893         this.textNodes = this.el.query("table.x-date-inner tbody span");
24894
24895         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24896             text: "&#160;",
24897             tooltip: this.monthYearText
24898         });
24899
24900         this.mbtn.on('click', this.showMonthPicker, this);
24901         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24902
24903
24904         var today = (new Date()).dateFormat(this.format);
24905         
24906         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24907         if (this.showClear) {
24908             baseTb.add( new Roo.Toolbar.Fill());
24909         }
24910         baseTb.add({
24911             text: String.format(this.todayText, today),
24912             tooltip: String.format(this.todayTip, today),
24913             handler: this.selectToday,
24914             scope: this
24915         });
24916         
24917         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24918             
24919         //});
24920         if (this.showClear) {
24921             
24922             baseTb.add( new Roo.Toolbar.Fill());
24923             baseTb.add({
24924                 text: '&#160;',
24925                 cls: 'x-btn-icon x-btn-clear',
24926                 handler: function() {
24927                     //this.value = '';
24928                     this.fireEvent("select", this, '');
24929                 },
24930                 scope: this
24931             });
24932         }
24933         
24934         
24935         if(Roo.isIE){
24936             this.el.repaint();
24937         }
24938         this.update(this.value);
24939     },
24940
24941     createMonthPicker : function(){
24942         if(!this.monthPicker.dom.firstChild){
24943             var buf = ['<table border="0" cellspacing="0">'];
24944             for(var i = 0; i < 6; i++){
24945                 buf.push(
24946                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24947                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24948                     i == 0 ?
24949                     '<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>' :
24950                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24951                 );
24952             }
24953             buf.push(
24954                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24955                     this.okText,
24956                     '</button><button type="button" class="x-date-mp-cancel">',
24957                     this.cancelText,
24958                     '</button></td></tr>',
24959                 '</table>'
24960             );
24961             this.monthPicker.update(buf.join(''));
24962             this.monthPicker.on('click', this.onMonthClick, this);
24963             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24964
24965             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24966             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24967
24968             this.mpMonths.each(function(m, a, i){
24969                 i += 1;
24970                 if((i%2) == 0){
24971                     m.dom.xmonth = 5 + Math.round(i * .5);
24972                 }else{
24973                     m.dom.xmonth = Math.round((i-1) * .5);
24974                 }
24975             });
24976         }
24977     },
24978
24979     showMonthPicker : function(){
24980         this.createMonthPicker();
24981         var size = this.el.getSize();
24982         this.monthPicker.setSize(size);
24983         this.monthPicker.child('table').setSize(size);
24984
24985         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24986         this.updateMPMonth(this.mpSelMonth);
24987         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24988         this.updateMPYear(this.mpSelYear);
24989
24990         this.monthPicker.slideIn('t', {duration:.2});
24991     },
24992
24993     updateMPYear : function(y){
24994         this.mpyear = y;
24995         var ys = this.mpYears.elements;
24996         for(var i = 1; i <= 10; i++){
24997             var td = ys[i-1], y2;
24998             if((i%2) == 0){
24999                 y2 = y + Math.round(i * .5);
25000                 td.firstChild.innerHTML = y2;
25001                 td.xyear = y2;
25002             }else{
25003                 y2 = y - (5-Math.round(i * .5));
25004                 td.firstChild.innerHTML = y2;
25005                 td.xyear = y2;
25006             }
25007             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25008         }
25009     },
25010
25011     updateMPMonth : function(sm){
25012         this.mpMonths.each(function(m, a, i){
25013             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         });
25015     },
25016
25017     selectMPMonth: function(m){
25018         
25019     },
25020
25021     onMonthClick : function(e, t){
25022         e.stopEvent();
25023         var el = new Roo.Element(t), pn;
25024         if(el.is('button.x-date-mp-cancel')){
25025             this.hideMonthPicker();
25026         }
25027         else if(el.is('button.x-date-mp-ok')){
25028             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25029             this.hideMonthPicker();
25030         }
25031         else if(pn = el.up('td.x-date-mp-month', 2)){
25032             this.mpMonths.removeClass('x-date-mp-sel');
25033             pn.addClass('x-date-mp-sel');
25034             this.mpSelMonth = pn.dom.xmonth;
25035         }
25036         else if(pn = el.up('td.x-date-mp-year', 2)){
25037             this.mpYears.removeClass('x-date-mp-sel');
25038             pn.addClass('x-date-mp-sel');
25039             this.mpSelYear = pn.dom.xyear;
25040         }
25041         else if(el.is('a.x-date-mp-prev')){
25042             this.updateMPYear(this.mpyear-10);
25043         }
25044         else if(el.is('a.x-date-mp-next')){
25045             this.updateMPYear(this.mpyear+10);
25046         }
25047     },
25048
25049     onMonthDblClick : function(e, t){
25050         e.stopEvent();
25051         var el = new Roo.Element(t), pn;
25052         if(pn = el.up('td.x-date-mp-month', 2)){
25053             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25054             this.hideMonthPicker();
25055         }
25056         else if(pn = el.up('td.x-date-mp-year', 2)){
25057             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25058             this.hideMonthPicker();
25059         }
25060     },
25061
25062     hideMonthPicker : function(disableAnim){
25063         if(this.monthPicker){
25064             if(disableAnim === true){
25065                 this.monthPicker.hide();
25066             }else{
25067                 this.monthPicker.slideOut('t', {duration:.2});
25068             }
25069         }
25070     },
25071
25072     // private
25073     showPrevMonth : function(e){
25074         this.update(this.activeDate.add("mo", -1));
25075     },
25076
25077     // private
25078     showNextMonth : function(e){
25079         this.update(this.activeDate.add("mo", 1));
25080     },
25081
25082     // private
25083     showPrevYear : function(){
25084         this.update(this.activeDate.add("y", -1));
25085     },
25086
25087     // private
25088     showNextYear : function(){
25089         this.update(this.activeDate.add("y", 1));
25090     },
25091
25092     // private
25093     handleMouseWheel : function(e){
25094         var delta = e.getWheelDelta();
25095         if(delta > 0){
25096             this.showPrevMonth();
25097             e.stopEvent();
25098         } else if(delta < 0){
25099             this.showNextMonth();
25100             e.stopEvent();
25101         }
25102     },
25103
25104     // private
25105     handleDateClick : function(e, t){
25106         e.stopEvent();
25107         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25108             this.setValue(new Date(t.dateValue));
25109             this.fireEvent("select", this, this.value);
25110         }
25111     },
25112
25113     // private
25114     selectToday : function(){
25115         this.setValue(new Date().clearTime());
25116         this.fireEvent("select", this, this.value);
25117     },
25118
25119     // private
25120     update : function(date)
25121     {
25122         var vd = this.activeDate;
25123         this.activeDate = date;
25124         if(vd && this.el){
25125             var t = date.getTime();
25126             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25127                 this.cells.removeClass("x-date-selected");
25128                 this.cells.each(function(c){
25129                    if(c.dom.firstChild.dateValue == t){
25130                        c.addClass("x-date-selected");
25131                        setTimeout(function(){
25132                             try{c.dom.firstChild.focus();}catch(e){}
25133                        }, 50);
25134                        return false;
25135                    }
25136                 });
25137                 return;
25138             }
25139         }
25140         
25141         var days = date.getDaysInMonth();
25142         var firstOfMonth = date.getFirstDateOfMonth();
25143         var startingPos = firstOfMonth.getDay()-this.startDay;
25144
25145         if(startingPos <= this.startDay){
25146             startingPos += 7;
25147         }
25148
25149         var pm = date.add("mo", -1);
25150         var prevStart = pm.getDaysInMonth()-startingPos;
25151
25152         var cells = this.cells.elements;
25153         var textEls = this.textNodes;
25154         days += startingPos;
25155
25156         // convert everything to numbers so it's fast
25157         var day = 86400000;
25158         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25159         var today = new Date().clearTime().getTime();
25160         var sel = date.clearTime().getTime();
25161         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25162         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25163         var ddMatch = this.disabledDatesRE;
25164         var ddText = this.disabledDatesText;
25165         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25166         var ddaysText = this.disabledDaysText;
25167         var format = this.format;
25168
25169         var setCellClass = function(cal, cell){
25170             cell.title = "";
25171             var t = d.getTime();
25172             cell.firstChild.dateValue = t;
25173             if(t == today){
25174                 cell.className += " x-date-today";
25175                 cell.title = cal.todayText;
25176             }
25177             if(t == sel){
25178                 cell.className += " x-date-selected";
25179                 setTimeout(function(){
25180                     try{cell.firstChild.focus();}catch(e){}
25181                 }, 50);
25182             }
25183             // disabling
25184             if(t < min) {
25185                 cell.className = " x-date-disabled";
25186                 cell.title = cal.minText;
25187                 return;
25188             }
25189             if(t > max) {
25190                 cell.className = " x-date-disabled";
25191                 cell.title = cal.maxText;
25192                 return;
25193             }
25194             if(ddays){
25195                 if(ddays.indexOf(d.getDay()) != -1){
25196                     cell.title = ddaysText;
25197                     cell.className = " x-date-disabled";
25198                 }
25199             }
25200             if(ddMatch && format){
25201                 var fvalue = d.dateFormat(format);
25202                 if(ddMatch.test(fvalue)){
25203                     cell.title = ddText.replace("%0", fvalue);
25204                     cell.className = " x-date-disabled";
25205                 }
25206             }
25207         };
25208
25209         var i = 0;
25210         for(; i < startingPos; i++) {
25211             textEls[i].innerHTML = (++prevStart);
25212             d.setDate(d.getDate()+1);
25213             cells[i].className = "x-date-prevday";
25214             setCellClass(this, cells[i]);
25215         }
25216         for(; i < days; i++){
25217             intDay = i - startingPos + 1;
25218             textEls[i].innerHTML = (intDay);
25219             d.setDate(d.getDate()+1);
25220             cells[i].className = "x-date-active";
25221             setCellClass(this, cells[i]);
25222         }
25223         var extraDays = 0;
25224         for(; i < 42; i++) {
25225              textEls[i].innerHTML = (++extraDays);
25226              d.setDate(d.getDate()+1);
25227              cells[i].className = "x-date-nextday";
25228              setCellClass(this, cells[i]);
25229         }
25230
25231         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25232         this.fireEvent('monthchange', this, date);
25233         
25234         if(!this.internalRender){
25235             var main = this.el.dom.firstChild;
25236             var w = main.offsetWidth;
25237             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25238             Roo.fly(main).setWidth(w);
25239             this.internalRender = true;
25240             // opera does not respect the auto grow header center column
25241             // then, after it gets a width opera refuses to recalculate
25242             // without a second pass
25243             if(Roo.isOpera && !this.secondPass){
25244                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25245                 this.secondPass = true;
25246                 this.update.defer(10, this, [date]);
25247             }
25248         }
25249         
25250         
25251     }
25252 });        /*
25253  * Based on:
25254  * Ext JS Library 1.1.1
25255  * Copyright(c) 2006-2007, Ext JS, LLC.
25256  *
25257  * Originally Released Under LGPL - original licence link has changed is not relivant.
25258  *
25259  * Fork - LGPL
25260  * <script type="text/javascript">
25261  */
25262 /**
25263  * @class Roo.TabPanel
25264  * @extends Roo.util.Observable
25265  * A lightweight tab container.
25266  * <br><br>
25267  * Usage:
25268  * <pre><code>
25269 // basic tabs 1, built from existing content
25270 var tabs = new Roo.TabPanel("tabs1");
25271 tabs.addTab("script", "View Script");
25272 tabs.addTab("markup", "View Markup");
25273 tabs.activate("script");
25274
25275 // more advanced tabs, built from javascript
25276 var jtabs = new Roo.TabPanel("jtabs");
25277 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25278
25279 // set up the UpdateManager
25280 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25281 var updater = tab2.getUpdateManager();
25282 updater.setDefaultUrl("ajax1.htm");
25283 tab2.on('activate', updater.refresh, updater, true);
25284
25285 // Use setUrl for Ajax loading
25286 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25287 tab3.setUrl("ajax2.htm", null, true);
25288
25289 // Disabled tab
25290 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25291 tab4.disable();
25292
25293 jtabs.activate("jtabs-1");
25294  * </code></pre>
25295  * @constructor
25296  * Create a new TabPanel.
25297  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25298  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25299  */
25300 Roo.TabPanel = function(container, config){
25301     /**
25302     * The container element for this TabPanel.
25303     * @type Roo.Element
25304     */
25305     this.el = Roo.get(container, true);
25306     if(config){
25307         if(typeof config == "boolean"){
25308             this.tabPosition = config ? "bottom" : "top";
25309         }else{
25310             Roo.apply(this, config);
25311         }
25312     }
25313     if(this.tabPosition == "bottom"){
25314         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25315         this.el.addClass("x-tabs-bottom");
25316     }
25317     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25318     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25319     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25320     if(Roo.isIE){
25321         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25322     }
25323     if(this.tabPosition != "bottom"){
25324         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25325          * @type Roo.Element
25326          */
25327         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25328         this.el.addClass("x-tabs-top");
25329     }
25330     this.items = [];
25331
25332     this.bodyEl.setStyle("position", "relative");
25333
25334     this.active = null;
25335     this.activateDelegate = this.activate.createDelegate(this);
25336
25337     this.addEvents({
25338         /**
25339          * @event tabchange
25340          * Fires when the active tab changes
25341          * @param {Roo.TabPanel} this
25342          * @param {Roo.TabPanelItem} activePanel The new active tab
25343          */
25344         "tabchange": true,
25345         /**
25346          * @event beforetabchange
25347          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25348          * @param {Roo.TabPanel} this
25349          * @param {Object} e Set cancel to true on this object to cancel the tab change
25350          * @param {Roo.TabPanelItem} tab The tab being changed to
25351          */
25352         "beforetabchange" : true
25353     });
25354
25355     Roo.EventManager.onWindowResize(this.onResize, this);
25356     this.cpad = this.el.getPadding("lr");
25357     this.hiddenCount = 0;
25358
25359
25360     // toolbar on the tabbar support...
25361     if (this.toolbar) {
25362         var tcfg = this.toolbar;
25363         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25364         this.toolbar = new Roo.Toolbar(tcfg);
25365         if (Roo.isSafari) {
25366             var tbl = tcfg.container.child('table', true);
25367             tbl.setAttribute('width', '100%');
25368         }
25369         
25370     }
25371    
25372
25373
25374     Roo.TabPanel.superclass.constructor.call(this);
25375 };
25376
25377 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25378     /*
25379      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25380      */
25381     tabPosition : "top",
25382     /*
25383      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25384      */
25385     currentTabWidth : 0,
25386     /*
25387      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25388      */
25389     minTabWidth : 40,
25390     /*
25391      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25392      */
25393     maxTabWidth : 250,
25394     /*
25395      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25396      */
25397     preferredTabWidth : 175,
25398     /*
25399      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25400      */
25401     resizeTabs : false,
25402     /*
25403      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25404      */
25405     monitorResize : true,
25406     /*
25407      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25408      */
25409     toolbar : false,
25410
25411     /**
25412      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25413      * @param {String} id The id of the div to use <b>or create</b>
25414      * @param {String} text The text for the tab
25415      * @param {String} content (optional) Content to put in the TabPanelItem body
25416      * @param {Boolean} closable (optional) True to create a close icon on the tab
25417      * @return {Roo.TabPanelItem} The created TabPanelItem
25418      */
25419     addTab : function(id, text, content, closable){
25420         var item = new Roo.TabPanelItem(this, id, text, closable);
25421         this.addTabItem(item);
25422         if(content){
25423             item.setContent(content);
25424         }
25425         return item;
25426     },
25427
25428     /**
25429      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25430      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25431      * @return {Roo.TabPanelItem}
25432      */
25433     getTab : function(id){
25434         return this.items[id];
25435     },
25436
25437     /**
25438      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25439      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25440      */
25441     hideTab : function(id){
25442         var t = this.items[id];
25443         if(!t.isHidden()){
25444            t.setHidden(true);
25445            this.hiddenCount++;
25446            this.autoSizeTabs();
25447         }
25448     },
25449
25450     /**
25451      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25452      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25453      */
25454     unhideTab : function(id){
25455         var t = this.items[id];
25456         if(t.isHidden()){
25457            t.setHidden(false);
25458            this.hiddenCount--;
25459            this.autoSizeTabs();
25460         }
25461     },
25462
25463     /**
25464      * Adds an existing {@link Roo.TabPanelItem}.
25465      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25466      */
25467     addTabItem : function(item){
25468         this.items[item.id] = item;
25469         this.items.push(item);
25470         if(this.resizeTabs){
25471            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25472            this.autoSizeTabs();
25473         }else{
25474             item.autoSize();
25475         }
25476     },
25477
25478     /**
25479      * Removes a {@link Roo.TabPanelItem}.
25480      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25481      */
25482     removeTab : function(id){
25483         var items = this.items;
25484         var tab = items[id];
25485         if(!tab) { return; }
25486         var index = items.indexOf(tab);
25487         if(this.active == tab && items.length > 1){
25488             var newTab = this.getNextAvailable(index);
25489             if(newTab) {
25490                 newTab.activate();
25491             }
25492         }
25493         this.stripEl.dom.removeChild(tab.pnode.dom);
25494         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25495             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25496         }
25497         items.splice(index, 1);
25498         delete this.items[tab.id];
25499         tab.fireEvent("close", tab);
25500         tab.purgeListeners();
25501         this.autoSizeTabs();
25502     },
25503
25504     getNextAvailable : function(start){
25505         var items = this.items;
25506         var index = start;
25507         // look for a next tab that will slide over to
25508         // replace the one being removed
25509         while(index < items.length){
25510             var item = items[++index];
25511             if(item && !item.isHidden()){
25512                 return item;
25513             }
25514         }
25515         // if one isn't found select the previous tab (on the left)
25516         index = start;
25517         while(index >= 0){
25518             var item = items[--index];
25519             if(item && !item.isHidden()){
25520                 return item;
25521             }
25522         }
25523         return null;
25524     },
25525
25526     /**
25527      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25528      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25529      */
25530     disableTab : function(id){
25531         var tab = this.items[id];
25532         if(tab && this.active != tab){
25533             tab.disable();
25534         }
25535     },
25536
25537     /**
25538      * Enables a {@link Roo.TabPanelItem} that is disabled.
25539      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25540      */
25541     enableTab : function(id){
25542         var tab = this.items[id];
25543         tab.enable();
25544     },
25545
25546     /**
25547      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25548      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25549      * @return {Roo.TabPanelItem} The TabPanelItem.
25550      */
25551     activate : function(id){
25552         var tab = this.items[id];
25553         if(!tab){
25554             return null;
25555         }
25556         if(tab == this.active || tab.disabled){
25557             return tab;
25558         }
25559         var e = {};
25560         this.fireEvent("beforetabchange", this, e, tab);
25561         if(e.cancel !== true && !tab.disabled){
25562             if(this.active){
25563                 this.active.hide();
25564             }
25565             this.active = this.items[id];
25566             this.active.show();
25567             this.fireEvent("tabchange", this, this.active);
25568         }
25569         return tab;
25570     },
25571
25572     /**
25573      * Gets the active {@link Roo.TabPanelItem}.
25574      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25575      */
25576     getActiveTab : function(){
25577         return this.active;
25578     },
25579
25580     /**
25581      * Updates the tab body element to fit the height of the container element
25582      * for overflow scrolling
25583      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25584      */
25585     syncHeight : function(targetHeight){
25586         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25587         var bm = this.bodyEl.getMargins();
25588         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25589         this.bodyEl.setHeight(newHeight);
25590         return newHeight;
25591     },
25592
25593     onResize : function(){
25594         if(this.monitorResize){
25595             this.autoSizeTabs();
25596         }
25597     },
25598
25599     /**
25600      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25601      */
25602     beginUpdate : function(){
25603         this.updating = true;
25604     },
25605
25606     /**
25607      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25608      */
25609     endUpdate : function(){
25610         this.updating = false;
25611         this.autoSizeTabs();
25612     },
25613
25614     /**
25615      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25616      */
25617     autoSizeTabs : function(){
25618         var count = this.items.length;
25619         var vcount = count - this.hiddenCount;
25620         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25621         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25622         var availWidth = Math.floor(w / vcount);
25623         var b = this.stripBody;
25624         if(b.getWidth() > w){
25625             var tabs = this.items;
25626             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25627             if(availWidth < this.minTabWidth){
25628                 /*if(!this.sleft){    // incomplete scrolling code
25629                     this.createScrollButtons();
25630                 }
25631                 this.showScroll();
25632                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25633             }
25634         }else{
25635             if(this.currentTabWidth < this.preferredTabWidth){
25636                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25637             }
25638         }
25639     },
25640
25641     /**
25642      * Returns the number of tabs in this TabPanel.
25643      * @return {Number}
25644      */
25645      getCount : function(){
25646          return this.items.length;
25647      },
25648
25649     /**
25650      * Resizes all the tabs to the passed width
25651      * @param {Number} The new width
25652      */
25653     setTabWidth : function(width){
25654         this.currentTabWidth = width;
25655         for(var i = 0, len = this.items.length; i < len; i++) {
25656                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25657         }
25658     },
25659
25660     /**
25661      * Destroys this TabPanel
25662      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25663      */
25664     destroy : function(removeEl){
25665         Roo.EventManager.removeResizeListener(this.onResize, this);
25666         for(var i = 0, len = this.items.length; i < len; i++){
25667             this.items[i].purgeListeners();
25668         }
25669         if(removeEl === true){
25670             this.el.update("");
25671             this.el.remove();
25672         }
25673     }
25674 });
25675
25676 /**
25677  * @class Roo.TabPanelItem
25678  * @extends Roo.util.Observable
25679  * Represents an individual item (tab plus body) in a TabPanel.
25680  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25681  * @param {String} id The id of this TabPanelItem
25682  * @param {String} text The text for the tab of this TabPanelItem
25683  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25684  */
25685 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25686     /**
25687      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25688      * @type Roo.TabPanel
25689      */
25690     this.tabPanel = tabPanel;
25691     /**
25692      * The id for this TabPanelItem
25693      * @type String
25694      */
25695     this.id = id;
25696     /** @private */
25697     this.disabled = false;
25698     /** @private */
25699     this.text = text;
25700     /** @private */
25701     this.loaded = false;
25702     this.closable = closable;
25703
25704     /**
25705      * The body element for this TabPanelItem.
25706      * @type Roo.Element
25707      */
25708     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25709     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25710     this.bodyEl.setStyle("display", "block");
25711     this.bodyEl.setStyle("zoom", "1");
25712     this.hideAction();
25713
25714     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25715     /** @private */
25716     this.el = Roo.get(els.el, true);
25717     this.inner = Roo.get(els.inner, true);
25718     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25719     this.pnode = Roo.get(els.el.parentNode, true);
25720     this.el.on("mousedown", this.onTabMouseDown, this);
25721     this.el.on("click", this.onTabClick, this);
25722     /** @private */
25723     if(closable){
25724         var c = Roo.get(els.close, true);
25725         c.dom.title = this.closeText;
25726         c.addClassOnOver("close-over");
25727         c.on("click", this.closeClick, this);
25728      }
25729
25730     this.addEvents({
25731          /**
25732          * @event activate
25733          * Fires when this tab becomes the active tab.
25734          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25735          * @param {Roo.TabPanelItem} this
25736          */
25737         "activate": true,
25738         /**
25739          * @event beforeclose
25740          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25741          * @param {Roo.TabPanelItem} this
25742          * @param {Object} e Set cancel to true on this object to cancel the close.
25743          */
25744         "beforeclose": true,
25745         /**
25746          * @event close
25747          * Fires when this tab is closed.
25748          * @param {Roo.TabPanelItem} this
25749          */
25750          "close": true,
25751         /**
25752          * @event deactivate
25753          * Fires when this tab is no longer the active tab.
25754          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25755          * @param {Roo.TabPanelItem} this
25756          */
25757          "deactivate" : true
25758     });
25759     this.hidden = false;
25760
25761     Roo.TabPanelItem.superclass.constructor.call(this);
25762 };
25763
25764 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25765     purgeListeners : function(){
25766        Roo.util.Observable.prototype.purgeListeners.call(this);
25767        this.el.removeAllListeners();
25768     },
25769     /**
25770      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25771      */
25772     show : function(){
25773         this.pnode.addClass("on");
25774         this.showAction();
25775         if(Roo.isOpera){
25776             this.tabPanel.stripWrap.repaint();
25777         }
25778         this.fireEvent("activate", this.tabPanel, this);
25779     },
25780
25781     /**
25782      * Returns true if this tab is the active tab.
25783      * @return {Boolean}
25784      */
25785     isActive : function(){
25786         return this.tabPanel.getActiveTab() == this;
25787     },
25788
25789     /**
25790      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25791      */
25792     hide : function(){
25793         this.pnode.removeClass("on");
25794         this.hideAction();
25795         this.fireEvent("deactivate", this.tabPanel, this);
25796     },
25797
25798     hideAction : function(){
25799         this.bodyEl.hide();
25800         this.bodyEl.setStyle("position", "absolute");
25801         this.bodyEl.setLeft("-20000px");
25802         this.bodyEl.setTop("-20000px");
25803     },
25804
25805     showAction : function(){
25806         this.bodyEl.setStyle("position", "relative");
25807         this.bodyEl.setTop("");
25808         this.bodyEl.setLeft("");
25809         this.bodyEl.show();
25810     },
25811
25812     /**
25813      * Set the tooltip for the tab.
25814      * @param {String} tooltip The tab's tooltip
25815      */
25816     setTooltip : function(text){
25817         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25818             this.textEl.dom.qtip = text;
25819             this.textEl.dom.removeAttribute('title');
25820         }else{
25821             this.textEl.dom.title = text;
25822         }
25823     },
25824
25825     onTabClick : function(e){
25826         e.preventDefault();
25827         this.tabPanel.activate(this.id);
25828     },
25829
25830     onTabMouseDown : function(e){
25831         e.preventDefault();
25832         this.tabPanel.activate(this.id);
25833     },
25834
25835     getWidth : function(){
25836         return this.inner.getWidth();
25837     },
25838
25839     setWidth : function(width){
25840         var iwidth = width - this.pnode.getPadding("lr");
25841         this.inner.setWidth(iwidth);
25842         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25843         this.pnode.setWidth(width);
25844     },
25845
25846     /**
25847      * Show or hide the tab
25848      * @param {Boolean} hidden True to hide or false to show.
25849      */
25850     setHidden : function(hidden){
25851         this.hidden = hidden;
25852         this.pnode.setStyle("display", hidden ? "none" : "");
25853     },
25854
25855     /**
25856      * Returns true if this tab is "hidden"
25857      * @return {Boolean}
25858      */
25859     isHidden : function(){
25860         return this.hidden;
25861     },
25862
25863     /**
25864      * Returns the text for this tab
25865      * @return {String}
25866      */
25867     getText : function(){
25868         return this.text;
25869     },
25870
25871     autoSize : function(){
25872         //this.el.beginMeasure();
25873         this.textEl.setWidth(1);
25874         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25875         //this.el.endMeasure();
25876     },
25877
25878     /**
25879      * Sets the text for the tab (Note: this also sets the tooltip text)
25880      * @param {String} text The tab's text and tooltip
25881      */
25882     setText : function(text){
25883         this.text = text;
25884         this.textEl.update(text);
25885         this.setTooltip(text);
25886         if(!this.tabPanel.resizeTabs){
25887             this.autoSize();
25888         }
25889     },
25890     /**
25891      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25892      */
25893     activate : function(){
25894         this.tabPanel.activate(this.id);
25895     },
25896
25897     /**
25898      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25899      */
25900     disable : function(){
25901         if(this.tabPanel.active != this){
25902             this.disabled = true;
25903             this.pnode.addClass("disabled");
25904         }
25905     },
25906
25907     /**
25908      * Enables this TabPanelItem if it was previously disabled.
25909      */
25910     enable : function(){
25911         this.disabled = false;
25912         this.pnode.removeClass("disabled");
25913     },
25914
25915     /**
25916      * Sets the content for this TabPanelItem.
25917      * @param {String} content The content
25918      * @param {Boolean} loadScripts true to look for and load scripts
25919      */
25920     setContent : function(content, loadScripts){
25921         this.bodyEl.update(content, loadScripts);
25922     },
25923
25924     /**
25925      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25926      * @return {Roo.UpdateManager} The UpdateManager
25927      */
25928     getUpdateManager : function(){
25929         return this.bodyEl.getUpdateManager();
25930     },
25931
25932     /**
25933      * Set a URL to be used to load the content for this TabPanelItem.
25934      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25935      * @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)
25936      * @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)
25937      * @return {Roo.UpdateManager} The UpdateManager
25938      */
25939     setUrl : function(url, params, loadOnce){
25940         if(this.refreshDelegate){
25941             this.un('activate', this.refreshDelegate);
25942         }
25943         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25944         this.on("activate", this.refreshDelegate);
25945         return this.bodyEl.getUpdateManager();
25946     },
25947
25948     /** @private */
25949     _handleRefresh : function(url, params, loadOnce){
25950         if(!loadOnce || !this.loaded){
25951             var updater = this.bodyEl.getUpdateManager();
25952             updater.update(url, params, this._setLoaded.createDelegate(this));
25953         }
25954     },
25955
25956     /**
25957      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25958      *   Will fail silently if the setUrl method has not been called.
25959      *   This does not activate the panel, just updates its content.
25960      */
25961     refresh : function(){
25962         if(this.refreshDelegate){
25963            this.loaded = false;
25964            this.refreshDelegate();
25965         }
25966     },
25967
25968     /** @private */
25969     _setLoaded : function(){
25970         this.loaded = true;
25971     },
25972
25973     /** @private */
25974     closeClick : function(e){
25975         var o = {};
25976         e.stopEvent();
25977         this.fireEvent("beforeclose", this, o);
25978         if(o.cancel !== true){
25979             this.tabPanel.removeTab(this.id);
25980         }
25981     },
25982     /**
25983      * The text displayed in the tooltip for the close icon.
25984      * @type String
25985      */
25986     closeText : "Close this tab"
25987 });
25988
25989 /** @private */
25990 Roo.TabPanel.prototype.createStrip = function(container){
25991     var strip = document.createElement("div");
25992     strip.className = "x-tabs-wrap";
25993     container.appendChild(strip);
25994     return strip;
25995 };
25996 /** @private */
25997 Roo.TabPanel.prototype.createStripList = function(strip){
25998     // div wrapper for retard IE
25999     // returns the "tr" element.
26000     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26001         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26002         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26003     return strip.firstChild.firstChild.firstChild.firstChild;
26004 };
26005 /** @private */
26006 Roo.TabPanel.prototype.createBody = function(container){
26007     var body = document.createElement("div");
26008     Roo.id(body, "tab-body");
26009     Roo.fly(body).addClass("x-tabs-body");
26010     container.appendChild(body);
26011     return body;
26012 };
26013 /** @private */
26014 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26015     var body = Roo.getDom(id);
26016     if(!body){
26017         body = document.createElement("div");
26018         body.id = id;
26019     }
26020     Roo.fly(body).addClass("x-tabs-item-body");
26021     bodyEl.insertBefore(body, bodyEl.firstChild);
26022     return body;
26023 };
26024 /** @private */
26025 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26026     var td = document.createElement("td");
26027     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26028     //stripEl.appendChild(td);
26029     if(closable){
26030         td.className = "x-tabs-closable";
26031         if(!this.closeTpl){
26032             this.closeTpl = new Roo.Template(
26033                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26034                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26035                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26036             );
26037         }
26038         var el = this.closeTpl.overwrite(td, {"text": text});
26039         var close = el.getElementsByTagName("div")[0];
26040         var inner = el.getElementsByTagName("em")[0];
26041         return {"el": el, "close": close, "inner": inner};
26042     } else {
26043         if(!this.tabTpl){
26044             this.tabTpl = new Roo.Template(
26045                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26046                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26047             );
26048         }
26049         var el = this.tabTpl.overwrite(td, {"text": text});
26050         var inner = el.getElementsByTagName("em")[0];
26051         return {"el": el, "inner": inner};
26052     }
26053 };/*
26054  * Based on:
26055  * Ext JS Library 1.1.1
26056  * Copyright(c) 2006-2007, Ext JS, LLC.
26057  *
26058  * Originally Released Under LGPL - original licence link has changed is not relivant.
26059  *
26060  * Fork - LGPL
26061  * <script type="text/javascript">
26062  */
26063
26064 /**
26065  * @class Roo.Button
26066  * @extends Roo.util.Observable
26067  * Simple Button class
26068  * @cfg {String} text The button text
26069  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26070  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26071  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26072  * @cfg {Object} scope The scope of the handler
26073  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26074  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26075  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26076  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26077  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26078  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26079    applies if enableToggle = true)
26080  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26081  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26082   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26083  * @constructor
26084  * Create a new button
26085  * @param {Object} config The config object
26086  */
26087 Roo.Button = function(renderTo, config)
26088 {
26089     if (!config) {
26090         config = renderTo;
26091         renderTo = config.renderTo || false;
26092     }
26093     
26094     Roo.apply(this, config);
26095     this.addEvents({
26096         /**
26097              * @event click
26098              * Fires when this button is clicked
26099              * @param {Button} this
26100              * @param {EventObject} e The click event
26101              */
26102             "click" : true,
26103         /**
26104              * @event toggle
26105              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26106              * @param {Button} this
26107              * @param {Boolean} pressed
26108              */
26109             "toggle" : true,
26110         /**
26111              * @event mouseover
26112              * Fires when the mouse hovers over the button
26113              * @param {Button} this
26114              * @param {Event} e The event object
26115              */
26116         'mouseover' : true,
26117         /**
26118              * @event mouseout
26119              * Fires when the mouse exits the button
26120              * @param {Button} this
26121              * @param {Event} e The event object
26122              */
26123         'mouseout': true,
26124          /**
26125              * @event render
26126              * Fires when the button is rendered
26127              * @param {Button} this
26128              */
26129         'render': true
26130     });
26131     if(this.menu){
26132         this.menu = Roo.menu.MenuMgr.get(this.menu);
26133     }
26134     // register listeners first!!  - so render can be captured..
26135     Roo.util.Observable.call(this);
26136     if(renderTo){
26137         this.render(renderTo);
26138     }
26139     
26140   
26141 };
26142
26143 Roo.extend(Roo.Button, Roo.util.Observable, {
26144     /**
26145      * 
26146      */
26147     
26148     /**
26149      * Read-only. True if this button is hidden
26150      * @type Boolean
26151      */
26152     hidden : false,
26153     /**
26154      * Read-only. True if this button is disabled
26155      * @type Boolean
26156      */
26157     disabled : false,
26158     /**
26159      * Read-only. True if this button is pressed (only if enableToggle = true)
26160      * @type Boolean
26161      */
26162     pressed : false,
26163
26164     /**
26165      * @cfg {Number} tabIndex 
26166      * The DOM tabIndex for this button (defaults to undefined)
26167      */
26168     tabIndex : undefined,
26169
26170     /**
26171      * @cfg {Boolean} enableToggle
26172      * True to enable pressed/not pressed toggling (defaults to false)
26173      */
26174     enableToggle: false,
26175     /**
26176      * @cfg {Mixed} menu
26177      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26178      */
26179     menu : undefined,
26180     /**
26181      * @cfg {String} menuAlign
26182      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26183      */
26184     menuAlign : "tl-bl?",
26185
26186     /**
26187      * @cfg {String} iconCls
26188      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26189      */
26190     iconCls : undefined,
26191     /**
26192      * @cfg {String} type
26193      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26194      */
26195     type : 'button',
26196
26197     // private
26198     menuClassTarget: 'tr',
26199
26200     /**
26201      * @cfg {String} clickEvent
26202      * The type of event to map to the button's event handler (defaults to 'click')
26203      */
26204     clickEvent : 'click',
26205
26206     /**
26207      * @cfg {Boolean} handleMouseEvents
26208      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26209      */
26210     handleMouseEvents : true,
26211
26212     /**
26213      * @cfg {String} tooltipType
26214      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26215      */
26216     tooltipType : 'qtip',
26217
26218     /**
26219      * @cfg {String} cls
26220      * A CSS class to apply to the button's main element.
26221      */
26222     
26223     /**
26224      * @cfg {Roo.Template} template (Optional)
26225      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26226      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26227      * require code modifications if required elements (e.g. a button) aren't present.
26228      */
26229
26230     // private
26231     render : function(renderTo){
26232         var btn;
26233         if(this.hideParent){
26234             this.parentEl = Roo.get(renderTo);
26235         }
26236         if(!this.dhconfig){
26237             if(!this.template){
26238                 if(!Roo.Button.buttonTemplate){
26239                     // hideous table template
26240                     Roo.Button.buttonTemplate = new Roo.Template(
26241                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26242                         '<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>',
26243                         "</tr></tbody></table>");
26244                 }
26245                 this.template = Roo.Button.buttonTemplate;
26246             }
26247             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26248             var btnEl = btn.child("button:first");
26249             btnEl.on('focus', this.onFocus, this);
26250             btnEl.on('blur', this.onBlur, this);
26251             if(this.cls){
26252                 btn.addClass(this.cls);
26253             }
26254             if(this.icon){
26255                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26256             }
26257             if(this.iconCls){
26258                 btnEl.addClass(this.iconCls);
26259                 if(!this.cls){
26260                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26261                 }
26262             }
26263             if(this.tabIndex !== undefined){
26264                 btnEl.dom.tabIndex = this.tabIndex;
26265             }
26266             if(this.tooltip){
26267                 if(typeof this.tooltip == 'object'){
26268                     Roo.QuickTips.tips(Roo.apply({
26269                           target: btnEl.id
26270                     }, this.tooltip));
26271                 } else {
26272                     btnEl.dom[this.tooltipType] = this.tooltip;
26273                 }
26274             }
26275         }else{
26276             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26277         }
26278         this.el = btn;
26279         if(this.id){
26280             this.el.dom.id = this.el.id = this.id;
26281         }
26282         if(this.menu){
26283             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26284             this.menu.on("show", this.onMenuShow, this);
26285             this.menu.on("hide", this.onMenuHide, this);
26286         }
26287         btn.addClass("x-btn");
26288         if(Roo.isIE && !Roo.isIE7){
26289             this.autoWidth.defer(1, this);
26290         }else{
26291             this.autoWidth();
26292         }
26293         if(this.handleMouseEvents){
26294             btn.on("mouseover", this.onMouseOver, this);
26295             btn.on("mouseout", this.onMouseOut, this);
26296             btn.on("mousedown", this.onMouseDown, this);
26297         }
26298         btn.on(this.clickEvent, this.onClick, this);
26299         //btn.on("mouseup", this.onMouseUp, this);
26300         if(this.hidden){
26301             this.hide();
26302         }
26303         if(this.disabled){
26304             this.disable();
26305         }
26306         Roo.ButtonToggleMgr.register(this);
26307         if(this.pressed){
26308             this.el.addClass("x-btn-pressed");
26309         }
26310         if(this.repeat){
26311             var repeater = new Roo.util.ClickRepeater(btn,
26312                 typeof this.repeat == "object" ? this.repeat : {}
26313             );
26314             repeater.on("click", this.onClick,  this);
26315         }
26316         
26317         this.fireEvent('render', this);
26318         
26319     },
26320     /**
26321      * Returns the button's underlying element
26322      * @return {Roo.Element} The element
26323      */
26324     getEl : function(){
26325         return this.el;  
26326     },
26327     
26328     /**
26329      * Destroys this Button and removes any listeners.
26330      */
26331     destroy : function(){
26332         Roo.ButtonToggleMgr.unregister(this);
26333         this.el.removeAllListeners();
26334         this.purgeListeners();
26335         this.el.remove();
26336     },
26337
26338     // private
26339     autoWidth : function(){
26340         if(this.el){
26341             this.el.setWidth("auto");
26342             if(Roo.isIE7 && Roo.isStrict){
26343                 var ib = this.el.child('button');
26344                 if(ib && ib.getWidth() > 20){
26345                     ib.clip();
26346                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26347                 }
26348             }
26349             if(this.minWidth){
26350                 if(this.hidden){
26351                     this.el.beginMeasure();
26352                 }
26353                 if(this.el.getWidth() < this.minWidth){
26354                     this.el.setWidth(this.minWidth);
26355                 }
26356                 if(this.hidden){
26357                     this.el.endMeasure();
26358                 }
26359             }
26360         }
26361     },
26362
26363     /**
26364      * Assigns this button's click handler
26365      * @param {Function} handler The function to call when the button is clicked
26366      * @param {Object} scope (optional) Scope for the function passed in
26367      */
26368     setHandler : function(handler, scope){
26369         this.handler = handler;
26370         this.scope = scope;  
26371     },
26372     
26373     /**
26374      * Sets this button's text
26375      * @param {String} text The button text
26376      */
26377     setText : function(text){
26378         this.text = text;
26379         if(this.el){
26380             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26381         }
26382         this.autoWidth();
26383     },
26384     
26385     /**
26386      * Gets the text for this button
26387      * @return {String} The button text
26388      */
26389     getText : function(){
26390         return this.text;  
26391     },
26392     
26393     /**
26394      * Show this button
26395      */
26396     show: function(){
26397         this.hidden = false;
26398         if(this.el){
26399             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26400         }
26401     },
26402     
26403     /**
26404      * Hide this button
26405      */
26406     hide: function(){
26407         this.hidden = true;
26408         if(this.el){
26409             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26410         }
26411     },
26412     
26413     /**
26414      * Convenience function for boolean show/hide
26415      * @param {Boolean} visible True to show, false to hide
26416      */
26417     setVisible: function(visible){
26418         if(visible) {
26419             this.show();
26420         }else{
26421             this.hide();
26422         }
26423     },
26424     
26425     /**
26426      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26427      * @param {Boolean} state (optional) Force a particular state
26428      */
26429     toggle : function(state){
26430         state = state === undefined ? !this.pressed : state;
26431         if(state != this.pressed){
26432             if(state){
26433                 this.el.addClass("x-btn-pressed");
26434                 this.pressed = true;
26435                 this.fireEvent("toggle", this, true);
26436             }else{
26437                 this.el.removeClass("x-btn-pressed");
26438                 this.pressed = false;
26439                 this.fireEvent("toggle", this, false);
26440             }
26441             if(this.toggleHandler){
26442                 this.toggleHandler.call(this.scope || this, this, state);
26443             }
26444         }
26445     },
26446     
26447     /**
26448      * Focus the button
26449      */
26450     focus : function(){
26451         this.el.child('button:first').focus();
26452     },
26453     
26454     /**
26455      * Disable this button
26456      */
26457     disable : function(){
26458         if(this.el){
26459             this.el.addClass("x-btn-disabled");
26460         }
26461         this.disabled = true;
26462     },
26463     
26464     /**
26465      * Enable this button
26466      */
26467     enable : function(){
26468         if(this.el){
26469             this.el.removeClass("x-btn-disabled");
26470         }
26471         this.disabled = false;
26472     },
26473
26474     /**
26475      * Convenience function for boolean enable/disable
26476      * @param {Boolean} enabled True to enable, false to disable
26477      */
26478     setDisabled : function(v){
26479         this[v !== true ? "enable" : "disable"]();
26480     },
26481
26482     // private
26483     onClick : function(e){
26484         if(e){
26485             e.preventDefault();
26486         }
26487         if(e.button != 0){
26488             return;
26489         }
26490         if(!this.disabled){
26491             if(this.enableToggle){
26492                 this.toggle();
26493             }
26494             if(this.menu && !this.menu.isVisible()){
26495                 this.menu.show(this.el, this.menuAlign);
26496             }
26497             this.fireEvent("click", this, e);
26498             if(this.handler){
26499                 this.el.removeClass("x-btn-over");
26500                 this.handler.call(this.scope || this, this, e);
26501             }
26502         }
26503     },
26504     // private
26505     onMouseOver : function(e){
26506         if(!this.disabled){
26507             this.el.addClass("x-btn-over");
26508             this.fireEvent('mouseover', this, e);
26509         }
26510     },
26511     // private
26512     onMouseOut : function(e){
26513         if(!e.within(this.el,  true)){
26514             this.el.removeClass("x-btn-over");
26515             this.fireEvent('mouseout', this, e);
26516         }
26517     },
26518     // private
26519     onFocus : function(e){
26520         if(!this.disabled){
26521             this.el.addClass("x-btn-focus");
26522         }
26523     },
26524     // private
26525     onBlur : function(e){
26526         this.el.removeClass("x-btn-focus");
26527     },
26528     // private
26529     onMouseDown : function(e){
26530         if(!this.disabled && e.button == 0){
26531             this.el.addClass("x-btn-click");
26532             Roo.get(document).on('mouseup', this.onMouseUp, this);
26533         }
26534     },
26535     // private
26536     onMouseUp : function(e){
26537         if(e.button == 0){
26538             this.el.removeClass("x-btn-click");
26539             Roo.get(document).un('mouseup', this.onMouseUp, this);
26540         }
26541     },
26542     // private
26543     onMenuShow : function(e){
26544         this.el.addClass("x-btn-menu-active");
26545     },
26546     // private
26547     onMenuHide : function(e){
26548         this.el.removeClass("x-btn-menu-active");
26549     }   
26550 });
26551
26552 // Private utility class used by Button
26553 Roo.ButtonToggleMgr = function(){
26554    var groups = {};
26555    
26556    function toggleGroup(btn, state){
26557        if(state){
26558            var g = groups[btn.toggleGroup];
26559            for(var i = 0, l = g.length; i < l; i++){
26560                if(g[i] != btn){
26561                    g[i].toggle(false);
26562                }
26563            }
26564        }
26565    }
26566    
26567    return {
26568        register : function(btn){
26569            if(!btn.toggleGroup){
26570                return;
26571            }
26572            var g = groups[btn.toggleGroup];
26573            if(!g){
26574                g = groups[btn.toggleGroup] = [];
26575            }
26576            g.push(btn);
26577            btn.on("toggle", toggleGroup);
26578        },
26579        
26580        unregister : function(btn){
26581            if(!btn.toggleGroup){
26582                return;
26583            }
26584            var g = groups[btn.toggleGroup];
26585            if(g){
26586                g.remove(btn);
26587                btn.un("toggle", toggleGroup);
26588            }
26589        }
26590    };
26591 }();/*
26592  * Based on:
26593  * Ext JS Library 1.1.1
26594  * Copyright(c) 2006-2007, Ext JS, LLC.
26595  *
26596  * Originally Released Under LGPL - original licence link has changed is not relivant.
26597  *
26598  * Fork - LGPL
26599  * <script type="text/javascript">
26600  */
26601  
26602 /**
26603  * @class Roo.SplitButton
26604  * @extends Roo.Button
26605  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26606  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26607  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26608  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26609  * @cfg {String} arrowTooltip The title attribute of the arrow
26610  * @constructor
26611  * Create a new menu button
26612  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26613  * @param {Object} config The config object
26614  */
26615 Roo.SplitButton = function(renderTo, config){
26616     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26617     /**
26618      * @event arrowclick
26619      * Fires when this button's arrow is clicked
26620      * @param {SplitButton} this
26621      * @param {EventObject} e The click event
26622      */
26623     this.addEvents({"arrowclick":true});
26624 };
26625
26626 Roo.extend(Roo.SplitButton, Roo.Button, {
26627     render : function(renderTo){
26628         // this is one sweet looking template!
26629         var tpl = new Roo.Template(
26630             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26631             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26632             '<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>',
26633             "</tbody></table></td><td>",
26634             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26635             '<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>',
26636             "</tbody></table></td></tr></table>"
26637         );
26638         var btn = tpl.append(renderTo, [this.text, this.type], true);
26639         var btnEl = btn.child("button");
26640         if(this.cls){
26641             btn.addClass(this.cls);
26642         }
26643         if(this.icon){
26644             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26645         }
26646         if(this.iconCls){
26647             btnEl.addClass(this.iconCls);
26648             if(!this.cls){
26649                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26650             }
26651         }
26652         this.el = btn;
26653         if(this.handleMouseEvents){
26654             btn.on("mouseover", this.onMouseOver, this);
26655             btn.on("mouseout", this.onMouseOut, this);
26656             btn.on("mousedown", this.onMouseDown, this);
26657             btn.on("mouseup", this.onMouseUp, this);
26658         }
26659         btn.on(this.clickEvent, this.onClick, this);
26660         if(this.tooltip){
26661             if(typeof this.tooltip == 'object'){
26662                 Roo.QuickTips.tips(Roo.apply({
26663                       target: btnEl.id
26664                 }, this.tooltip));
26665             } else {
26666                 btnEl.dom[this.tooltipType] = this.tooltip;
26667             }
26668         }
26669         if(this.arrowTooltip){
26670             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26671         }
26672         if(this.hidden){
26673             this.hide();
26674         }
26675         if(this.disabled){
26676             this.disable();
26677         }
26678         if(this.pressed){
26679             this.el.addClass("x-btn-pressed");
26680         }
26681         if(Roo.isIE && !Roo.isIE7){
26682             this.autoWidth.defer(1, this);
26683         }else{
26684             this.autoWidth();
26685         }
26686         if(this.menu){
26687             this.menu.on("show", this.onMenuShow, this);
26688             this.menu.on("hide", this.onMenuHide, this);
26689         }
26690         this.fireEvent('render', this);
26691     },
26692
26693     // private
26694     autoWidth : function(){
26695         if(this.el){
26696             var tbl = this.el.child("table:first");
26697             var tbl2 = this.el.child("table:last");
26698             this.el.setWidth("auto");
26699             tbl.setWidth("auto");
26700             if(Roo.isIE7 && Roo.isStrict){
26701                 var ib = this.el.child('button:first');
26702                 if(ib && ib.getWidth() > 20){
26703                     ib.clip();
26704                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26705                 }
26706             }
26707             if(this.minWidth){
26708                 if(this.hidden){
26709                     this.el.beginMeasure();
26710                 }
26711                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26712                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26713                 }
26714                 if(this.hidden){
26715                     this.el.endMeasure();
26716                 }
26717             }
26718             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26719         } 
26720     },
26721     /**
26722      * Sets this button's click handler
26723      * @param {Function} handler The function to call when the button is clicked
26724      * @param {Object} scope (optional) Scope for the function passed above
26725      */
26726     setHandler : function(handler, scope){
26727         this.handler = handler;
26728         this.scope = scope;  
26729     },
26730     
26731     /**
26732      * Sets this button's arrow click handler
26733      * @param {Function} handler The function to call when the arrow is clicked
26734      * @param {Object} scope (optional) Scope for the function passed above
26735      */
26736     setArrowHandler : function(handler, scope){
26737         this.arrowHandler = handler;
26738         this.scope = scope;  
26739     },
26740     
26741     /**
26742      * Focus the button
26743      */
26744     focus : function(){
26745         if(this.el){
26746             this.el.child("button:first").focus();
26747         }
26748     },
26749
26750     // private
26751     onClick : function(e){
26752         e.preventDefault();
26753         if(!this.disabled){
26754             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26755                 if(this.menu && !this.menu.isVisible()){
26756                     this.menu.show(this.el, this.menuAlign);
26757                 }
26758                 this.fireEvent("arrowclick", this, e);
26759                 if(this.arrowHandler){
26760                     this.arrowHandler.call(this.scope || this, this, e);
26761                 }
26762             }else{
26763                 this.fireEvent("click", this, e);
26764                 if(this.handler){
26765                     this.handler.call(this.scope || this, this, e);
26766                 }
26767             }
26768         }
26769     },
26770     // private
26771     onMouseDown : function(e){
26772         if(!this.disabled){
26773             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26774         }
26775     },
26776     // private
26777     onMouseUp : function(e){
26778         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26779     }   
26780 });
26781
26782
26783 // backwards compat
26784 Roo.MenuButton = Roo.SplitButton;/*
26785  * Based on:
26786  * Ext JS Library 1.1.1
26787  * Copyright(c) 2006-2007, Ext JS, LLC.
26788  *
26789  * Originally Released Under LGPL - original licence link has changed is not relivant.
26790  *
26791  * Fork - LGPL
26792  * <script type="text/javascript">
26793  */
26794
26795 /**
26796  * @class Roo.Toolbar
26797  * Basic Toolbar class.
26798  * @constructor
26799  * Creates a new Toolbar
26800  * @param {Object} container The config object
26801  */ 
26802 Roo.Toolbar = function(container, buttons, config)
26803 {
26804     /// old consturctor format still supported..
26805     if(container instanceof Array){ // omit the container for later rendering
26806         buttons = container;
26807         config = buttons;
26808         container = null;
26809     }
26810     if (typeof(container) == 'object' && container.xtype) {
26811         config = container;
26812         container = config.container;
26813         buttons = config.buttons || []; // not really - use items!!
26814     }
26815     var xitems = [];
26816     if (config && config.items) {
26817         xitems = config.items;
26818         delete config.items;
26819     }
26820     Roo.apply(this, config);
26821     this.buttons = buttons;
26822     
26823     if(container){
26824         this.render(container);
26825     }
26826     this.xitems = xitems;
26827     Roo.each(xitems, function(b) {
26828         this.add(b);
26829     }, this);
26830     
26831 };
26832
26833 Roo.Toolbar.prototype = {
26834     /**
26835      * @cfg {Array} items
26836      * array of button configs or elements to add (will be converted to a MixedCollection)
26837      */
26838     
26839     /**
26840      * @cfg {String/HTMLElement/Element} container
26841      * The id or element that will contain the toolbar
26842      */
26843     // private
26844     render : function(ct){
26845         this.el = Roo.get(ct);
26846         if(this.cls){
26847             this.el.addClass(this.cls);
26848         }
26849         // using a table allows for vertical alignment
26850         // 100% width is needed by Safari...
26851         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26852         this.tr = this.el.child("tr", true);
26853         var autoId = 0;
26854         this.items = new Roo.util.MixedCollection(false, function(o){
26855             return o.id || ("item" + (++autoId));
26856         });
26857         if(this.buttons){
26858             this.add.apply(this, this.buttons);
26859             delete this.buttons;
26860         }
26861     },
26862
26863     /**
26864      * Adds element(s) to the toolbar -- this function takes a variable number of 
26865      * arguments of mixed type and adds them to the toolbar.
26866      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26867      * <ul>
26868      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26869      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26870      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26871      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26872      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26873      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26874      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26875      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26876      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26877      * </ul>
26878      * @param {Mixed} arg2
26879      * @param {Mixed} etc.
26880      */
26881     add : function(){
26882         var a = arguments, l = a.length;
26883         for(var i = 0; i < l; i++){
26884             this._add(a[i]);
26885         }
26886     },
26887     // private..
26888     _add : function(el) {
26889         
26890         if (el.xtype) {
26891             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26892         }
26893         
26894         if (el.applyTo){ // some kind of form field
26895             return this.addField(el);
26896         } 
26897         if (el.render){ // some kind of Toolbar.Item
26898             return this.addItem(el);
26899         }
26900         if (typeof el == "string"){ // string
26901             if(el == "separator" || el == "-"){
26902                 return this.addSeparator();
26903             }
26904             if (el == " "){
26905                 return this.addSpacer();
26906             }
26907             if(el == "->"){
26908                 return this.addFill();
26909             }
26910             return this.addText(el);
26911             
26912         }
26913         if(el.tagName){ // element
26914             return this.addElement(el);
26915         }
26916         if(typeof el == "object"){ // must be button config?
26917             return this.addButton(el);
26918         }
26919         // and now what?!?!
26920         return false;
26921         
26922     },
26923     
26924     /**
26925      * Add an Xtype element
26926      * @param {Object} xtype Xtype Object
26927      * @return {Object} created Object
26928      */
26929     addxtype : function(e){
26930         return this.add(e);  
26931     },
26932     
26933     /**
26934      * Returns the Element for this toolbar.
26935      * @return {Roo.Element}
26936      */
26937     getEl : function(){
26938         return this.el;  
26939     },
26940     
26941     /**
26942      * Adds a separator
26943      * @return {Roo.Toolbar.Item} The separator item
26944      */
26945     addSeparator : function(){
26946         return this.addItem(new Roo.Toolbar.Separator());
26947     },
26948
26949     /**
26950      * Adds a spacer element
26951      * @return {Roo.Toolbar.Spacer} The spacer item
26952      */
26953     addSpacer : function(){
26954         return this.addItem(new Roo.Toolbar.Spacer());
26955     },
26956
26957     /**
26958      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26959      * @return {Roo.Toolbar.Fill} The fill item
26960      */
26961     addFill : function(){
26962         return this.addItem(new Roo.Toolbar.Fill());
26963     },
26964
26965     /**
26966      * Adds any standard HTML element to the toolbar
26967      * @param {String/HTMLElement/Element} el The element or id of the element to add
26968      * @return {Roo.Toolbar.Item} The element's item
26969      */
26970     addElement : function(el){
26971         return this.addItem(new Roo.Toolbar.Item(el));
26972     },
26973     /**
26974      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26975      * @type Roo.util.MixedCollection  
26976      */
26977     items : false,
26978      
26979     /**
26980      * Adds any Toolbar.Item or subclass
26981      * @param {Roo.Toolbar.Item} item
26982      * @return {Roo.Toolbar.Item} The item
26983      */
26984     addItem : function(item){
26985         var td = this.nextBlock();
26986         item.render(td);
26987         this.items.add(item);
26988         return item;
26989     },
26990     
26991     /**
26992      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26993      * @param {Object/Array} config A button config or array of configs
26994      * @return {Roo.Toolbar.Button/Array}
26995      */
26996     addButton : function(config){
26997         if(config instanceof Array){
26998             var buttons = [];
26999             for(var i = 0, len = config.length; i < len; i++) {
27000                 buttons.push(this.addButton(config[i]));
27001             }
27002             return buttons;
27003         }
27004         var b = config;
27005         if(!(config instanceof Roo.Toolbar.Button)){
27006             b = config.split ?
27007                 new Roo.Toolbar.SplitButton(config) :
27008                 new Roo.Toolbar.Button(config);
27009         }
27010         var td = this.nextBlock();
27011         b.render(td);
27012         this.items.add(b);
27013         return b;
27014     },
27015     
27016     /**
27017      * Adds text to the toolbar
27018      * @param {String} text The text to add
27019      * @return {Roo.Toolbar.Item} The element's item
27020      */
27021     addText : function(text){
27022         return this.addItem(new Roo.Toolbar.TextItem(text));
27023     },
27024     
27025     /**
27026      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27027      * @param {Number} index The index where the item is to be inserted
27028      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27029      * @return {Roo.Toolbar.Button/Item}
27030      */
27031     insertButton : function(index, item){
27032         if(item instanceof Array){
27033             var buttons = [];
27034             for(var i = 0, len = item.length; i < len; i++) {
27035                buttons.push(this.insertButton(index + i, item[i]));
27036             }
27037             return buttons;
27038         }
27039         if (!(item instanceof Roo.Toolbar.Button)){
27040            item = new Roo.Toolbar.Button(item);
27041         }
27042         var td = document.createElement("td");
27043         this.tr.insertBefore(td, this.tr.childNodes[index]);
27044         item.render(td);
27045         this.items.insert(index, item);
27046         return item;
27047     },
27048     
27049     /**
27050      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27051      * @param {Object} config
27052      * @return {Roo.Toolbar.Item} The element's item
27053      */
27054     addDom : function(config, returnEl){
27055         var td = this.nextBlock();
27056         Roo.DomHelper.overwrite(td, config);
27057         var ti = new Roo.Toolbar.Item(td.firstChild);
27058         ti.render(td);
27059         this.items.add(ti);
27060         return ti;
27061     },
27062
27063     /**
27064      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27065      * @type Roo.util.MixedCollection  
27066      */
27067     fields : false,
27068     
27069     /**
27070      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27071      * Note: the field should not have been rendered yet. For a field that has already been
27072      * rendered, use {@link #addElement}.
27073      * @param {Roo.form.Field} field
27074      * @return {Roo.ToolbarItem}
27075      */
27076      
27077       
27078     addField : function(field) {
27079         if (!this.fields) {
27080             var autoId = 0;
27081             this.fields = new Roo.util.MixedCollection(false, function(o){
27082                 return o.id || ("item" + (++autoId));
27083             });
27084
27085         }
27086         
27087         var td = this.nextBlock();
27088         field.render(td);
27089         var ti = new Roo.Toolbar.Item(td.firstChild);
27090         ti.render(td);
27091         this.items.add(ti);
27092         this.fields.add(field);
27093         return ti;
27094     },
27095     /**
27096      * Hide the toolbar
27097      * @method hide
27098      */
27099      
27100       
27101     hide : function()
27102     {
27103         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27104         this.el.child('div').hide();
27105     },
27106     /**
27107      * Show the toolbar
27108      * @method show
27109      */
27110     show : function()
27111     {
27112         this.el.child('div').show();
27113     },
27114       
27115     // private
27116     nextBlock : function(){
27117         var td = document.createElement("td");
27118         this.tr.appendChild(td);
27119         return td;
27120     },
27121
27122     // private
27123     destroy : function(){
27124         if(this.items){ // rendered?
27125             Roo.destroy.apply(Roo, this.items.items);
27126         }
27127         if(this.fields){ // rendered?
27128             Roo.destroy.apply(Roo, this.fields.items);
27129         }
27130         Roo.Element.uncache(this.el, this.tr);
27131     }
27132 };
27133
27134 /**
27135  * @class Roo.Toolbar.Item
27136  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27137  * @constructor
27138  * Creates a new Item
27139  * @param {HTMLElement} el 
27140  */
27141 Roo.Toolbar.Item = function(el){
27142     this.el = Roo.getDom(el);
27143     this.id = Roo.id(this.el);
27144     this.hidden = false;
27145 };
27146
27147 Roo.Toolbar.Item.prototype = {
27148     
27149     /**
27150      * Get this item's HTML Element
27151      * @return {HTMLElement}
27152      */
27153     getEl : function(){
27154        return this.el;  
27155     },
27156
27157     // private
27158     render : function(td){
27159         this.td = td;
27160         td.appendChild(this.el);
27161     },
27162     
27163     /**
27164      * Removes and destroys this item.
27165      */
27166     destroy : function(){
27167         this.td.parentNode.removeChild(this.td);
27168     },
27169     
27170     /**
27171      * Shows this item.
27172      */
27173     show: function(){
27174         this.hidden = false;
27175         this.td.style.display = "";
27176     },
27177     
27178     /**
27179      * Hides this item.
27180      */
27181     hide: function(){
27182         this.hidden = true;
27183         this.td.style.display = "none";
27184     },
27185     
27186     /**
27187      * Convenience function for boolean show/hide.
27188      * @param {Boolean} visible true to show/false to hide
27189      */
27190     setVisible: function(visible){
27191         if(visible) {
27192             this.show();
27193         }else{
27194             this.hide();
27195         }
27196     },
27197     
27198     /**
27199      * Try to focus this item.
27200      */
27201     focus : function(){
27202         Roo.fly(this.el).focus();
27203     },
27204     
27205     /**
27206      * Disables this item.
27207      */
27208     disable : function(){
27209         Roo.fly(this.td).addClass("x-item-disabled");
27210         this.disabled = true;
27211         this.el.disabled = true;
27212     },
27213     
27214     /**
27215      * Enables this item.
27216      */
27217     enable : function(){
27218         Roo.fly(this.td).removeClass("x-item-disabled");
27219         this.disabled = false;
27220         this.el.disabled = false;
27221     }
27222 };
27223
27224
27225 /**
27226  * @class Roo.Toolbar.Separator
27227  * @extends Roo.Toolbar.Item
27228  * A simple toolbar separator class
27229  * @constructor
27230  * Creates a new Separator
27231  */
27232 Roo.Toolbar.Separator = function(){
27233     var s = document.createElement("span");
27234     s.className = "ytb-sep";
27235     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27236 };
27237 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27238     enable:Roo.emptyFn,
27239     disable:Roo.emptyFn,
27240     focus:Roo.emptyFn
27241 });
27242
27243 /**
27244  * @class Roo.Toolbar.Spacer
27245  * @extends Roo.Toolbar.Item
27246  * A simple element that adds extra horizontal space to a toolbar.
27247  * @constructor
27248  * Creates a new Spacer
27249  */
27250 Roo.Toolbar.Spacer = function(){
27251     var s = document.createElement("div");
27252     s.className = "ytb-spacer";
27253     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27254 };
27255 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27256     enable:Roo.emptyFn,
27257     disable:Roo.emptyFn,
27258     focus:Roo.emptyFn
27259 });
27260
27261 /**
27262  * @class Roo.Toolbar.Fill
27263  * @extends Roo.Toolbar.Spacer
27264  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27265  * @constructor
27266  * Creates a new Spacer
27267  */
27268 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27269     // private
27270     render : function(td){
27271         td.style.width = '100%';
27272         Roo.Toolbar.Fill.superclass.render.call(this, td);
27273     }
27274 });
27275
27276 /**
27277  * @class Roo.Toolbar.TextItem
27278  * @extends Roo.Toolbar.Item
27279  * A simple class that renders text directly into a toolbar.
27280  * @constructor
27281  * Creates a new TextItem
27282  * @param {String} text
27283  */
27284 Roo.Toolbar.TextItem = function(text){
27285     if (typeof(text) == 'object') {
27286         text = text.text;
27287     }
27288     var s = document.createElement("span");
27289     s.className = "ytb-text";
27290     s.innerHTML = text;
27291     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27292 };
27293 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27294     enable:Roo.emptyFn,
27295     disable:Roo.emptyFn,
27296     focus:Roo.emptyFn
27297 });
27298
27299 /**
27300  * @class Roo.Toolbar.Button
27301  * @extends Roo.Button
27302  * A button that renders into a toolbar.
27303  * @constructor
27304  * Creates a new Button
27305  * @param {Object} config A standard {@link Roo.Button} config object
27306  */
27307 Roo.Toolbar.Button = function(config){
27308     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27309 };
27310 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27311     render : function(td){
27312         this.td = td;
27313         Roo.Toolbar.Button.superclass.render.call(this, td);
27314     },
27315     
27316     /**
27317      * Removes and destroys this button
27318      */
27319     destroy : function(){
27320         Roo.Toolbar.Button.superclass.destroy.call(this);
27321         this.td.parentNode.removeChild(this.td);
27322     },
27323     
27324     /**
27325      * Shows this button
27326      */
27327     show: function(){
27328         this.hidden = false;
27329         this.td.style.display = "";
27330     },
27331     
27332     /**
27333      * Hides this button
27334      */
27335     hide: function(){
27336         this.hidden = true;
27337         this.td.style.display = "none";
27338     },
27339
27340     /**
27341      * Disables this item
27342      */
27343     disable : function(){
27344         Roo.fly(this.td).addClass("x-item-disabled");
27345         this.disabled = true;
27346     },
27347
27348     /**
27349      * Enables this item
27350      */
27351     enable : function(){
27352         Roo.fly(this.td).removeClass("x-item-disabled");
27353         this.disabled = false;
27354     }
27355 });
27356 // backwards compat
27357 Roo.ToolbarButton = Roo.Toolbar.Button;
27358
27359 /**
27360  * @class Roo.Toolbar.SplitButton
27361  * @extends Roo.SplitButton
27362  * A menu button that renders into a toolbar.
27363  * @constructor
27364  * Creates a new SplitButton
27365  * @param {Object} config A standard {@link Roo.SplitButton} config object
27366  */
27367 Roo.Toolbar.SplitButton = function(config){
27368     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27369 };
27370 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27371     render : function(td){
27372         this.td = td;
27373         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27374     },
27375     
27376     /**
27377      * Removes and destroys this button
27378      */
27379     destroy : function(){
27380         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27381         this.td.parentNode.removeChild(this.td);
27382     },
27383     
27384     /**
27385      * Shows this button
27386      */
27387     show: function(){
27388         this.hidden = false;
27389         this.td.style.display = "";
27390     },
27391     
27392     /**
27393      * Hides this button
27394      */
27395     hide: function(){
27396         this.hidden = true;
27397         this.td.style.display = "none";
27398     }
27399 });
27400
27401 // backwards compat
27402 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27403  * Based on:
27404  * Ext JS Library 1.1.1
27405  * Copyright(c) 2006-2007, Ext JS, LLC.
27406  *
27407  * Originally Released Under LGPL - original licence link has changed is not relivant.
27408  *
27409  * Fork - LGPL
27410  * <script type="text/javascript">
27411  */
27412  
27413 /**
27414  * @class Roo.PagingToolbar
27415  * @extends Roo.Toolbar
27416  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27417  * @constructor
27418  * Create a new PagingToolbar
27419  * @param {Object} config The config object
27420  */
27421 Roo.PagingToolbar = function(el, ds, config)
27422 {
27423     // old args format still supported... - xtype is prefered..
27424     if (typeof(el) == 'object' && el.xtype) {
27425         // created from xtype...
27426         config = el;
27427         ds = el.dataSource;
27428         el = config.container;
27429     }
27430     var items = [];
27431     if (config.items) {
27432         items = config.items;
27433         config.items = [];
27434     }
27435     
27436     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27437     this.ds = ds;
27438     this.cursor = 0;
27439     this.renderButtons(this.el);
27440     this.bind(ds);
27441     
27442     // supprot items array.
27443    
27444     Roo.each(items, function(e) {
27445         this.add(Roo.factory(e));
27446     },this);
27447     
27448 };
27449
27450 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27451     /**
27452      * @cfg {Roo.data.Store} dataSource
27453      * The underlying data store providing the paged data
27454      */
27455     /**
27456      * @cfg {String/HTMLElement/Element} container
27457      * container The id or element that will contain the toolbar
27458      */
27459     /**
27460      * @cfg {Boolean} displayInfo
27461      * True to display the displayMsg (defaults to false)
27462      */
27463     /**
27464      * @cfg {Number} pageSize
27465      * The number of records to display per page (defaults to 20)
27466      */
27467     pageSize: 20,
27468     /**
27469      * @cfg {String} displayMsg
27470      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27471      */
27472     displayMsg : 'Displaying {0} - {1} of {2}',
27473     /**
27474      * @cfg {String} emptyMsg
27475      * The message to display when no records are found (defaults to "No data to display")
27476      */
27477     emptyMsg : 'No data to display',
27478     /**
27479      * Customizable piece of the default paging text (defaults to "Page")
27480      * @type String
27481      */
27482     beforePageText : "Page",
27483     /**
27484      * Customizable piece of the default paging text (defaults to "of %0")
27485      * @type String
27486      */
27487     afterPageText : "of {0}",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "First Page")
27490      * @type String
27491      */
27492     firstText : "First Page",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "Previous Page")
27495      * @type String
27496      */
27497     prevText : "Previous Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Next Page")
27500      * @type String
27501      */
27502     nextText : "Next Page",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "Last Page")
27505      * @type String
27506      */
27507     lastText : "Last Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Refresh")
27510      * @type String
27511      */
27512     refreshText : "Refresh",
27513
27514     // private
27515     renderButtons : function(el){
27516         Roo.PagingToolbar.superclass.render.call(this, el);
27517         this.first = this.addButton({
27518             tooltip: this.firstText,
27519             cls: "x-btn-icon x-grid-page-first",
27520             disabled: true,
27521             handler: this.onClick.createDelegate(this, ["first"])
27522         });
27523         this.prev = this.addButton({
27524             tooltip: this.prevText,
27525             cls: "x-btn-icon x-grid-page-prev",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["prev"])
27528         });
27529         //this.addSeparator();
27530         this.add(this.beforePageText);
27531         this.field = Roo.get(this.addDom({
27532            tag: "input",
27533            type: "text",
27534            size: "3",
27535            value: "1",
27536            cls: "x-grid-page-number"
27537         }).el);
27538         this.field.on("keydown", this.onPagingKeydown, this);
27539         this.field.on("focus", function(){this.dom.select();});
27540         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27541         this.field.setHeight(18);
27542         //this.addSeparator();
27543         this.next = this.addButton({
27544             tooltip: this.nextText,
27545             cls: "x-btn-icon x-grid-page-next",
27546             disabled: true,
27547             handler: this.onClick.createDelegate(this, ["next"])
27548         });
27549         this.last = this.addButton({
27550             tooltip: this.lastText,
27551             cls: "x-btn-icon x-grid-page-last",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["last"])
27554         });
27555         //this.addSeparator();
27556         this.loading = this.addButton({
27557             tooltip: this.refreshText,
27558             cls: "x-btn-icon x-grid-loading",
27559             handler: this.onClick.createDelegate(this, ["refresh"])
27560         });
27561
27562         if(this.displayInfo){
27563             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27564         }
27565     },
27566
27567     // private
27568     updateInfo : function(){
27569         if(this.displayEl){
27570             var count = this.ds.getCount();
27571             var msg = count == 0 ?
27572                 this.emptyMsg :
27573                 String.format(
27574                     this.displayMsg,
27575                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27576                 );
27577             this.displayEl.update(msg);
27578         }
27579     },
27580
27581     // private
27582     onLoad : function(ds, r, o){
27583        this.cursor = o.params ? o.params.start : 0;
27584        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27585
27586        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27587        this.field.dom.value = ap;
27588        this.first.setDisabled(ap == 1);
27589        this.prev.setDisabled(ap == 1);
27590        this.next.setDisabled(ap == ps);
27591        this.last.setDisabled(ap == ps);
27592        this.loading.enable();
27593        this.updateInfo();
27594     },
27595
27596     // private
27597     getPageData : function(){
27598         var total = this.ds.getTotalCount();
27599         return {
27600             total : total,
27601             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27602             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27603         };
27604     },
27605
27606     // private
27607     onLoadError : function(){
27608         this.loading.enable();
27609     },
27610
27611     // private
27612     onPagingKeydown : function(e){
27613         var k = e.getKey();
27614         var d = this.getPageData();
27615         if(k == e.RETURN){
27616             var v = this.field.dom.value, pageNum;
27617             if(!v || isNaN(pageNum = parseInt(v, 10))){
27618                 this.field.dom.value = d.activePage;
27619                 return;
27620             }
27621             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623             e.stopEvent();
27624         }
27625         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))
27626         {
27627           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27628           this.field.dom.value = pageNum;
27629           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27630           e.stopEvent();
27631         }
27632         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27633         {
27634           var v = this.field.dom.value, pageNum; 
27635           var increment = (e.shiftKey) ? 10 : 1;
27636           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27637             increment *= -1;
27638           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27639             this.field.dom.value = d.activePage;
27640             return;
27641           }
27642           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27643           {
27644             this.field.dom.value = parseInt(v, 10) + increment;
27645             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27646             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27647           }
27648           e.stopEvent();
27649         }
27650     },
27651
27652     // private
27653     beforeLoad : function(){
27654         if(this.loading){
27655             this.loading.disable();
27656         }
27657     },
27658
27659     // private
27660     onClick : function(which){
27661         var ds = this.ds;
27662         switch(which){
27663             case "first":
27664                 ds.load({params:{start: 0, limit: this.pageSize}});
27665             break;
27666             case "prev":
27667                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27668             break;
27669             case "next":
27670                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27671             break;
27672             case "last":
27673                 var total = ds.getTotalCount();
27674                 var extra = total % this.pageSize;
27675                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27676                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27677             break;
27678             case "refresh":
27679                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27680             break;
27681         }
27682     },
27683
27684     /**
27685      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27686      * @param {Roo.data.Store} store The data store to unbind
27687      */
27688     unbind : function(ds){
27689         ds.un("beforeload", this.beforeLoad, this);
27690         ds.un("load", this.onLoad, this);
27691         ds.un("loadexception", this.onLoadError, this);
27692         ds.un("remove", this.updateInfo, this);
27693         ds.un("add", this.updateInfo, this);
27694         this.ds = undefined;
27695     },
27696
27697     /**
27698      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27699      * @param {Roo.data.Store} store The data store to bind
27700      */
27701     bind : function(ds){
27702         ds.on("beforeload", this.beforeLoad, this);
27703         ds.on("load", this.onLoad, this);
27704         ds.on("loadexception", this.onLoadError, this);
27705         ds.on("remove", this.updateInfo, this);
27706         ds.on("add", this.updateInfo, this);
27707         this.ds = ds;
27708     }
27709 });/*
27710  * Based on:
27711  * Ext JS Library 1.1.1
27712  * Copyright(c) 2006-2007, Ext JS, LLC.
27713  *
27714  * Originally Released Under LGPL - original licence link has changed is not relivant.
27715  *
27716  * Fork - LGPL
27717  * <script type="text/javascript">
27718  */
27719
27720 /**
27721  * @class Roo.Resizable
27722  * @extends Roo.util.Observable
27723  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27724  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27725  * 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
27726  * the element will be wrapped for you automatically.</p>
27727  * <p>Here is the list of valid resize handles:</p>
27728  * <pre>
27729 Value   Description
27730 ------  -------------------
27731  'n'     north
27732  's'     south
27733  'e'     east
27734  'w'     west
27735  'nw'    northwest
27736  'sw'    southwest
27737  'se'    southeast
27738  'ne'    northeast
27739  'hd'    horizontal drag
27740  'all'   all
27741 </pre>
27742  * <p>Here's an example showing the creation of a typical Resizable:</p>
27743  * <pre><code>
27744 var resizer = new Roo.Resizable("element-id", {
27745     handles: 'all',
27746     minWidth: 200,
27747     minHeight: 100,
27748     maxWidth: 500,
27749     maxHeight: 400,
27750     pinned: true
27751 });
27752 resizer.on("resize", myHandler);
27753 </code></pre>
27754  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27755  * resizer.east.setDisplayed(false);</p>
27756  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27757  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27758  * resize operation's new size (defaults to [0, 0])
27759  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27760  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27761  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27762  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27763  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27764  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27765  * @cfg {Number} width The width of the element in pixels (defaults to null)
27766  * @cfg {Number} height The height of the element in pixels (defaults to null)
27767  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27768  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27769  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27770  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27771  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27772  * in favor of the handles config option (defaults to false)
27773  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27774  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27775  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27776  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27777  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27778  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27779  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27780  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27781  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27782  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27783  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27784  * @constructor
27785  * Create a new resizable component
27786  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27787  * @param {Object} config configuration options
27788   */
27789 Roo.Resizable = function(el, config)
27790 {
27791     this.el = Roo.get(el);
27792
27793     if(config && config.wrap){
27794         config.resizeChild = this.el;
27795         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27796         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27797         this.el.setStyle("overflow", "hidden");
27798         this.el.setPositioning(config.resizeChild.getPositioning());
27799         config.resizeChild.clearPositioning();
27800         if(!config.width || !config.height){
27801             var csize = config.resizeChild.getSize();
27802             this.el.setSize(csize.width, csize.height);
27803         }
27804         if(config.pinned && !config.adjustments){
27805             config.adjustments = "auto";
27806         }
27807     }
27808
27809     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27810     this.proxy.unselectable();
27811     this.proxy.enableDisplayMode('block');
27812
27813     Roo.apply(this, config);
27814
27815     if(this.pinned){
27816         this.disableTrackOver = true;
27817         this.el.addClass("x-resizable-pinned");
27818     }
27819     // if the element isn't positioned, make it relative
27820     var position = this.el.getStyle("position");
27821     if(position != "absolute" && position != "fixed"){
27822         this.el.setStyle("position", "relative");
27823     }
27824     if(!this.handles){ // no handles passed, must be legacy style
27825         this.handles = 's,e,se';
27826         if(this.multiDirectional){
27827             this.handles += ',n,w';
27828         }
27829     }
27830     if(this.handles == "all"){
27831         this.handles = "n s e w ne nw se sw";
27832     }
27833     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27834     var ps = Roo.Resizable.positions;
27835     for(var i = 0, len = hs.length; i < len; i++){
27836         if(hs[i] && ps[hs[i]]){
27837             var pos = ps[hs[i]];
27838             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27839         }
27840     }
27841     // legacy
27842     this.corner = this.southeast;
27843     
27844     // updateBox = the box can move..
27845     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27846         this.updateBox = true;
27847     }
27848
27849     this.activeHandle = null;
27850
27851     if(this.resizeChild){
27852         if(typeof this.resizeChild == "boolean"){
27853             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27854         }else{
27855             this.resizeChild = Roo.get(this.resizeChild, true);
27856         }
27857     }
27858     
27859     if(this.adjustments == "auto"){
27860         var rc = this.resizeChild;
27861         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27862         if(rc && (hw || hn)){
27863             rc.position("relative");
27864             rc.setLeft(hw ? hw.el.getWidth() : 0);
27865             rc.setTop(hn ? hn.el.getHeight() : 0);
27866         }
27867         this.adjustments = [
27868             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27869             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27870         ];
27871     }
27872
27873     if(this.draggable){
27874         this.dd = this.dynamic ?
27875             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27876         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27877     }
27878
27879     // public events
27880     this.addEvents({
27881         /**
27882          * @event beforeresize
27883          * Fired before resize is allowed. Set enabled to false to cancel resize.
27884          * @param {Roo.Resizable} this
27885          * @param {Roo.EventObject} e The mousedown event
27886          */
27887         "beforeresize" : true,
27888         /**
27889          * @event resize
27890          * Fired after a resize.
27891          * @param {Roo.Resizable} this
27892          * @param {Number} width The new width
27893          * @param {Number} height The new height
27894          * @param {Roo.EventObject} e The mouseup event
27895          */
27896         "resize" : true
27897     });
27898
27899     if(this.width !== null && this.height !== null){
27900         this.resizeTo(this.width, this.height);
27901     }else{
27902         this.updateChildSize();
27903     }
27904     if(Roo.isIE){
27905         this.el.dom.style.zoom = 1;
27906     }
27907     Roo.Resizable.superclass.constructor.call(this);
27908 };
27909
27910 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27911         resizeChild : false,
27912         adjustments : [0, 0],
27913         minWidth : 5,
27914         minHeight : 5,
27915         maxWidth : 10000,
27916         maxHeight : 10000,
27917         enabled : true,
27918         animate : false,
27919         duration : .35,
27920         dynamic : false,
27921         handles : false,
27922         multiDirectional : false,
27923         disableTrackOver : false,
27924         easing : 'easeOutStrong',
27925         widthIncrement : 0,
27926         heightIncrement : 0,
27927         pinned : false,
27928         width : null,
27929         height : null,
27930         preserveRatio : false,
27931         transparent: false,
27932         minX: 0,
27933         minY: 0,
27934         draggable: false,
27935
27936         /**
27937          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27938          */
27939         constrainTo: undefined,
27940         /**
27941          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27942          */
27943         resizeRegion: undefined,
27944
27945
27946     /**
27947      * Perform a manual resize
27948      * @param {Number} width
27949      * @param {Number} height
27950      */
27951     resizeTo : function(width, height){
27952         this.el.setSize(width, height);
27953         this.updateChildSize();
27954         this.fireEvent("resize", this, width, height, null);
27955     },
27956
27957     // private
27958     startSizing : function(e, handle){
27959         this.fireEvent("beforeresize", this, e);
27960         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27961
27962             if(!this.overlay){
27963                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27964                 this.overlay.unselectable();
27965                 this.overlay.enableDisplayMode("block");
27966                 this.overlay.on("mousemove", this.onMouseMove, this);
27967                 this.overlay.on("mouseup", this.onMouseUp, this);
27968             }
27969             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27970
27971             this.resizing = true;
27972             this.startBox = this.el.getBox();
27973             this.startPoint = e.getXY();
27974             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27975                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27976
27977             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27978             this.overlay.show();
27979
27980             if(this.constrainTo) {
27981                 var ct = Roo.get(this.constrainTo);
27982                 this.resizeRegion = ct.getRegion().adjust(
27983                     ct.getFrameWidth('t'),
27984                     ct.getFrameWidth('l'),
27985                     -ct.getFrameWidth('b'),
27986                     -ct.getFrameWidth('r')
27987                 );
27988             }
27989
27990             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27991             this.proxy.show();
27992             this.proxy.setBox(this.startBox);
27993             if(!this.dynamic){
27994                 this.proxy.setStyle('visibility', 'visible');
27995             }
27996         }
27997     },
27998
27999     // private
28000     onMouseDown : function(handle, e){
28001         if(this.enabled){
28002             e.stopEvent();
28003             this.activeHandle = handle;
28004             this.startSizing(e, handle);
28005         }
28006     },
28007
28008     // private
28009     onMouseUp : function(e){
28010         var size = this.resizeElement();
28011         this.resizing = false;
28012         this.handleOut();
28013         this.overlay.hide();
28014         this.proxy.hide();
28015         this.fireEvent("resize", this, size.width, size.height, e);
28016     },
28017
28018     // private
28019     updateChildSize : function(){
28020         if(this.resizeChild){
28021             var el = this.el;
28022             var child = this.resizeChild;
28023             var adj = this.adjustments;
28024             if(el.dom.offsetWidth){
28025                 var b = el.getSize(true);
28026                 child.setSize(b.width+adj[0], b.height+adj[1]);
28027             }
28028             // Second call here for IE
28029             // The first call enables instant resizing and
28030             // the second call corrects scroll bars if they
28031             // exist
28032             if(Roo.isIE){
28033                 setTimeout(function(){
28034                     if(el.dom.offsetWidth){
28035                         var b = el.getSize(true);
28036                         child.setSize(b.width+adj[0], b.height+adj[1]);
28037                     }
28038                 }, 10);
28039             }
28040         }
28041     },
28042
28043     // private
28044     snap : function(value, inc, min){
28045         if(!inc || !value) return value;
28046         var newValue = value;
28047         var m = value % inc;
28048         if(m > 0){
28049             if(m > (inc/2)){
28050                 newValue = value + (inc-m);
28051             }else{
28052                 newValue = value - m;
28053             }
28054         }
28055         return Math.max(min, newValue);
28056     },
28057
28058     // private
28059     resizeElement : function(){
28060         var box = this.proxy.getBox();
28061         if(this.updateBox){
28062             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28063         }else{
28064             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28065         }
28066         this.updateChildSize();
28067         if(!this.dynamic){
28068             this.proxy.hide();
28069         }
28070         return box;
28071     },
28072
28073     // private
28074     constrain : function(v, diff, m, mx){
28075         if(v - diff < m){
28076             diff = v - m;
28077         }else if(v - diff > mx){
28078             diff = mx - v;
28079         }
28080         return diff;
28081     },
28082
28083     // private
28084     onMouseMove : function(e){
28085         if(this.enabled){
28086             try{// try catch so if something goes wrong the user doesn't get hung
28087
28088             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28089                 return;
28090             }
28091
28092             //var curXY = this.startPoint;
28093             var curSize = this.curSize || this.startBox;
28094             var x = this.startBox.x, y = this.startBox.y;
28095             var ox = x, oy = y;
28096             var w = curSize.width, h = curSize.height;
28097             var ow = w, oh = h;
28098             var mw = this.minWidth, mh = this.minHeight;
28099             var mxw = this.maxWidth, mxh = this.maxHeight;
28100             var wi = this.widthIncrement;
28101             var hi = this.heightIncrement;
28102
28103             var eventXY = e.getXY();
28104             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28105             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28106
28107             var pos = this.activeHandle.position;
28108
28109             switch(pos){
28110                 case "east":
28111                     w += diffX;
28112                     w = Math.min(Math.max(mw, w), mxw);
28113                     break;
28114              
28115                 case "south":
28116                     h += diffY;
28117                     h = Math.min(Math.max(mh, h), mxh);
28118                     break;
28119                 case "southeast":
28120                     w += diffX;
28121                     h += diffY;
28122                     w = Math.min(Math.max(mw, w), mxw);
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "north":
28126                     diffY = this.constrain(h, diffY, mh, mxh);
28127                     y += diffY;
28128                     h -= diffY;
28129                     break;
28130                 case "hdrag":
28131                     
28132                     if (wi) {
28133                         var adiffX = Math.abs(diffX);
28134                         var sub = (adiffX % wi); // how much 
28135                         if (sub > (wi/2)) { // far enough to snap
28136                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28137                         } else {
28138                             // remove difference.. 
28139                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28140                         }
28141                     }
28142                     x += diffX;
28143                     x = Math.max(this.minX, x);
28144                     break;
28145                 case "west":
28146                     diffX = this.constrain(w, diffX, mw, mxw);
28147                     x += diffX;
28148                     w -= diffX;
28149                     break;
28150                 case "northeast":
28151                     w += diffX;
28152                     w = Math.min(Math.max(mw, w), mxw);
28153                     diffY = this.constrain(h, diffY, mh, mxh);
28154                     y += diffY;
28155                     h -= diffY;
28156                     break;
28157                 case "northwest":
28158                     diffX = this.constrain(w, diffX, mw, mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     x += diffX;
28163                     w -= diffX;
28164                     break;
28165                case "southwest":
28166                     diffX = this.constrain(w, diffX, mw, mxw);
28167                     h += diffY;
28168                     h = Math.min(Math.max(mh, h), mxh);
28169                     x += diffX;
28170                     w -= diffX;
28171                     break;
28172             }
28173
28174             var sw = this.snap(w, wi, mw);
28175             var sh = this.snap(h, hi, mh);
28176             if(sw != w || sh != h){
28177                 switch(pos){
28178                     case "northeast":
28179                         y -= sh - h;
28180                     break;
28181                     case "north":
28182                         y -= sh - h;
28183                         break;
28184                     case "southwest":
28185                         x -= sw - w;
28186                     break;
28187                     case "west":
28188                         x -= sw - w;
28189                         break;
28190                     case "northwest":
28191                         x -= sw - w;
28192                         y -= sh - h;
28193                     break;
28194                 }
28195                 w = sw;
28196                 h = sh;
28197             }
28198
28199             if(this.preserveRatio){
28200                 switch(pos){
28201                     case "southeast":
28202                     case "east":
28203                         h = oh * (w/ow);
28204                         h = Math.min(Math.max(mh, h), mxh);
28205                         w = ow * (h/oh);
28206                        break;
28207                     case "south":
28208                         w = ow * (h/oh);
28209                         w = Math.min(Math.max(mw, w), mxw);
28210                         h = oh * (w/ow);
28211                         break;
28212                     case "northeast":
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                     break;
28217                     case "north":
28218                         var tw = w;
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                         x += (tw - w) / 2;
28223                         break;
28224                     case "southwest":
28225                         h = oh * (w/ow);
28226                         h = Math.min(Math.max(mh, h), mxh);
28227                         var tw = w;
28228                         w = ow * (h/oh);
28229                         x += tw - w;
28230                         break;
28231                     case "west":
28232                         var th = h;
28233                         h = oh * (w/ow);
28234                         h = Math.min(Math.max(mh, h), mxh);
28235                         y += (th - h) / 2;
28236                         var tw = w;
28237                         w = ow * (h/oh);
28238                         x += tw - w;
28239                        break;
28240                     case "northwest":
28241                         var tw = w;
28242                         var th = h;
28243                         h = oh * (w/ow);
28244                         h = Math.min(Math.max(mh, h), mxh);
28245                         w = ow * (h/oh);
28246                         y += th - h;
28247                         x += tw - w;
28248                        break;
28249
28250                 }
28251             }
28252             if (pos == 'hdrag') {
28253                 w = ow;
28254             }
28255             this.proxy.setBounds(x, y, w, h);
28256             if(this.dynamic){
28257                 this.resizeElement();
28258             }
28259             }catch(e){}
28260         }
28261     },
28262
28263     // private
28264     handleOver : function(){
28265         if(this.enabled){
28266             this.el.addClass("x-resizable-over");
28267         }
28268     },
28269
28270     // private
28271     handleOut : function(){
28272         if(!this.resizing){
28273             this.el.removeClass("x-resizable-over");
28274         }
28275     },
28276
28277     /**
28278      * Returns the element this component is bound to.
28279      * @return {Roo.Element}
28280      */
28281     getEl : function(){
28282         return this.el;
28283     },
28284
28285     /**
28286      * Returns the resizeChild element (or null).
28287      * @return {Roo.Element}
28288      */
28289     getResizeChild : function(){
28290         return this.resizeChild;
28291     },
28292
28293     /**
28294      * Destroys this resizable. If the element was wrapped and
28295      * removeEl is not true then the element remains.
28296      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28297      */
28298     destroy : function(removeEl){
28299         this.proxy.remove();
28300         if(this.overlay){
28301             this.overlay.removeAllListeners();
28302             this.overlay.remove();
28303         }
28304         var ps = Roo.Resizable.positions;
28305         for(var k in ps){
28306             if(typeof ps[k] != "function" && this[ps[k]]){
28307                 var h = this[ps[k]];
28308                 h.el.removeAllListeners();
28309                 h.el.remove();
28310             }
28311         }
28312         if(removeEl){
28313             this.el.update("");
28314             this.el.remove();
28315         }
28316     }
28317 });
28318
28319 // private
28320 // hash to map config positions to true positions
28321 Roo.Resizable.positions = {
28322     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28323     hd: "hdrag"
28324 };
28325
28326 // private
28327 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28328     if(!this.tpl){
28329         // only initialize the template if resizable is used
28330         var tpl = Roo.DomHelper.createTemplate(
28331             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28332         );
28333         tpl.compile();
28334         Roo.Resizable.Handle.prototype.tpl = tpl;
28335     }
28336     this.position = pos;
28337     this.rz = rz;
28338     // show north drag fro topdra
28339     var handlepos = pos == 'hdrag' ? 'north' : pos;
28340     
28341     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28342     if (pos == 'hdrag') {
28343         this.el.setStyle('cursor', 'pointer');
28344     }
28345     this.el.unselectable();
28346     if(transparent){
28347         this.el.setOpacity(0);
28348     }
28349     this.el.on("mousedown", this.onMouseDown, this);
28350     if(!disableTrackOver){
28351         this.el.on("mouseover", this.onMouseOver, this);
28352         this.el.on("mouseout", this.onMouseOut, this);
28353     }
28354 };
28355
28356 // private
28357 Roo.Resizable.Handle.prototype = {
28358     afterResize : function(rz){
28359         // do nothing
28360     },
28361     // private
28362     onMouseDown : function(e){
28363         this.rz.onMouseDown(this, e);
28364     },
28365     // private
28366     onMouseOver : function(e){
28367         this.rz.handleOver(this, e);
28368     },
28369     // private
28370     onMouseOut : function(e){
28371         this.rz.handleOut(this, e);
28372     }
28373 };/*
28374  * Based on:
28375  * Ext JS Library 1.1.1
28376  * Copyright(c) 2006-2007, Ext JS, LLC.
28377  *
28378  * Originally Released Under LGPL - original licence link has changed is not relivant.
28379  *
28380  * Fork - LGPL
28381  * <script type="text/javascript">
28382  */
28383
28384 /**
28385  * @class Roo.Editor
28386  * @extends Roo.Component
28387  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28388  * @constructor
28389  * Create a new Editor
28390  * @param {Roo.form.Field} field The Field object (or descendant)
28391  * @param {Object} config The config object
28392  */
28393 Roo.Editor = function(field, config){
28394     Roo.Editor.superclass.constructor.call(this, config);
28395     this.field = field;
28396     this.addEvents({
28397         /**
28398              * @event beforestartedit
28399              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28400              * false from the handler of this event.
28401              * @param {Editor} this
28402              * @param {Roo.Element} boundEl The underlying element bound to this editor
28403              * @param {Mixed} value The field value being set
28404              */
28405         "beforestartedit" : true,
28406         /**
28407              * @event startedit
28408              * Fires when this editor is displayed
28409              * @param {Roo.Element} boundEl The underlying element bound to this editor
28410              * @param {Mixed} value The starting field value
28411              */
28412         "startedit" : true,
28413         /**
28414              * @event beforecomplete
28415              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28416              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28417              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28418              * event will not fire since no edit actually occurred.
28419              * @param {Editor} this
28420              * @param {Mixed} value The current field value
28421              * @param {Mixed} startValue The original field value
28422              */
28423         "beforecomplete" : true,
28424         /**
28425              * @event complete
28426              * Fires after editing is complete and any changed value has been written to the underlying field.
28427              * @param {Editor} this
28428              * @param {Mixed} value The current field value
28429              * @param {Mixed} startValue The original field value
28430              */
28431         "complete" : true,
28432         /**
28433          * @event specialkey
28434          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28435          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28436          * @param {Roo.form.Field} this
28437          * @param {Roo.EventObject} e The event object
28438          */
28439         "specialkey" : true
28440     });
28441 };
28442
28443 Roo.extend(Roo.Editor, Roo.Component, {
28444     /**
28445      * @cfg {Boolean/String} autosize
28446      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28447      * or "height" to adopt the height only (defaults to false)
28448      */
28449     /**
28450      * @cfg {Boolean} revertInvalid
28451      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28452      * validation fails (defaults to true)
28453      */
28454     /**
28455      * @cfg {Boolean} ignoreNoChange
28456      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28457      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28458      * will never be ignored.
28459      */
28460     /**
28461      * @cfg {Boolean} hideEl
28462      * False to keep the bound element visible while the editor is displayed (defaults to true)
28463      */
28464     /**
28465      * @cfg {Mixed} value
28466      * The data value of the underlying field (defaults to "")
28467      */
28468     value : "",
28469     /**
28470      * @cfg {String} alignment
28471      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28472      */
28473     alignment: "c-c?",
28474     /**
28475      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28476      * for bottom-right shadow (defaults to "frame")
28477      */
28478     shadow : "frame",
28479     /**
28480      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28481      */
28482     constrain : false,
28483     /**
28484      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28485      */
28486     completeOnEnter : false,
28487     /**
28488      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28489      */
28490     cancelOnEsc : false,
28491     /**
28492      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28493      */
28494     updateEl : false,
28495
28496     // private
28497     onRender : function(ct, position){
28498         this.el = new Roo.Layer({
28499             shadow: this.shadow,
28500             cls: "x-editor",
28501             parentEl : ct,
28502             shim : this.shim,
28503             shadowOffset:4,
28504             id: this.id,
28505             constrain: this.constrain
28506         });
28507         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28508         if(this.field.msgTarget != 'title'){
28509             this.field.msgTarget = 'qtip';
28510         }
28511         this.field.render(this.el);
28512         if(Roo.isGecko){
28513             this.field.el.dom.setAttribute('autocomplete', 'off');
28514         }
28515         this.field.on("specialkey", this.onSpecialKey, this);
28516         if(this.swallowKeys){
28517             this.field.el.swallowEvent(['keydown','keypress']);
28518         }
28519         this.field.show();
28520         this.field.on("blur", this.onBlur, this);
28521         if(this.field.grow){
28522             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28523         }
28524     },
28525
28526     onSpecialKey : function(field, e)
28527     {
28528         //Roo.log('editor onSpecialKey');
28529         if(this.completeOnEnter && e.getKey() == e.ENTER){
28530             e.stopEvent();
28531             this.completeEdit();
28532             return;
28533         }
28534         // do not fire special key otherwise it might hide close the editor...
28535         if(e.getKey() == e.ENTER){    
28536             return;
28537         }
28538         if(this.cancelOnEsc && e.getKey() == e.ESC){
28539             this.cancelEdit();
28540             return;
28541         } 
28542         this.fireEvent('specialkey', field, e);
28543     
28544     },
28545
28546     /**
28547      * Starts the editing process and shows the editor.
28548      * @param {String/HTMLElement/Element} el The element to edit
28549      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28550       * to the innerHTML of el.
28551      */
28552     startEdit : function(el, value){
28553         if(this.editing){
28554             this.completeEdit();
28555         }
28556         this.boundEl = Roo.get(el);
28557         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28558         if(!this.rendered){
28559             this.render(this.parentEl || document.body);
28560         }
28561         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28562             return;
28563         }
28564         this.startValue = v;
28565         this.field.setValue(v);
28566         if(this.autoSize){
28567             var sz = this.boundEl.getSize();
28568             switch(this.autoSize){
28569                 case "width":
28570                 this.setSize(sz.width,  "");
28571                 break;
28572                 case "height":
28573                 this.setSize("",  sz.height);
28574                 break;
28575                 default:
28576                 this.setSize(sz.width,  sz.height);
28577             }
28578         }
28579         this.el.alignTo(this.boundEl, this.alignment);
28580         this.editing = true;
28581         if(Roo.QuickTips){
28582             Roo.QuickTips.disable();
28583         }
28584         this.show();
28585     },
28586
28587     /**
28588      * Sets the height and width of this editor.
28589      * @param {Number} width The new width
28590      * @param {Number} height The new height
28591      */
28592     setSize : function(w, h){
28593         this.field.setSize(w, h);
28594         if(this.el){
28595             this.el.sync();
28596         }
28597     },
28598
28599     /**
28600      * Realigns the editor to the bound field based on the current alignment config value.
28601      */
28602     realign : function(){
28603         this.el.alignTo(this.boundEl, this.alignment);
28604     },
28605
28606     /**
28607      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28608      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28609      */
28610     completeEdit : function(remainVisible){
28611         if(!this.editing){
28612             return;
28613         }
28614         var v = this.getValue();
28615         if(this.revertInvalid !== false && !this.field.isValid()){
28616             v = this.startValue;
28617             this.cancelEdit(true);
28618         }
28619         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28620             this.editing = false;
28621             this.hide();
28622             return;
28623         }
28624         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28625             this.editing = false;
28626             if(this.updateEl && this.boundEl){
28627                 this.boundEl.update(v);
28628             }
28629             if(remainVisible !== true){
28630                 this.hide();
28631             }
28632             this.fireEvent("complete", this, v, this.startValue);
28633         }
28634     },
28635
28636     // private
28637     onShow : function(){
28638         this.el.show();
28639         if(this.hideEl !== false){
28640             this.boundEl.hide();
28641         }
28642         this.field.show();
28643         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28644             this.fixIEFocus = true;
28645             this.deferredFocus.defer(50, this);
28646         }else{
28647             this.field.focus();
28648         }
28649         this.fireEvent("startedit", this.boundEl, this.startValue);
28650     },
28651
28652     deferredFocus : function(){
28653         if(this.editing){
28654             this.field.focus();
28655         }
28656     },
28657
28658     /**
28659      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28660      * reverted to the original starting value.
28661      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28662      * cancel (defaults to false)
28663      */
28664     cancelEdit : function(remainVisible){
28665         if(this.editing){
28666             this.setValue(this.startValue);
28667             if(remainVisible !== true){
28668                 this.hide();
28669             }
28670         }
28671     },
28672
28673     // private
28674     onBlur : function(){
28675         if(this.allowBlur !== true && this.editing){
28676             this.completeEdit();
28677         }
28678     },
28679
28680     // private
28681     onHide : function(){
28682         if(this.editing){
28683             this.completeEdit();
28684             return;
28685         }
28686         this.field.blur();
28687         if(this.field.collapse){
28688             this.field.collapse();
28689         }
28690         this.el.hide();
28691         if(this.hideEl !== false){
28692             this.boundEl.show();
28693         }
28694         if(Roo.QuickTips){
28695             Roo.QuickTips.enable();
28696         }
28697     },
28698
28699     /**
28700      * Sets the data value of the editor
28701      * @param {Mixed} value Any valid value supported by the underlying field
28702      */
28703     setValue : function(v){
28704         this.field.setValue(v);
28705     },
28706
28707     /**
28708      * Gets the data value of the editor
28709      * @return {Mixed} The data value
28710      */
28711     getValue : function(){
28712         return this.field.getValue();
28713     }
28714 });/*
28715  * Based on:
28716  * Ext JS Library 1.1.1
28717  * Copyright(c) 2006-2007, Ext JS, LLC.
28718  *
28719  * Originally Released Under LGPL - original licence link has changed is not relivant.
28720  *
28721  * Fork - LGPL
28722  * <script type="text/javascript">
28723  */
28724  
28725 /**
28726  * @class Roo.BasicDialog
28727  * @extends Roo.util.Observable
28728  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28729  * <pre><code>
28730 var dlg = new Roo.BasicDialog("my-dlg", {
28731     height: 200,
28732     width: 300,
28733     minHeight: 100,
28734     minWidth: 150,
28735     modal: true,
28736     proxyDrag: true,
28737     shadow: true
28738 });
28739 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28740 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28741 dlg.addButton('Cancel', dlg.hide, dlg);
28742 dlg.show();
28743 </code></pre>
28744   <b>A Dialog should always be a direct child of the body element.</b>
28745  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28746  * @cfg {String} title Default text to display in the title bar (defaults to null)
28747  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28748  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28749  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28750  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28751  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28752  * (defaults to null with no animation)
28753  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28754  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28755  * property for valid values (defaults to 'all')
28756  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28757  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28758  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28759  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28760  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28761  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28762  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28763  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28764  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28765  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28766  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28767  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28768  * draggable = true (defaults to false)
28769  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28770  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28771  * shadow (defaults to false)
28772  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28773  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28774  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28775  * @cfg {Array} buttons Array of buttons
28776  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28777  * @constructor
28778  * Create a new BasicDialog.
28779  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28780  * @param {Object} config Configuration options
28781  */
28782 Roo.BasicDialog = function(el, config){
28783     this.el = Roo.get(el);
28784     var dh = Roo.DomHelper;
28785     if(!this.el && config && config.autoCreate){
28786         if(typeof config.autoCreate == "object"){
28787             if(!config.autoCreate.id){
28788                 config.autoCreate.id = el;
28789             }
28790             this.el = dh.append(document.body,
28791                         config.autoCreate, true);
28792         }else{
28793             this.el = dh.append(document.body,
28794                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28795         }
28796     }
28797     el = this.el;
28798     el.setDisplayed(true);
28799     el.hide = this.hideAction;
28800     this.id = el.id;
28801     el.addClass("x-dlg");
28802
28803     Roo.apply(this, config);
28804
28805     this.proxy = el.createProxy("x-dlg-proxy");
28806     this.proxy.hide = this.hideAction;
28807     this.proxy.setOpacity(.5);
28808     this.proxy.hide();
28809
28810     if(config.width){
28811         el.setWidth(config.width);
28812     }
28813     if(config.height){
28814         el.setHeight(config.height);
28815     }
28816     this.size = el.getSize();
28817     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28818         this.xy = [config.x,config.y];
28819     }else{
28820         this.xy = el.getCenterXY(true);
28821     }
28822     /** The header element @type Roo.Element */
28823     this.header = el.child("> .x-dlg-hd");
28824     /** The body element @type Roo.Element */
28825     this.body = el.child("> .x-dlg-bd");
28826     /** The footer element @type Roo.Element */
28827     this.footer = el.child("> .x-dlg-ft");
28828
28829     if(!this.header){
28830         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28831     }
28832     if(!this.body){
28833         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28834     }
28835
28836     this.header.unselectable();
28837     if(this.title){
28838         this.header.update(this.title);
28839     }
28840     // this element allows the dialog to be focused for keyboard event
28841     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28842     this.focusEl.swallowEvent("click", true);
28843
28844     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28845
28846     // wrap the body and footer for special rendering
28847     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28848     if(this.footer){
28849         this.bwrap.dom.appendChild(this.footer.dom);
28850     }
28851
28852     this.bg = this.el.createChild({
28853         tag: "div", cls:"x-dlg-bg",
28854         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28855     });
28856     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28857
28858
28859     if(this.autoScroll !== false && !this.autoTabs){
28860         this.body.setStyle("overflow", "auto");
28861     }
28862
28863     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28864
28865     if(this.closable !== false){
28866         this.el.addClass("x-dlg-closable");
28867         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28868         this.close.on("click", this.closeClick, this);
28869         this.close.addClassOnOver("x-dlg-close-over");
28870     }
28871     if(this.collapsible !== false){
28872         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28873         this.collapseBtn.on("click", this.collapseClick, this);
28874         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28875         this.header.on("dblclick", this.collapseClick, this);
28876     }
28877     if(this.resizable !== false){
28878         this.el.addClass("x-dlg-resizable");
28879         this.resizer = new Roo.Resizable(el, {
28880             minWidth: this.minWidth || 80,
28881             minHeight:this.minHeight || 80,
28882             handles: this.resizeHandles || "all",
28883             pinned: true
28884         });
28885         this.resizer.on("beforeresize", this.beforeResize, this);
28886         this.resizer.on("resize", this.onResize, this);
28887     }
28888     if(this.draggable !== false){
28889         el.addClass("x-dlg-draggable");
28890         if (!this.proxyDrag) {
28891             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28892         }
28893         else {
28894             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28895         }
28896         dd.setHandleElId(this.header.id);
28897         dd.endDrag = this.endMove.createDelegate(this);
28898         dd.startDrag = this.startMove.createDelegate(this);
28899         dd.onDrag = this.onDrag.createDelegate(this);
28900         dd.scroll = false;
28901         this.dd = dd;
28902     }
28903     if(this.modal){
28904         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28905         this.mask.enableDisplayMode("block");
28906         this.mask.hide();
28907         this.el.addClass("x-dlg-modal");
28908     }
28909     if(this.shadow){
28910         this.shadow = new Roo.Shadow({
28911             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28912             offset : this.shadowOffset
28913         });
28914     }else{
28915         this.shadowOffset = 0;
28916     }
28917     if(Roo.useShims && this.shim !== false){
28918         this.shim = this.el.createShim();
28919         this.shim.hide = this.hideAction;
28920         this.shim.hide();
28921     }else{
28922         this.shim = false;
28923     }
28924     if(this.autoTabs){
28925         this.initTabs();
28926     }
28927     if (this.buttons) { 
28928         var bts= this.buttons;
28929         this.buttons = [];
28930         Roo.each(bts, function(b) {
28931             this.addButton(b);
28932         }, this);
28933     }
28934     
28935     
28936     this.addEvents({
28937         /**
28938          * @event keydown
28939          * Fires when a key is pressed
28940          * @param {Roo.BasicDialog} this
28941          * @param {Roo.EventObject} e
28942          */
28943         "keydown" : true,
28944         /**
28945          * @event move
28946          * Fires when this dialog is moved by the user.
28947          * @param {Roo.BasicDialog} this
28948          * @param {Number} x The new page X
28949          * @param {Number} y The new page Y
28950          */
28951         "move" : true,
28952         /**
28953          * @event resize
28954          * Fires when this dialog is resized by the user.
28955          * @param {Roo.BasicDialog} this
28956          * @param {Number} width The new width
28957          * @param {Number} height The new height
28958          */
28959         "resize" : true,
28960         /**
28961          * @event beforehide
28962          * Fires before this dialog is hidden.
28963          * @param {Roo.BasicDialog} this
28964          */
28965         "beforehide" : true,
28966         /**
28967          * @event hide
28968          * Fires when this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "hide" : true,
28972         /**
28973          * @event beforeshow
28974          * Fires before this dialog is shown.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "beforeshow" : true,
28978         /**
28979          * @event show
28980          * Fires when this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "show" : true
28984     });
28985     el.on("keydown", this.onKeyDown, this);
28986     el.on("mousedown", this.toFront, this);
28987     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28988     this.el.hide();
28989     Roo.DialogManager.register(this);
28990     Roo.BasicDialog.superclass.constructor.call(this);
28991 };
28992
28993 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28994     shadowOffset: Roo.isIE ? 6 : 5,
28995     minHeight: 80,
28996     minWidth: 200,
28997     minButtonWidth: 75,
28998     defaultButton: null,
28999     buttonAlign: "right",
29000     tabTag: 'div',
29001     firstShow: true,
29002
29003     /**
29004      * Sets the dialog title text
29005      * @param {String} text The title text to display
29006      * @return {Roo.BasicDialog} this
29007      */
29008     setTitle : function(text){
29009         this.header.update(text);
29010         return this;
29011     },
29012
29013     // private
29014     closeClick : function(){
29015         this.hide();
29016     },
29017
29018     // private
29019     collapseClick : function(){
29020         this[this.collapsed ? "expand" : "collapse"]();
29021     },
29022
29023     /**
29024      * Collapses the dialog to its minimized state (only the title bar is visible).
29025      * Equivalent to the user clicking the collapse dialog button.
29026      */
29027     collapse : function(){
29028         if(!this.collapsed){
29029             this.collapsed = true;
29030             this.el.addClass("x-dlg-collapsed");
29031             this.restoreHeight = this.el.getHeight();
29032             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29033         }
29034     },
29035
29036     /**
29037      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29038      * clicking the expand dialog button.
29039      */
29040     expand : function(){
29041         if(this.collapsed){
29042             this.collapsed = false;
29043             this.el.removeClass("x-dlg-collapsed");
29044             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29045         }
29046     },
29047
29048     /**
29049      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29050      * @return {Roo.TabPanel} The tabs component
29051      */
29052     initTabs : function(){
29053         var tabs = this.getTabs();
29054         while(tabs.getTab(0)){
29055             tabs.removeTab(0);
29056         }
29057         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29058             var dom = el.dom;
29059             tabs.addTab(Roo.id(dom), dom.title);
29060             dom.title = "";
29061         });
29062         tabs.activate(0);
29063         return tabs;
29064     },
29065
29066     // private
29067     beforeResize : function(){
29068         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29069     },
29070
29071     // private
29072     onResize : function(){
29073         this.refreshSize();
29074         this.syncBodyHeight();
29075         this.adjustAssets();
29076         this.focus();
29077         this.fireEvent("resize", this, this.size.width, this.size.height);
29078     },
29079
29080     // private
29081     onKeyDown : function(e){
29082         if(this.isVisible()){
29083             this.fireEvent("keydown", this, e);
29084         }
29085     },
29086
29087     /**
29088      * Resizes the dialog.
29089      * @param {Number} width
29090      * @param {Number} height
29091      * @return {Roo.BasicDialog} this
29092      */
29093     resizeTo : function(width, height){
29094         this.el.setSize(width, height);
29095         this.size = {width: width, height: height};
29096         this.syncBodyHeight();
29097         if(this.fixedcenter){
29098             this.center();
29099         }
29100         if(this.isVisible()){
29101             this.constrainXY();
29102             this.adjustAssets();
29103         }
29104         this.fireEvent("resize", this, width, height);
29105         return this;
29106     },
29107
29108
29109     /**
29110      * Resizes the dialog to fit the specified content size.
29111      * @param {Number} width
29112      * @param {Number} height
29113      * @return {Roo.BasicDialog} this
29114      */
29115     setContentSize : function(w, h){
29116         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29117         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29118         //if(!this.el.isBorderBox()){
29119             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29120             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29121         //}
29122         if(this.tabs){
29123             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29124             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29125         }
29126         this.resizeTo(w, h);
29127         return this;
29128     },
29129
29130     /**
29131      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29132      * executed in response to a particular key being pressed while the dialog is active.
29133      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29134      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29135      * @param {Function} fn The function to call
29136      * @param {Object} scope (optional) The scope of the function
29137      * @return {Roo.BasicDialog} this
29138      */
29139     addKeyListener : function(key, fn, scope){
29140         var keyCode, shift, ctrl, alt;
29141         if(typeof key == "object" && !(key instanceof Array)){
29142             keyCode = key["key"];
29143             shift = key["shift"];
29144             ctrl = key["ctrl"];
29145             alt = key["alt"];
29146         }else{
29147             keyCode = key;
29148         }
29149         var handler = function(dlg, e){
29150             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29151                 var k = e.getKey();
29152                 if(keyCode instanceof Array){
29153                     for(var i = 0, len = keyCode.length; i < len; i++){
29154                         if(keyCode[i] == k){
29155                           fn.call(scope || window, dlg, k, e);
29156                           return;
29157                         }
29158                     }
29159                 }else{
29160                     if(k == keyCode){
29161                         fn.call(scope || window, dlg, k, e);
29162                     }
29163                 }
29164             }
29165         };
29166         this.on("keydown", handler);
29167         return this;
29168     },
29169
29170     /**
29171      * Returns the TabPanel component (creates it if it doesn't exist).
29172      * Note: If you wish to simply check for the existence of tabs without creating them,
29173      * check for a null 'tabs' property.
29174      * @return {Roo.TabPanel} The tabs component
29175      */
29176     getTabs : function(){
29177         if(!this.tabs){
29178             this.el.addClass("x-dlg-auto-tabs");
29179             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29180             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29181         }
29182         return this.tabs;
29183     },
29184
29185     /**
29186      * Adds a button to the footer section of the dialog.
29187      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29188      * object or a valid Roo.DomHelper element config
29189      * @param {Function} handler The function called when the button is clicked
29190      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29191      * @return {Roo.Button} The new button
29192      */
29193     addButton : function(config, handler, scope){
29194         var dh = Roo.DomHelper;
29195         if(!this.footer){
29196             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29197         }
29198         if(!this.btnContainer){
29199             var tb = this.footer.createChild({
29200
29201                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29202                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29203             }, null, true);
29204             this.btnContainer = tb.firstChild.firstChild.firstChild;
29205         }
29206         var bconfig = {
29207             handler: handler,
29208             scope: scope,
29209             minWidth: this.minButtonWidth,
29210             hideParent:true
29211         };
29212         if(typeof config == "string"){
29213             bconfig.text = config;
29214         }else{
29215             if(config.tag){
29216                 bconfig.dhconfig = config;
29217             }else{
29218                 Roo.apply(bconfig, config);
29219             }
29220         }
29221         var fc = false;
29222         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29223             bconfig.position = Math.max(0, bconfig.position);
29224             fc = this.btnContainer.childNodes[bconfig.position];
29225         }
29226          
29227         var btn = new Roo.Button(
29228             fc ? 
29229                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29230                 : this.btnContainer.appendChild(document.createElement("td")),
29231             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29232             bconfig
29233         );
29234         this.syncBodyHeight();
29235         if(!this.buttons){
29236             /**
29237              * Array of all the buttons that have been added to this dialog via addButton
29238              * @type Array
29239              */
29240             this.buttons = [];
29241         }
29242         this.buttons.push(btn);
29243         return btn;
29244     },
29245
29246     /**
29247      * Sets the default button to be focused when the dialog is displayed.
29248      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29249      * @return {Roo.BasicDialog} this
29250      */
29251     setDefaultButton : function(btn){
29252         this.defaultButton = btn;
29253         return this;
29254     },
29255
29256     // private
29257     getHeaderFooterHeight : function(safe){
29258         var height = 0;
29259         if(this.header){
29260            height += this.header.getHeight();
29261         }
29262         if(this.footer){
29263            var fm = this.footer.getMargins();
29264             height += (this.footer.getHeight()+fm.top+fm.bottom);
29265         }
29266         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29267         height += this.centerBg.getPadding("tb");
29268         return height;
29269     },
29270
29271     // private
29272     syncBodyHeight : function(){
29273         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29274         var height = this.size.height - this.getHeaderFooterHeight(false);
29275         bd.setHeight(height-bd.getMargins("tb"));
29276         var hh = this.header.getHeight();
29277         var h = this.size.height-hh;
29278         cb.setHeight(h);
29279         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29280         bw.setHeight(h-cb.getPadding("tb"));
29281         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29282         bd.setWidth(bw.getWidth(true));
29283         if(this.tabs){
29284             this.tabs.syncHeight();
29285             if(Roo.isIE){
29286                 this.tabs.el.repaint();
29287             }
29288         }
29289     },
29290
29291     /**
29292      * Restores the previous state of the dialog if Roo.state is configured.
29293      * @return {Roo.BasicDialog} this
29294      */
29295     restoreState : function(){
29296         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29297         if(box && box.width){
29298             this.xy = [box.x, box.y];
29299             this.resizeTo(box.width, box.height);
29300         }
29301         return this;
29302     },
29303
29304     // private
29305     beforeShow : function(){
29306         this.expand();
29307         if(this.fixedcenter){
29308             this.xy = this.el.getCenterXY(true);
29309         }
29310         if(this.modal){
29311             Roo.get(document.body).addClass("x-body-masked");
29312             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29313             this.mask.show();
29314         }
29315         this.constrainXY();
29316     },
29317
29318     // private
29319     animShow : function(){
29320         var b = Roo.get(this.animateTarget).getBox();
29321         this.proxy.setSize(b.width, b.height);
29322         this.proxy.setLocation(b.x, b.y);
29323         this.proxy.show();
29324         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29325                     true, .35, this.showEl.createDelegate(this));
29326     },
29327
29328     /**
29329      * Shows the dialog.
29330      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29331      * @return {Roo.BasicDialog} this
29332      */
29333     show : function(animateTarget){
29334         if (this.fireEvent("beforeshow", this) === false){
29335             return;
29336         }
29337         if(this.syncHeightBeforeShow){
29338             this.syncBodyHeight();
29339         }else if(this.firstShow){
29340             this.firstShow = false;
29341             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29342         }
29343         this.animateTarget = animateTarget || this.animateTarget;
29344         if(!this.el.isVisible()){
29345             this.beforeShow();
29346             if(this.animateTarget && Roo.get(this.animateTarget)){
29347                 this.animShow();
29348             }else{
29349                 this.showEl();
29350             }
29351         }
29352         return this;
29353     },
29354
29355     // private
29356     showEl : function(){
29357         this.proxy.hide();
29358         this.el.setXY(this.xy);
29359         this.el.show();
29360         this.adjustAssets(true);
29361         this.toFront();
29362         this.focus();
29363         // IE peekaboo bug - fix found by Dave Fenwick
29364         if(Roo.isIE){
29365             this.el.repaint();
29366         }
29367         this.fireEvent("show", this);
29368     },
29369
29370     /**
29371      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29372      * dialog itself will receive focus.
29373      */
29374     focus : function(){
29375         if(this.defaultButton){
29376             this.defaultButton.focus();
29377         }else{
29378             this.focusEl.focus();
29379         }
29380     },
29381
29382     // private
29383     constrainXY : function(){
29384         if(this.constraintoviewport !== false){
29385             if(!this.viewSize){
29386                 if(this.container){
29387                     var s = this.container.getSize();
29388                     this.viewSize = [s.width, s.height];
29389                 }else{
29390                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29391                 }
29392             }
29393             var s = Roo.get(this.container||document).getScroll();
29394
29395             var x = this.xy[0], y = this.xy[1];
29396             var w = this.size.width, h = this.size.height;
29397             var vw = this.viewSize[0], vh = this.viewSize[1];
29398             // only move it if it needs it
29399             var moved = false;
29400             // first validate right/bottom
29401             if(x + w > vw+s.left){
29402                 x = vw - w;
29403                 moved = true;
29404             }
29405             if(y + h > vh+s.top){
29406                 y = vh - h;
29407                 moved = true;
29408             }
29409             // then make sure top/left isn't negative
29410             if(x < s.left){
29411                 x = s.left;
29412                 moved = true;
29413             }
29414             if(y < s.top){
29415                 y = s.top;
29416                 moved = true;
29417             }
29418             if(moved){
29419                 // cache xy
29420                 this.xy = [x, y];
29421                 if(this.isVisible()){
29422                     this.el.setLocation(x, y);
29423                     this.adjustAssets();
29424                 }
29425             }
29426         }
29427     },
29428
29429     // private
29430     onDrag : function(){
29431         if(!this.proxyDrag){
29432             this.xy = this.el.getXY();
29433             this.adjustAssets();
29434         }
29435     },
29436
29437     // private
29438     adjustAssets : function(doShow){
29439         var x = this.xy[0], y = this.xy[1];
29440         var w = this.size.width, h = this.size.height;
29441         if(doShow === true){
29442             if(this.shadow){
29443                 this.shadow.show(this.el);
29444             }
29445             if(this.shim){
29446                 this.shim.show();
29447             }
29448         }
29449         if(this.shadow && this.shadow.isVisible()){
29450             this.shadow.show(this.el);
29451         }
29452         if(this.shim && this.shim.isVisible()){
29453             this.shim.setBounds(x, y, w, h);
29454         }
29455     },
29456
29457     // private
29458     adjustViewport : function(w, h){
29459         if(!w || !h){
29460             w = Roo.lib.Dom.getViewWidth();
29461             h = Roo.lib.Dom.getViewHeight();
29462         }
29463         // cache the size
29464         this.viewSize = [w, h];
29465         if(this.modal && this.mask.isVisible()){
29466             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29467             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29468         }
29469         if(this.isVisible()){
29470             this.constrainXY();
29471         }
29472     },
29473
29474     /**
29475      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29476      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29477      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29478      */
29479     destroy : function(removeEl){
29480         if(this.isVisible()){
29481             this.animateTarget = null;
29482             this.hide();
29483         }
29484         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29485         if(this.tabs){
29486             this.tabs.destroy(removeEl);
29487         }
29488         Roo.destroy(
29489              this.shim,
29490              this.proxy,
29491              this.resizer,
29492              this.close,
29493              this.mask
29494         );
29495         if(this.dd){
29496             this.dd.unreg();
29497         }
29498         if(this.buttons){
29499            for(var i = 0, len = this.buttons.length; i < len; i++){
29500                this.buttons[i].destroy();
29501            }
29502         }
29503         this.el.removeAllListeners();
29504         if(removeEl === true){
29505             this.el.update("");
29506             this.el.remove();
29507         }
29508         Roo.DialogManager.unregister(this);
29509     },
29510
29511     // private
29512     startMove : function(){
29513         if(this.proxyDrag){
29514             this.proxy.show();
29515         }
29516         if(this.constraintoviewport !== false){
29517             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29518         }
29519     },
29520
29521     // private
29522     endMove : function(){
29523         if(!this.proxyDrag){
29524             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29525         }else{
29526             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29527             this.proxy.hide();
29528         }
29529         this.refreshSize();
29530         this.adjustAssets();
29531         this.focus();
29532         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29533     },
29534
29535     /**
29536      * Brings this dialog to the front of any other visible dialogs
29537      * @return {Roo.BasicDialog} this
29538      */
29539     toFront : function(){
29540         Roo.DialogManager.bringToFront(this);
29541         return this;
29542     },
29543
29544     /**
29545      * Sends this dialog to the back (under) of any other visible dialogs
29546      * @return {Roo.BasicDialog} this
29547      */
29548     toBack : function(){
29549         Roo.DialogManager.sendToBack(this);
29550         return this;
29551     },
29552
29553     /**
29554      * Centers this dialog in the viewport
29555      * @return {Roo.BasicDialog} this
29556      */
29557     center : function(){
29558         var xy = this.el.getCenterXY(true);
29559         this.moveTo(xy[0], xy[1]);
29560         return this;
29561     },
29562
29563     /**
29564      * Moves the dialog's top-left corner to the specified point
29565      * @param {Number} x
29566      * @param {Number} y
29567      * @return {Roo.BasicDialog} this
29568      */
29569     moveTo : function(x, y){
29570         this.xy = [x,y];
29571         if(this.isVisible()){
29572             this.el.setXY(this.xy);
29573             this.adjustAssets();
29574         }
29575         return this;
29576     },
29577
29578     /**
29579      * Aligns the dialog to the specified element
29580      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29581      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29582      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29583      * @return {Roo.BasicDialog} this
29584      */
29585     alignTo : function(element, position, offsets){
29586         this.xy = this.el.getAlignToXY(element, position, offsets);
29587         if(this.isVisible()){
29588             this.el.setXY(this.xy);
29589             this.adjustAssets();
29590         }
29591         return this;
29592     },
29593
29594     /**
29595      * Anchors an element to another element and realigns it when the window is resized.
29596      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29597      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29598      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29599      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29600      * is a number, it is used as the buffer delay (defaults to 50ms).
29601      * @return {Roo.BasicDialog} this
29602      */
29603     anchorTo : function(el, alignment, offsets, monitorScroll){
29604         var action = function(){
29605             this.alignTo(el, alignment, offsets);
29606         };
29607         Roo.EventManager.onWindowResize(action, this);
29608         var tm = typeof monitorScroll;
29609         if(tm != 'undefined'){
29610             Roo.EventManager.on(window, 'scroll', action, this,
29611                 {buffer: tm == 'number' ? monitorScroll : 50});
29612         }
29613         action.call(this);
29614         return this;
29615     },
29616
29617     /**
29618      * Returns true if the dialog is visible
29619      * @return {Boolean}
29620      */
29621     isVisible : function(){
29622         return this.el.isVisible();
29623     },
29624
29625     // private
29626     animHide : function(callback){
29627         var b = Roo.get(this.animateTarget).getBox();
29628         this.proxy.show();
29629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29630         this.el.hide();
29631         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29632                     this.hideEl.createDelegate(this, [callback]));
29633     },
29634
29635     /**
29636      * Hides the dialog.
29637      * @param {Function} callback (optional) Function to call when the dialog is hidden
29638      * @return {Roo.BasicDialog} this
29639      */
29640     hide : function(callback){
29641         if (this.fireEvent("beforehide", this) === false){
29642             return;
29643         }
29644         if(this.shadow){
29645             this.shadow.hide();
29646         }
29647         if(this.shim) {
29648           this.shim.hide();
29649         }
29650         // sometimes animateTarget seems to get set.. causing problems...
29651         // this just double checks..
29652         if(this.animateTarget && Roo.get(this.animateTarget)) {
29653            this.animHide(callback);
29654         }else{
29655             this.el.hide();
29656             this.hideEl(callback);
29657         }
29658         return this;
29659     },
29660
29661     // private
29662     hideEl : function(callback){
29663         this.proxy.hide();
29664         if(this.modal){
29665             this.mask.hide();
29666             Roo.get(document.body).removeClass("x-body-masked");
29667         }
29668         this.fireEvent("hide", this);
29669         if(typeof callback == "function"){
29670             callback();
29671         }
29672     },
29673
29674     // private
29675     hideAction : function(){
29676         this.setLeft("-10000px");
29677         this.setTop("-10000px");
29678         this.setStyle("visibility", "hidden");
29679     },
29680
29681     // private
29682     refreshSize : function(){
29683         this.size = this.el.getSize();
29684         this.xy = this.el.getXY();
29685         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29686     },
29687
29688     // private
29689     // z-index is managed by the DialogManager and may be overwritten at any time
29690     setZIndex : function(index){
29691         if(this.modal){
29692             this.mask.setStyle("z-index", index);
29693         }
29694         if(this.shim){
29695             this.shim.setStyle("z-index", ++index);
29696         }
29697         if(this.shadow){
29698             this.shadow.setZIndex(++index);
29699         }
29700         this.el.setStyle("z-index", ++index);
29701         if(this.proxy){
29702             this.proxy.setStyle("z-index", ++index);
29703         }
29704         if(this.resizer){
29705             this.resizer.proxy.setStyle("z-index", ++index);
29706         }
29707
29708         this.lastZIndex = index;
29709     },
29710
29711     /**
29712      * Returns the element for this dialog
29713      * @return {Roo.Element} The underlying dialog Element
29714      */
29715     getEl : function(){
29716         return this.el;
29717     }
29718 });
29719
29720 /**
29721  * @class Roo.DialogManager
29722  * Provides global access to BasicDialogs that have been created and
29723  * support for z-indexing (layering) multiple open dialogs.
29724  */
29725 Roo.DialogManager = function(){
29726     var list = {};
29727     var accessList = [];
29728     var front = null;
29729
29730     // private
29731     var sortDialogs = function(d1, d2){
29732         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29733     };
29734
29735     // private
29736     var orderDialogs = function(){
29737         accessList.sort(sortDialogs);
29738         var seed = Roo.DialogManager.zseed;
29739         for(var i = 0, len = accessList.length; i < len; i++){
29740             var dlg = accessList[i];
29741             if(dlg){
29742                 dlg.setZIndex(seed + (i*10));
29743             }
29744         }
29745     };
29746
29747     return {
29748         /**
29749          * The starting z-index for BasicDialogs (defaults to 9000)
29750          * @type Number The z-index value
29751          */
29752         zseed : 9000,
29753
29754         // private
29755         register : function(dlg){
29756             list[dlg.id] = dlg;
29757             accessList.push(dlg);
29758         },
29759
29760         // private
29761         unregister : function(dlg){
29762             delete list[dlg.id];
29763             var i=0;
29764             var len=0;
29765             if(!accessList.indexOf){
29766                 for(  i = 0, len = accessList.length; i < len; i++){
29767                     if(accessList[i] == dlg){
29768                         accessList.splice(i, 1);
29769                         return;
29770                     }
29771                 }
29772             }else{
29773                  i = accessList.indexOf(dlg);
29774                 if(i != -1){
29775                     accessList.splice(i, 1);
29776                 }
29777             }
29778         },
29779
29780         /**
29781          * Gets a registered dialog by id
29782          * @param {String/Object} id The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         get : function(id){
29786             return typeof id == "object" ? id : list[id];
29787         },
29788
29789         /**
29790          * Brings the specified dialog to the front
29791          * @param {String/Object} dlg The id of the dialog or a dialog
29792          * @return {Roo.BasicDialog} this
29793          */
29794         bringToFront : function(dlg){
29795             dlg = this.get(dlg);
29796             if(dlg != front){
29797                 front = dlg;
29798                 dlg._lastAccess = new Date().getTime();
29799                 orderDialogs();
29800             }
29801             return dlg;
29802         },
29803
29804         /**
29805          * Sends the specified dialog to the back
29806          * @param {String/Object} dlg The id of the dialog or a dialog
29807          * @return {Roo.BasicDialog} this
29808          */
29809         sendToBack : function(dlg){
29810             dlg = this.get(dlg);
29811             dlg._lastAccess = -(new Date().getTime());
29812             orderDialogs();
29813             return dlg;
29814         },
29815
29816         /**
29817          * Hides all dialogs
29818          */
29819         hideAll : function(){
29820             for(var id in list){
29821                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29822                     list[id].hide();
29823                 }
29824             }
29825         }
29826     };
29827 }();
29828
29829 /**
29830  * @class Roo.LayoutDialog
29831  * @extends Roo.BasicDialog
29832  * Dialog which provides adjustments for working with a layout in a Dialog.
29833  * Add your necessary layout config options to the dialog's config.<br>
29834  * Example usage (including a nested layout):
29835  * <pre><code>
29836 if(!dialog){
29837     dialog = new Roo.LayoutDialog("download-dlg", {
29838         modal: true,
29839         width:600,
29840         height:450,
29841         shadow:true,
29842         minWidth:500,
29843         minHeight:350,
29844         autoTabs:true,
29845         proxyDrag:true,
29846         // layout config merges with the dialog config
29847         center:{
29848             tabPosition: "top",
29849             alwaysShowTabs: true
29850         }
29851     });
29852     dialog.addKeyListener(27, dialog.hide, dialog);
29853     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29854     dialog.addButton("Build It!", this.getDownload, this);
29855
29856     // we can even add nested layouts
29857     var innerLayout = new Roo.BorderLayout("dl-inner", {
29858         east: {
29859             initialSize: 200,
29860             autoScroll:true,
29861             split:true
29862         },
29863         center: {
29864             autoScroll:true
29865         }
29866     });
29867     innerLayout.beginUpdate();
29868     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29869     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29870     innerLayout.endUpdate(true);
29871
29872     var layout = dialog.getLayout();
29873     layout.beginUpdate();
29874     layout.add("center", new Roo.ContentPanel("standard-panel",
29875                         {title: "Download the Source", fitToFrame:true}));
29876     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29877                {title: "Build your own roo.js"}));
29878     layout.getRegion("center").showPanel(sp);
29879     layout.endUpdate();
29880 }
29881 </code></pre>
29882     * @constructor
29883     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29884     * @param {Object} config configuration options
29885   */
29886 Roo.LayoutDialog = function(el, cfg){
29887     
29888     var config=  cfg;
29889     if (typeof(cfg) == 'undefined') {
29890         config = Roo.apply({}, el);
29891         // not sure why we use documentElement here.. - it should always be body.
29892         // IE7 borks horribly if we use documentElement.
29893         // webkit also does not like documentElement - it creates a body element...
29894         el = Roo.get( document.body || document.documentElement ).createChild();
29895         //config.autoCreate = true;
29896     }
29897     
29898     
29899     config.autoTabs = false;
29900     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29901     this.body.setStyle({overflow:"hidden", position:"relative"});
29902     this.layout = new Roo.BorderLayout(this.body.dom, config);
29903     this.layout.monitorWindowResize = false;
29904     this.el.addClass("x-dlg-auto-layout");
29905     // fix case when center region overwrites center function
29906     this.center = Roo.BasicDialog.prototype.center;
29907     this.on("show", this.layout.layout, this.layout, true);
29908     if (config.items) {
29909         var xitems = config.items;
29910         delete config.items;
29911         Roo.each(xitems, this.addxtype, this);
29912     }
29913     
29914     
29915 };
29916 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29917     /**
29918      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29919      * @deprecated
29920      */
29921     endUpdate : function(){
29922         this.layout.endUpdate();
29923     },
29924
29925     /**
29926      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29927      *  @deprecated
29928      */
29929     beginUpdate : function(){
29930         this.layout.beginUpdate();
29931     },
29932
29933     /**
29934      * Get the BorderLayout for this dialog
29935      * @return {Roo.BorderLayout}
29936      */
29937     getLayout : function(){
29938         return this.layout;
29939     },
29940
29941     showEl : function(){
29942         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29943         if(Roo.isIE7){
29944             this.layout.layout();
29945         }
29946     },
29947
29948     // private
29949     // Use the syncHeightBeforeShow config option to control this automatically
29950     syncBodyHeight : function(){
29951         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29952         if(this.layout){this.layout.layout();}
29953     },
29954     
29955       /**
29956      * Add an xtype element (actually adds to the layout.)
29957      * @return {Object} xdata xtype object data.
29958      */
29959     
29960     addxtype : function(c) {
29961         return this.layout.addxtype(c);
29962     }
29963 });/*
29964  * Based on:
29965  * Ext JS Library 1.1.1
29966  * Copyright(c) 2006-2007, Ext JS, LLC.
29967  *
29968  * Originally Released Under LGPL - original licence link has changed is not relivant.
29969  *
29970  * Fork - LGPL
29971  * <script type="text/javascript">
29972  */
29973  
29974 /**
29975  * @class Roo.MessageBox
29976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29977  * Example usage:
29978  *<pre><code>
29979 // Basic alert:
29980 Roo.Msg.alert('Status', 'Changes saved successfully.');
29981
29982 // Prompt for user data:
29983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29984     if (btn == 'ok'){
29985         // process text value...
29986     }
29987 });
29988
29989 // Show a dialog using config options:
29990 Roo.Msg.show({
29991    title:'Save Changes?',
29992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29993    buttons: Roo.Msg.YESNOCANCEL,
29994    fn: processResult,
29995    animEl: 'elId'
29996 });
29997 </code></pre>
29998  * @singleton
29999  */
30000 Roo.MessageBox = function(){
30001     var dlg, opt, mask, waitTimer;
30002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30003     var buttons, activeTextEl, bwidth;
30004
30005     // private
30006     var handleButton = function(button){
30007         dlg.hide();
30008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30009     };
30010
30011     // private
30012     var handleHide = function(){
30013         if(opt && opt.cls){
30014             dlg.el.removeClass(opt.cls);
30015         }
30016         if(waitTimer){
30017             Roo.TaskMgr.stop(waitTimer);
30018             waitTimer = null;
30019         }
30020     };
30021
30022     // private
30023     var updateButtons = function(b){
30024         var width = 0;
30025         if(!b){
30026             buttons["ok"].hide();
30027             buttons["cancel"].hide();
30028             buttons["yes"].hide();
30029             buttons["no"].hide();
30030             dlg.footer.dom.style.display = 'none';
30031             return width;
30032         }
30033         dlg.footer.dom.style.display = '';
30034         for(var k in buttons){
30035             if(typeof buttons[k] != "function"){
30036                 if(b[k]){
30037                     buttons[k].show();
30038                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30039                     width += buttons[k].el.getWidth()+15;
30040                 }else{
30041                     buttons[k].hide();
30042                 }
30043             }
30044         }
30045         return width;
30046     };
30047
30048     // private
30049     var handleEsc = function(d, k, e){
30050         if(opt && opt.closable !== false){
30051             dlg.hide();
30052         }
30053         if(e){
30054             e.stopEvent();
30055         }
30056     };
30057
30058     return {
30059         /**
30060          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30061          * @return {Roo.BasicDialog} The BasicDialog element
30062          */
30063         getDialog : function(){
30064            if(!dlg){
30065                 dlg = new Roo.BasicDialog("x-msg-box", {
30066                     autoCreate : true,
30067                     shadow: true,
30068                     draggable: true,
30069                     resizable:false,
30070                     constraintoviewport:false,
30071                     fixedcenter:true,
30072                     collapsible : false,
30073                     shim:true,
30074                     modal: true,
30075                     width:400, height:100,
30076                     buttonAlign:"center",
30077                     closeClick : function(){
30078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30079                             handleButton("no");
30080                         }else{
30081                             handleButton("cancel");
30082                         }
30083                     }
30084                 });
30085                 dlg.on("hide", handleHide);
30086                 mask = dlg.mask;
30087                 dlg.addKeyListener(27, handleEsc);
30088                 buttons = {};
30089                 var bt = this.buttonText;
30090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30094                 bodyEl = dlg.body.createChild({
30095
30096                     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>'
30097                 });
30098                 msgEl = bodyEl.dom.firstChild;
30099                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30100                 textboxEl.enableDisplayMode();
30101                 textboxEl.addKeyListener([10,13], function(){
30102                     if(dlg.isVisible() && opt && opt.buttons){
30103                         if(opt.buttons.ok){
30104                             handleButton("ok");
30105                         }else if(opt.buttons.yes){
30106                             handleButton("yes");
30107                         }
30108                     }
30109                 });
30110                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30111                 textareaEl.enableDisplayMode();
30112                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30113                 progressEl.enableDisplayMode();
30114                 var pf = progressEl.dom.firstChild;
30115                 if (pf) {
30116                     pp = Roo.get(pf.firstChild);
30117                     pp.setHeight(pf.offsetHeight);
30118                 }
30119                 
30120             }
30121             return dlg;
30122         },
30123
30124         /**
30125          * Updates the message box body text
30126          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30127          * the XHTML-compliant non-breaking space character '&amp;#160;')
30128          * @return {Roo.MessageBox} This message box
30129          */
30130         updateText : function(text){
30131             if(!dlg.isVisible() && !opt.width){
30132                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30133             }
30134             msgEl.innerHTML = text || '&#160;';
30135             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30136                         Math.max(opt.minWidth || this.minWidth, bwidth));
30137             if(opt.prompt){
30138                 activeTextEl.setWidth(w);
30139             }
30140             if(dlg.isVisible()){
30141                 dlg.fixedcenter = false;
30142             }
30143             dlg.setContentSize(w, bodyEl.getHeight());
30144             if(dlg.isVisible()){
30145                 dlg.fixedcenter = true;
30146             }
30147             return this;
30148         },
30149
30150         /**
30151          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30152          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30153          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30154          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30155          * @return {Roo.MessageBox} This message box
30156          */
30157         updateProgress : function(value, text){
30158             if(text){
30159                 this.updateText(text);
30160             }
30161             if (pp) { // weird bug on my firefox - for some reason this is not defined
30162                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30163             }
30164             return this;
30165         },        
30166
30167         /**
30168          * Returns true if the message box is currently displayed
30169          * @return {Boolean} True if the message box is visible, else false
30170          */
30171         isVisible : function(){
30172             return dlg && dlg.isVisible();  
30173         },
30174
30175         /**
30176          * Hides the message box if it is displayed
30177          */
30178         hide : function(){
30179             if(this.isVisible()){
30180                 dlg.hide();
30181             }  
30182         },
30183
30184         /**
30185          * Displays a new message box, or reinitializes an existing message box, based on the config options
30186          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30187          * The following config object properties are supported:
30188          * <pre>
30189 Property    Type             Description
30190 ----------  ---------------  ------------------------------------------------------------------------------------
30191 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30192                                    closes (defaults to undefined)
30193 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30194                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30195 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30196                                    progress and wait dialogs will ignore this property and always hide the
30197                                    close button as they can only be closed programmatically.
30198 cls               String           A custom CSS class to apply to the message box element
30199 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30200                                    displayed (defaults to 75)
30201 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30202                                    function will be btn (the name of the button that was clicked, if applicable,
30203                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30204                                    Progress and wait dialogs will ignore this option since they do not respond to
30205                                    user actions and can only be closed programmatically, so any required function
30206                                    should be called by the same code after it closes the dialog.
30207 icon              String           A CSS class that provides a background image to be used as an icon for
30208                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30209 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30210 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30211 modal             Boolean          False to allow user interaction with the page while the message box is
30212                                    displayed (defaults to true)
30213 msg               String           A string that will replace the existing message box body text (defaults
30214                                    to the XHTML-compliant non-breaking space character '&#160;')
30215 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30216 progress          Boolean          True to display a progress bar (defaults to false)
30217 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30218 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30219 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30220 title             String           The title text
30221 value             String           The string value to set into the active textbox element if displayed
30222 wait              Boolean          True to display a progress bar (defaults to false)
30223 width             Number           The width of the dialog in pixels
30224 </pre>
30225          *
30226          * Example usage:
30227          * <pre><code>
30228 Roo.Msg.show({
30229    title: 'Address',
30230    msg: 'Please enter your address:',
30231    width: 300,
30232    buttons: Roo.MessageBox.OKCANCEL,
30233    multiline: true,
30234    fn: saveAddress,
30235    animEl: 'addAddressBtn'
30236 });
30237 </code></pre>
30238          * @param {Object} config Configuration options
30239          * @return {Roo.MessageBox} This message box
30240          */
30241         show : function(options)
30242         {
30243             
30244             // this causes nightmares if you show one dialog after another
30245             // especially on callbacks..
30246              
30247             if(this.isVisible()){
30248                 
30249                 this.hide();
30250                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30253                 
30254             }
30255             var d = this.getDialog();
30256             opt = options;
30257             d.setTitle(opt.title || "&#160;");
30258             d.close.setDisplayed(opt.closable !== false);
30259             activeTextEl = textboxEl;
30260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30261             if(opt.prompt){
30262                 if(opt.multiline){
30263                     textboxEl.hide();
30264                     textareaEl.show();
30265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30266                         opt.multiline : this.defaultTextHeight);
30267                     activeTextEl = textareaEl;
30268                 }else{
30269                     textboxEl.show();
30270                     textareaEl.hide();
30271                 }
30272             }else{
30273                 textboxEl.hide();
30274                 textareaEl.hide();
30275             }
30276             progressEl.setDisplayed(opt.progress === true);
30277             this.updateProgress(0);
30278             activeTextEl.dom.value = opt.value || "";
30279             if(opt.prompt){
30280                 dlg.setDefaultButton(activeTextEl);
30281             }else{
30282                 var bs = opt.buttons;
30283                 var db = null;
30284                 if(bs && bs.ok){
30285                     db = buttons["ok"];
30286                 }else if(bs && bs.yes){
30287                     db = buttons["yes"];
30288                 }
30289                 dlg.setDefaultButton(db);
30290             }
30291             bwidth = updateButtons(opt.buttons);
30292             this.updateText(opt.msg);
30293             if(opt.cls){
30294                 d.el.addClass(opt.cls);
30295             }
30296             d.proxyDrag = opt.proxyDrag === true;
30297             d.modal = opt.modal !== false;
30298             d.mask = opt.modal !== false ? mask : false;
30299             if(!d.isVisible()){
30300                 // force it to the end of the z-index stack so it gets a cursor in FF
30301                 document.body.appendChild(dlg.el.dom);
30302                 d.animateTarget = null;
30303                 d.show(options.animEl);
30304             }
30305             return this;
30306         },
30307
30308         /**
30309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30311          * and closing the message box when the process is complete.
30312          * @param {String} title The title bar text
30313          * @param {String} msg The message box body text
30314          * @return {Roo.MessageBox} This message box
30315          */
30316         progress : function(title, msg){
30317             this.show({
30318                 title : title,
30319                 msg : msg,
30320                 buttons: false,
30321                 progress:true,
30322                 closable:false,
30323                 minWidth: this.minProgressWidth,
30324                 modal : true
30325             });
30326             return this;
30327         },
30328
30329         /**
30330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30331          * If a callback function is passed it will be called after the user clicks the button, and the
30332          * id of the button that was clicked will be passed as the only parameter to the callback
30333          * (could also be the top-right close button).
30334          * @param {String} title The title bar text
30335          * @param {String} msg The message box body text
30336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30337          * @param {Object} scope (optional) The scope of the callback function
30338          * @return {Roo.MessageBox} This message box
30339          */
30340         alert : function(title, msg, fn, scope){
30341             this.show({
30342                 title : title,
30343                 msg : msg,
30344                 buttons: this.OK,
30345                 fn: fn,
30346                 scope : scope,
30347                 modal : true
30348             });
30349             return this;
30350         },
30351
30352         /**
30353          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30354          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30355          * You are responsible for closing the message box when the process is complete.
30356          * @param {String} msg The message box body text
30357          * @param {String} title (optional) The title bar text
30358          * @return {Roo.MessageBox} This message box
30359          */
30360         wait : function(msg, title){
30361             this.show({
30362                 title : title,
30363                 msg : msg,
30364                 buttons: false,
30365                 closable:false,
30366                 progress:true,
30367                 modal:true,
30368                 width:300,
30369                 wait:true
30370             });
30371             waitTimer = Roo.TaskMgr.start({
30372                 run: function(i){
30373                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30374                 },
30375                 interval: 1000
30376             });
30377             return this;
30378         },
30379
30380         /**
30381          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30382          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30383          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30384          * @param {String} title The title bar text
30385          * @param {String} msg The message box body text
30386          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30387          * @param {Object} scope (optional) The scope of the callback function
30388          * @return {Roo.MessageBox} This message box
30389          */
30390         confirm : function(title, msg, fn, scope){
30391             this.show({
30392                 title : title,
30393                 msg : msg,
30394                 buttons: this.YESNO,
30395                 fn: fn,
30396                 scope : scope,
30397                 modal : true
30398             });
30399             return this;
30400         },
30401
30402         /**
30403          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30404          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30405          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30406          * (could also be the top-right close button) and the text that was entered will be passed as the two
30407          * parameters to the callback.
30408          * @param {String} title The title bar text
30409          * @param {String} msg The message box body text
30410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30411          * @param {Object} scope (optional) The scope of the callback function
30412          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30413          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30414          * @return {Roo.MessageBox} This message box
30415          */
30416         prompt : function(title, msg, fn, scope, multiline){
30417             this.show({
30418                 title : title,
30419                 msg : msg,
30420                 buttons: this.OKCANCEL,
30421                 fn: fn,
30422                 minWidth:250,
30423                 scope : scope,
30424                 prompt:true,
30425                 multiline: multiline,
30426                 modal : true
30427             });
30428             return this;
30429         },
30430
30431         /**
30432          * Button config that displays a single OK button
30433          * @type Object
30434          */
30435         OK : {ok:true},
30436         /**
30437          * Button config that displays Yes and No buttons
30438          * @type Object
30439          */
30440         YESNO : {yes:true, no:true},
30441         /**
30442          * Button config that displays OK and Cancel buttons
30443          * @type Object
30444          */
30445         OKCANCEL : {ok:true, cancel:true},
30446         /**
30447          * Button config that displays Yes, No and Cancel buttons
30448          * @type Object
30449          */
30450         YESNOCANCEL : {yes:true, no:true, cancel:true},
30451
30452         /**
30453          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30454          * @type Number
30455          */
30456         defaultTextHeight : 75,
30457         /**
30458          * The maximum width in pixels of the message box (defaults to 600)
30459          * @type Number
30460          */
30461         maxWidth : 600,
30462         /**
30463          * The minimum width in pixels of the message box (defaults to 100)
30464          * @type Number
30465          */
30466         minWidth : 100,
30467         /**
30468          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30469          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30470          * @type Number
30471          */
30472         minProgressWidth : 250,
30473         /**
30474          * An object containing the default button text strings that can be overriden for localized language support.
30475          * Supported properties are: ok, cancel, yes and no.
30476          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30477          * @type Object
30478          */
30479         buttonText : {
30480             ok : "OK",
30481             cancel : "Cancel",
30482             yes : "Yes",
30483             no : "No"
30484         }
30485     };
30486 }();
30487
30488 /**
30489  * Shorthand for {@link Roo.MessageBox}
30490  */
30491 Roo.Msg = Roo.MessageBox;/*
30492  * Based on:
30493  * Ext JS Library 1.1.1
30494  * Copyright(c) 2006-2007, Ext JS, LLC.
30495  *
30496  * Originally Released Under LGPL - original licence link has changed is not relivant.
30497  *
30498  * Fork - LGPL
30499  * <script type="text/javascript">
30500  */
30501 /**
30502  * @class Roo.QuickTips
30503  * Provides attractive and customizable tooltips for any element.
30504  * @singleton
30505  */
30506 Roo.QuickTips = function(){
30507     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30508     var ce, bd, xy, dd;
30509     var visible = false, disabled = true, inited = false;
30510     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30511     
30512     var onOver = function(e){
30513         if(disabled){
30514             return;
30515         }
30516         var t = e.getTarget();
30517         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30518             return;
30519         }
30520         if(ce && t == ce.el){
30521             clearTimeout(hideProc);
30522             return;
30523         }
30524         if(t && tagEls[t.id]){
30525             tagEls[t.id].el = t;
30526             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30527             return;
30528         }
30529         var ttp, et = Roo.fly(t);
30530         var ns = cfg.namespace;
30531         if(tm.interceptTitles && t.title){
30532             ttp = t.title;
30533             t.qtip = ttp;
30534             t.removeAttribute("title");
30535             e.preventDefault();
30536         }else{
30537             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30538         }
30539         if(ttp){
30540             showProc = show.defer(tm.showDelay, tm, [{
30541                 el: t, 
30542                 text: ttp, 
30543                 width: et.getAttributeNS(ns, cfg.width),
30544                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30545                 title: et.getAttributeNS(ns, cfg.title),
30546                     cls: et.getAttributeNS(ns, cfg.cls)
30547             }]);
30548         }
30549     };
30550     
30551     var onOut = function(e){
30552         clearTimeout(showProc);
30553         var t = e.getTarget();
30554         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30555             hideProc = setTimeout(hide, tm.hideDelay);
30556         }
30557     };
30558     
30559     var onMove = function(e){
30560         if(disabled){
30561             return;
30562         }
30563         xy = e.getXY();
30564         xy[1] += 18;
30565         if(tm.trackMouse && ce){
30566             el.setXY(xy);
30567         }
30568     };
30569     
30570     var onDown = function(e){
30571         clearTimeout(showProc);
30572         clearTimeout(hideProc);
30573         if(!e.within(el)){
30574             if(tm.hideOnClick){
30575                 hide();
30576                 tm.disable();
30577                 tm.enable.defer(100, tm);
30578             }
30579         }
30580     };
30581     
30582     var getPad = function(){
30583         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30584     };
30585
30586     var show = function(o){
30587         if(disabled){
30588             return;
30589         }
30590         clearTimeout(dismissProc);
30591         ce = o;
30592         if(removeCls){ // in case manually hidden
30593             el.removeClass(removeCls);
30594             removeCls = null;
30595         }
30596         if(ce.cls){
30597             el.addClass(ce.cls);
30598             removeCls = ce.cls;
30599         }
30600         if(ce.title){
30601             tipTitle.update(ce.title);
30602             tipTitle.show();
30603         }else{
30604             tipTitle.update('');
30605             tipTitle.hide();
30606         }
30607         el.dom.style.width  = tm.maxWidth+'px';
30608         //tipBody.dom.style.width = '';
30609         tipBodyText.update(o.text);
30610         var p = getPad(), w = ce.width;
30611         if(!w){
30612             var td = tipBodyText.dom;
30613             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30614             if(aw > tm.maxWidth){
30615                 w = tm.maxWidth;
30616             }else if(aw < tm.minWidth){
30617                 w = tm.minWidth;
30618             }else{
30619                 w = aw;
30620             }
30621         }
30622         //tipBody.setWidth(w);
30623         el.setWidth(parseInt(w, 10) + p);
30624         if(ce.autoHide === false){
30625             close.setDisplayed(true);
30626             if(dd){
30627                 dd.unlock();
30628             }
30629         }else{
30630             close.setDisplayed(false);
30631             if(dd){
30632                 dd.lock();
30633             }
30634         }
30635         if(xy){
30636             el.avoidY = xy[1]-18;
30637             el.setXY(xy);
30638         }
30639         if(tm.animate){
30640             el.setOpacity(.1);
30641             el.setStyle("visibility", "visible");
30642             el.fadeIn({callback: afterShow});
30643         }else{
30644             afterShow();
30645         }
30646     };
30647     
30648     var afterShow = function(){
30649         if(ce){
30650             el.show();
30651             esc.enable();
30652             if(tm.autoDismiss && ce.autoHide !== false){
30653                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30654             }
30655         }
30656     };
30657     
30658     var hide = function(noanim){
30659         clearTimeout(dismissProc);
30660         clearTimeout(hideProc);
30661         ce = null;
30662         if(el.isVisible()){
30663             esc.disable();
30664             if(noanim !== true && tm.animate){
30665                 el.fadeOut({callback: afterHide});
30666             }else{
30667                 afterHide();
30668             } 
30669         }
30670     };
30671     
30672     var afterHide = function(){
30673         el.hide();
30674         if(removeCls){
30675             el.removeClass(removeCls);
30676             removeCls = null;
30677         }
30678     };
30679     
30680     return {
30681         /**
30682         * @cfg {Number} minWidth
30683         * The minimum width of the quick tip (defaults to 40)
30684         */
30685        minWidth : 40,
30686         /**
30687         * @cfg {Number} maxWidth
30688         * The maximum width of the quick tip (defaults to 300)
30689         */
30690        maxWidth : 300,
30691         /**
30692         * @cfg {Boolean} interceptTitles
30693         * True to automatically use the element's DOM title value if available (defaults to false)
30694         */
30695        interceptTitles : false,
30696         /**
30697         * @cfg {Boolean} trackMouse
30698         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30699         */
30700        trackMouse : false,
30701         /**
30702         * @cfg {Boolean} hideOnClick
30703         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30704         */
30705        hideOnClick : true,
30706         /**
30707         * @cfg {Number} showDelay
30708         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30709         */
30710        showDelay : 500,
30711         /**
30712         * @cfg {Number} hideDelay
30713         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30714         */
30715        hideDelay : 200,
30716         /**
30717         * @cfg {Boolean} autoHide
30718         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30719         * Used in conjunction with hideDelay.
30720         */
30721        autoHide : true,
30722         /**
30723         * @cfg {Boolean}
30724         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30725         * (defaults to true).  Used in conjunction with autoDismissDelay.
30726         */
30727        autoDismiss : true,
30728         /**
30729         * @cfg {Number}
30730         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30731         */
30732        autoDismissDelay : 5000,
30733        /**
30734         * @cfg {Boolean} animate
30735         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30736         */
30737        animate : false,
30738
30739        /**
30740         * @cfg {String} title
30741         * Title text to display (defaults to '').  This can be any valid HTML markup.
30742         */
30743         title: '',
30744        /**
30745         * @cfg {String} text
30746         * Body text to display (defaults to '').  This can be any valid HTML markup.
30747         */
30748         text : '',
30749        /**
30750         * @cfg {String} cls
30751         * A CSS class to apply to the base quick tip element (defaults to '').
30752         */
30753         cls : '',
30754        /**
30755         * @cfg {Number} width
30756         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30757         * minWidth or maxWidth.
30758         */
30759         width : null,
30760
30761     /**
30762      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30763      * or display QuickTips in a page.
30764      */
30765        init : function(){
30766           tm = Roo.QuickTips;
30767           cfg = tm.tagConfig;
30768           if(!inited){
30769               if(!Roo.isReady){ // allow calling of init() before onReady
30770                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30771                   return;
30772               }
30773               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30774               el.fxDefaults = {stopFx: true};
30775               // maximum custom styling
30776               //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>');
30777               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>');              
30778               tipTitle = el.child('h3');
30779               tipTitle.enableDisplayMode("block");
30780               tipBody = el.child('div.x-tip-bd');
30781               tipBodyText = el.child('div.x-tip-bd-inner');
30782               //bdLeft = el.child('div.x-tip-bd-left');
30783               //bdRight = el.child('div.x-tip-bd-right');
30784               close = el.child('div.x-tip-close');
30785               close.enableDisplayMode("block");
30786               close.on("click", hide);
30787               var d = Roo.get(document);
30788               d.on("mousedown", onDown);
30789               d.on("mouseover", onOver);
30790               d.on("mouseout", onOut);
30791               d.on("mousemove", onMove);
30792               esc = d.addKeyListener(27, hide);
30793               esc.disable();
30794               if(Roo.dd.DD){
30795                   dd = el.initDD("default", null, {
30796                       onDrag : function(){
30797                           el.sync();  
30798                       }
30799                   });
30800                   dd.setHandleElId(tipTitle.id);
30801                   dd.lock();
30802               }
30803               inited = true;
30804           }
30805           this.enable(); 
30806        },
30807
30808     /**
30809      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30810      * are supported:
30811      * <pre>
30812 Property    Type                   Description
30813 ----------  ---------------------  ------------------------------------------------------------------------
30814 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30815      * </ul>
30816      * @param {Object} config The config object
30817      */
30818        register : function(config){
30819            var cs = config instanceof Array ? config : arguments;
30820            for(var i = 0, len = cs.length; i < len; i++) {
30821                var c = cs[i];
30822                var target = c.target;
30823                if(target){
30824                    if(target instanceof Array){
30825                        for(var j = 0, jlen = target.length; j < jlen; j++){
30826                            tagEls[target[j]] = c;
30827                        }
30828                    }else{
30829                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30830                    }
30831                }
30832            }
30833        },
30834
30835     /**
30836      * Removes this quick tip from its element and destroys it.
30837      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30838      */
30839        unregister : function(el){
30840            delete tagEls[Roo.id(el)];
30841        },
30842
30843     /**
30844      * Enable this quick tip.
30845      */
30846        enable : function(){
30847            if(inited && disabled){
30848                locks.pop();
30849                if(locks.length < 1){
30850                    disabled = false;
30851                }
30852            }
30853        },
30854
30855     /**
30856      * Disable this quick tip.
30857      */
30858        disable : function(){
30859           disabled = true;
30860           clearTimeout(showProc);
30861           clearTimeout(hideProc);
30862           clearTimeout(dismissProc);
30863           if(ce){
30864               hide(true);
30865           }
30866           locks.push(1);
30867        },
30868
30869     /**
30870      * Returns true if the quick tip is enabled, else false.
30871      */
30872        isEnabled : function(){
30873             return !disabled;
30874        },
30875
30876         // private
30877        tagConfig : {
30878            namespace : "ext",
30879            attribute : "qtip",
30880            width : "width",
30881            target : "target",
30882            title : "qtitle",
30883            hide : "hide",
30884            cls : "qclass"
30885        }
30886    };
30887 }();
30888
30889 // backwards compat
30890 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30891  * Based on:
30892  * Ext JS Library 1.1.1
30893  * Copyright(c) 2006-2007, Ext JS, LLC.
30894  *
30895  * Originally Released Under LGPL - original licence link has changed is not relivant.
30896  *
30897  * Fork - LGPL
30898  * <script type="text/javascript">
30899  */
30900  
30901
30902 /**
30903  * @class Roo.tree.TreePanel
30904  * @extends Roo.data.Tree
30905
30906  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30907  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30908  * @cfg {Boolean} enableDD true to enable drag and drop
30909  * @cfg {Boolean} enableDrag true to enable just drag
30910  * @cfg {Boolean} enableDrop true to enable just drop
30911  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30912  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30913  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30914  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30915  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30916  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30917  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30918  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30919  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30920  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30921  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30922  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30923  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30924  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30925  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30926  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30927  * 
30928  * @constructor
30929  * @param {String/HTMLElement/Element} el The container element
30930  * @param {Object} config
30931  */
30932 Roo.tree.TreePanel = function(el, config){
30933     var root = false;
30934     var loader = false;
30935     if (config.root) {
30936         root = config.root;
30937         delete config.root;
30938     }
30939     if (config.loader) {
30940         loader = config.loader;
30941         delete config.loader;
30942     }
30943     
30944     Roo.apply(this, config);
30945     Roo.tree.TreePanel.superclass.constructor.call(this);
30946     this.el = Roo.get(el);
30947     this.el.addClass('x-tree');
30948     //console.log(root);
30949     if (root) {
30950         this.setRootNode( Roo.factory(root, Roo.tree));
30951     }
30952     if (loader) {
30953         this.loader = Roo.factory(loader, Roo.tree);
30954     }
30955    /**
30956     * Read-only. The id of the container element becomes this TreePanel's id.
30957     */
30958     this.id = this.el.id;
30959     this.addEvents({
30960         /**
30961         * @event beforeload
30962         * Fires before a node is loaded, return false to cancel
30963         * @param {Node} node The node being loaded
30964         */
30965         "beforeload" : true,
30966         /**
30967         * @event load
30968         * Fires when a node is loaded
30969         * @param {Node} node The node that was loaded
30970         */
30971         "load" : true,
30972         /**
30973         * @event textchange
30974         * Fires when the text for a node is changed
30975         * @param {Node} node The node
30976         * @param {String} text The new text
30977         * @param {String} oldText The old text
30978         */
30979         "textchange" : true,
30980         /**
30981         * @event beforeexpand
30982         * Fires before a node is expanded, return false to cancel.
30983         * @param {Node} node The node
30984         * @param {Boolean} deep
30985         * @param {Boolean} anim
30986         */
30987         "beforeexpand" : true,
30988         /**
30989         * @event beforecollapse
30990         * Fires before a node is collapsed, return false to cancel.
30991         * @param {Node} node The node
30992         * @param {Boolean} deep
30993         * @param {Boolean} anim
30994         */
30995         "beforecollapse" : true,
30996         /**
30997         * @event expand
30998         * Fires when a node is expanded
30999         * @param {Node} node The node
31000         */
31001         "expand" : true,
31002         /**
31003         * @event disabledchange
31004         * Fires when the disabled status of a node changes
31005         * @param {Node} node The node
31006         * @param {Boolean} disabled
31007         */
31008         "disabledchange" : true,
31009         /**
31010         * @event collapse
31011         * Fires when a node is collapsed
31012         * @param {Node} node The node
31013         */
31014         "collapse" : true,
31015         /**
31016         * @event beforeclick
31017         * Fires before click processing on a node. Return false to cancel the default action.
31018         * @param {Node} node The node
31019         * @param {Roo.EventObject} e The event object
31020         */
31021         "beforeclick":true,
31022         /**
31023         * @event checkchange
31024         * Fires when a node with a checkbox's checked property changes
31025         * @param {Node} this This node
31026         * @param {Boolean} checked
31027         */
31028         "checkchange":true,
31029         /**
31030         * @event click
31031         * Fires when a node is clicked
31032         * @param {Node} node The node
31033         * @param {Roo.EventObject} e The event object
31034         */
31035         "click":true,
31036         /**
31037         * @event dblclick
31038         * Fires when a node is double clicked
31039         * @param {Node} node The node
31040         * @param {Roo.EventObject} e The event object
31041         */
31042         "dblclick":true,
31043         /**
31044         * @event contextmenu
31045         * Fires when a node is right clicked
31046         * @param {Node} node The node
31047         * @param {Roo.EventObject} e The event object
31048         */
31049         "contextmenu":true,
31050         /**
31051         * @event beforechildrenrendered
31052         * Fires right before the child nodes for a node are rendered
31053         * @param {Node} node The node
31054         */
31055         "beforechildrenrendered":true,
31056         /**
31057         * @event startdrag
31058         * Fires when a node starts being dragged
31059         * @param {Roo.tree.TreePanel} this
31060         * @param {Roo.tree.TreeNode} node
31061         * @param {event} e The raw browser event
31062         */ 
31063        "startdrag" : true,
31064        /**
31065         * @event enddrag
31066         * Fires when a drag operation is complete
31067         * @param {Roo.tree.TreePanel} this
31068         * @param {Roo.tree.TreeNode} node
31069         * @param {event} e The raw browser event
31070         */
31071        "enddrag" : true,
31072        /**
31073         * @event dragdrop
31074         * Fires when a dragged node is dropped on a valid DD target
31075         * @param {Roo.tree.TreePanel} this
31076         * @param {Roo.tree.TreeNode} node
31077         * @param {DD} dd The dd it was dropped on
31078         * @param {event} e The raw browser event
31079         */
31080        "dragdrop" : true,
31081        /**
31082         * @event beforenodedrop
31083         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31084         * passed to handlers has the following properties:<br />
31085         * <ul style="padding:5px;padding-left:16px;">
31086         * <li>tree - The TreePanel</li>
31087         * <li>target - The node being targeted for the drop</li>
31088         * <li>data - The drag data from the drag source</li>
31089         * <li>point - The point of the drop - append, above or below</li>
31090         * <li>source - The drag source</li>
31091         * <li>rawEvent - Raw mouse event</li>
31092         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31093         * to be inserted by setting them on this object.</li>
31094         * <li>cancel - Set this to true to cancel the drop.</li>
31095         * </ul>
31096         * @param {Object} dropEvent
31097         */
31098        "beforenodedrop" : true,
31099        /**
31100         * @event nodedrop
31101         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31102         * passed to handlers has the following properties:<br />
31103         * <ul style="padding:5px;padding-left:16px;">
31104         * <li>tree - The TreePanel</li>
31105         * <li>target - The node being targeted for the drop</li>
31106         * <li>data - The drag data from the drag source</li>
31107         * <li>point - The point of the drop - append, above or below</li>
31108         * <li>source - The drag source</li>
31109         * <li>rawEvent - Raw mouse event</li>
31110         * <li>dropNode - Dropped node(s).</li>
31111         * </ul>
31112         * @param {Object} dropEvent
31113         */
31114        "nodedrop" : true,
31115         /**
31116         * @event nodedragover
31117         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31118         * passed to handlers has the following properties:<br />
31119         * <ul style="padding:5px;padding-left:16px;">
31120         * <li>tree - The TreePanel</li>
31121         * <li>target - The node being targeted for the drop</li>
31122         * <li>data - The drag data from the drag source</li>
31123         * <li>point - The point of the drop - append, above or below</li>
31124         * <li>source - The drag source</li>
31125         * <li>rawEvent - Raw mouse event</li>
31126         * <li>dropNode - Drop node(s) provided by the source.</li>
31127         * <li>cancel - Set this to true to signal drop not allowed.</li>
31128         * </ul>
31129         * @param {Object} dragOverEvent
31130         */
31131        "nodedragover" : true
31132         
31133     });
31134     if(this.singleExpand){
31135        this.on("beforeexpand", this.restrictExpand, this);
31136     }
31137     if (this.editor) {
31138         this.editor.tree = this;
31139         this.editor = Roo.factory(this.editor, Roo.tree);
31140     }
31141     
31142     if (this.selModel) {
31143         this.selModel = Roo.factory(this.selModel, Roo.tree);
31144     }
31145    
31146 };
31147 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31148     rootVisible : true,
31149     animate: Roo.enableFx,
31150     lines : true,
31151     enableDD : false,
31152     hlDrop : Roo.enableFx,
31153   
31154     renderer: false,
31155     
31156     rendererTip: false,
31157     // private
31158     restrictExpand : function(node){
31159         var p = node.parentNode;
31160         if(p){
31161             if(p.expandedChild && p.expandedChild.parentNode == p){
31162                 p.expandedChild.collapse();
31163             }
31164             p.expandedChild = node;
31165         }
31166     },
31167
31168     // private override
31169     setRootNode : function(node){
31170         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31171         if(!this.rootVisible){
31172             node.ui = new Roo.tree.RootTreeNodeUI(node);
31173         }
31174         return node;
31175     },
31176
31177     /**
31178      * Returns the container element for this TreePanel
31179      */
31180     getEl : function(){
31181         return this.el;
31182     },
31183
31184     /**
31185      * Returns the default TreeLoader for this TreePanel
31186      */
31187     getLoader : function(){
31188         return this.loader;
31189     },
31190
31191     /**
31192      * Expand all nodes
31193      */
31194     expandAll : function(){
31195         this.root.expand(true);
31196     },
31197
31198     /**
31199      * Collapse all nodes
31200      */
31201     collapseAll : function(){
31202         this.root.collapse(true);
31203     },
31204
31205     /**
31206      * Returns the selection model used by this TreePanel
31207      */
31208     getSelectionModel : function(){
31209         if(!this.selModel){
31210             this.selModel = new Roo.tree.DefaultSelectionModel();
31211         }
31212         return this.selModel;
31213     },
31214
31215     /**
31216      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31217      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31218      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31219      * @return {Array}
31220      */
31221     getChecked : function(a, startNode){
31222         startNode = startNode || this.root;
31223         var r = [];
31224         var f = function(){
31225             if(this.attributes.checked){
31226                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31227             }
31228         }
31229         startNode.cascade(f);
31230         return r;
31231     },
31232
31233     /**
31234      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31235      * @param {String} path
31236      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31237      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31238      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31239      */
31240     expandPath : function(path, attr, callback){
31241         attr = attr || "id";
31242         var keys = path.split(this.pathSeparator);
31243         var curNode = this.root;
31244         if(curNode.attributes[attr] != keys[1]){ // invalid root
31245             if(callback){
31246                 callback(false, null);
31247             }
31248             return;
31249         }
31250         var index = 1;
31251         var f = function(){
31252             if(++index == keys.length){
31253                 if(callback){
31254                     callback(true, curNode);
31255                 }
31256                 return;
31257             }
31258             var c = curNode.findChild(attr, keys[index]);
31259             if(!c){
31260                 if(callback){
31261                     callback(false, curNode);
31262                 }
31263                 return;
31264             }
31265             curNode = c;
31266             c.expand(false, false, f);
31267         };
31268         curNode.expand(false, false, f);
31269     },
31270
31271     /**
31272      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31273      * @param {String} path
31274      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31275      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31276      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31277      */
31278     selectPath : function(path, attr, callback){
31279         attr = attr || "id";
31280         var keys = path.split(this.pathSeparator);
31281         var v = keys.pop();
31282         if(keys.length > 0){
31283             var f = function(success, node){
31284                 if(success && node){
31285                     var n = node.findChild(attr, v);
31286                     if(n){
31287                         n.select();
31288                         if(callback){
31289                             callback(true, n);
31290                         }
31291                     }else if(callback){
31292                         callback(false, n);
31293                     }
31294                 }else{
31295                     if(callback){
31296                         callback(false, n);
31297                     }
31298                 }
31299             };
31300             this.expandPath(keys.join(this.pathSeparator), attr, f);
31301         }else{
31302             this.root.select();
31303             if(callback){
31304                 callback(true, this.root);
31305             }
31306         }
31307     },
31308
31309     getTreeEl : function(){
31310         return this.el;
31311     },
31312
31313     /**
31314      * Trigger rendering of this TreePanel
31315      */
31316     render : function(){
31317         if (this.innerCt) {
31318             return this; // stop it rendering more than once!!
31319         }
31320         
31321         this.innerCt = this.el.createChild({tag:"ul",
31322                cls:"x-tree-root-ct " +
31323                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31324
31325         if(this.containerScroll){
31326             Roo.dd.ScrollManager.register(this.el);
31327         }
31328         if((this.enableDD || this.enableDrop) && !this.dropZone){
31329            /**
31330             * The dropZone used by this tree if drop is enabled
31331             * @type Roo.tree.TreeDropZone
31332             */
31333              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31334                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31335            });
31336         }
31337         if((this.enableDD || this.enableDrag) && !this.dragZone){
31338            /**
31339             * The dragZone used by this tree if drag is enabled
31340             * @type Roo.tree.TreeDragZone
31341             */
31342             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31343                ddGroup: this.ddGroup || "TreeDD",
31344                scroll: this.ddScroll
31345            });
31346         }
31347         this.getSelectionModel().init(this);
31348         if (!this.root) {
31349             console.log("ROOT not set in tree");
31350             return;
31351         }
31352         this.root.render();
31353         if(!this.rootVisible){
31354             this.root.renderChildren();
31355         }
31356         return this;
31357     }
31358 });/*
31359  * Based on:
31360  * Ext JS Library 1.1.1
31361  * Copyright(c) 2006-2007, Ext JS, LLC.
31362  *
31363  * Originally Released Under LGPL - original licence link has changed is not relivant.
31364  *
31365  * Fork - LGPL
31366  * <script type="text/javascript">
31367  */
31368  
31369
31370 /**
31371  * @class Roo.tree.DefaultSelectionModel
31372  * @extends Roo.util.Observable
31373  * The default single selection for a TreePanel.
31374  */
31375 Roo.tree.DefaultSelectionModel = function(cfg){
31376    this.selNode = null;
31377    
31378    
31379    
31380    this.addEvents({
31381        /**
31382         * @event selectionchange
31383         * Fires when the selected node changes
31384         * @param {DefaultSelectionModel} this
31385         * @param {TreeNode} node the new selection
31386         */
31387        "selectionchange" : true,
31388
31389        /**
31390         * @event beforeselect
31391         * Fires before the selected node changes, return false to cancel the change
31392         * @param {DefaultSelectionModel} this
31393         * @param {TreeNode} node the new selection
31394         * @param {TreeNode} node the old selection
31395         */
31396        "beforeselect" : true
31397    });
31398    
31399     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31400 };
31401
31402 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31403     init : function(tree){
31404         this.tree = tree;
31405         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31406         tree.on("click", this.onNodeClick, this);
31407     },
31408     
31409     onNodeClick : function(node, e){
31410         if (e.ctrlKey && this.selNode == node)  {
31411             this.unselect(node);
31412             return;
31413         }
31414         this.select(node);
31415     },
31416     
31417     /**
31418      * Select a node.
31419      * @param {TreeNode} node The node to select
31420      * @return {TreeNode} The selected node
31421      */
31422     select : function(node){
31423         var last = this.selNode;
31424         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31425             if(last){
31426                 last.ui.onSelectedChange(false);
31427             }
31428             this.selNode = node;
31429             node.ui.onSelectedChange(true);
31430             this.fireEvent("selectionchange", this, node, last);
31431         }
31432         return node;
31433     },
31434     
31435     /**
31436      * Deselect a node.
31437      * @param {TreeNode} node The node to unselect
31438      */
31439     unselect : function(node){
31440         if(this.selNode == node){
31441             this.clearSelections();
31442         }    
31443     },
31444     
31445     /**
31446      * Clear all selections
31447      */
31448     clearSelections : function(){
31449         var n = this.selNode;
31450         if(n){
31451             n.ui.onSelectedChange(false);
31452             this.selNode = null;
31453             this.fireEvent("selectionchange", this, null);
31454         }
31455         return n;
31456     },
31457     
31458     /**
31459      * Get the selected node
31460      * @return {TreeNode} The selected node
31461      */
31462     getSelectedNode : function(){
31463         return this.selNode;    
31464     },
31465     
31466     /**
31467      * Returns true if the node is selected
31468      * @param {TreeNode} node The node to check
31469      * @return {Boolean}
31470      */
31471     isSelected : function(node){
31472         return this.selNode == node;  
31473     },
31474
31475     /**
31476      * Selects the node above the selected node in the tree, intelligently walking the nodes
31477      * @return TreeNode The new selection
31478      */
31479     selectPrevious : function(){
31480         var s = this.selNode || this.lastSelNode;
31481         if(!s){
31482             return null;
31483         }
31484         var ps = s.previousSibling;
31485         if(ps){
31486             if(!ps.isExpanded() || ps.childNodes.length < 1){
31487                 return this.select(ps);
31488             } else{
31489                 var lc = ps.lastChild;
31490                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31491                     lc = lc.lastChild;
31492                 }
31493                 return this.select(lc);
31494             }
31495         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31496             return this.select(s.parentNode);
31497         }
31498         return null;
31499     },
31500
31501     /**
31502      * Selects the node above the selected node in the tree, intelligently walking the nodes
31503      * @return TreeNode The new selection
31504      */
31505     selectNext : function(){
31506         var s = this.selNode || this.lastSelNode;
31507         if(!s){
31508             return null;
31509         }
31510         if(s.firstChild && s.isExpanded()){
31511              return this.select(s.firstChild);
31512          }else if(s.nextSibling){
31513              return this.select(s.nextSibling);
31514          }else if(s.parentNode){
31515             var newS = null;
31516             s.parentNode.bubble(function(){
31517                 if(this.nextSibling){
31518                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31519                     return false;
31520                 }
31521             });
31522             return newS;
31523          }
31524         return null;
31525     },
31526
31527     onKeyDown : function(e){
31528         var s = this.selNode || this.lastSelNode;
31529         // undesirable, but required
31530         var sm = this;
31531         if(!s){
31532             return;
31533         }
31534         var k = e.getKey();
31535         switch(k){
31536              case e.DOWN:
31537                  e.stopEvent();
31538                  this.selectNext();
31539              break;
31540              case e.UP:
31541                  e.stopEvent();
31542                  this.selectPrevious();
31543              break;
31544              case e.RIGHT:
31545                  e.preventDefault();
31546                  if(s.hasChildNodes()){
31547                      if(!s.isExpanded()){
31548                          s.expand();
31549                      }else if(s.firstChild){
31550                          this.select(s.firstChild, e);
31551                      }
31552                  }
31553              break;
31554              case e.LEFT:
31555                  e.preventDefault();
31556                  if(s.hasChildNodes() && s.isExpanded()){
31557                      s.collapse();
31558                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31559                      this.select(s.parentNode, e);
31560                  }
31561              break;
31562         };
31563     }
31564 });
31565
31566 /**
31567  * @class Roo.tree.MultiSelectionModel
31568  * @extends Roo.util.Observable
31569  * Multi selection for a TreePanel.
31570  */
31571 Roo.tree.MultiSelectionModel = function(){
31572    this.selNodes = [];
31573    this.selMap = {};
31574    this.addEvents({
31575        /**
31576         * @event selectionchange
31577         * Fires when the selected nodes change
31578         * @param {MultiSelectionModel} this
31579         * @param {Array} nodes Array of the selected nodes
31580         */
31581        "selectionchange" : true
31582    });
31583    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31584    
31585 };
31586
31587 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31588     init : function(tree){
31589         this.tree = tree;
31590         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31591         tree.on("click", this.onNodeClick, this);
31592     },
31593     
31594     onNodeClick : function(node, e){
31595         this.select(node, e, e.ctrlKey);
31596     },
31597     
31598     /**
31599      * Select a node.
31600      * @param {TreeNode} node The node to select
31601      * @param {EventObject} e (optional) An event associated with the selection
31602      * @param {Boolean} keepExisting True to retain existing selections
31603      * @return {TreeNode} The selected node
31604      */
31605     select : function(node, e, keepExisting){
31606         if(keepExisting !== true){
31607             this.clearSelections(true);
31608         }
31609         if(this.isSelected(node)){
31610             this.lastSelNode = node;
31611             return node;
31612         }
31613         this.selNodes.push(node);
31614         this.selMap[node.id] = node;
31615         this.lastSelNode = node;
31616         node.ui.onSelectedChange(true);
31617         this.fireEvent("selectionchange", this, this.selNodes);
31618         return node;
31619     },
31620     
31621     /**
31622      * Deselect a node.
31623      * @param {TreeNode} node The node to unselect
31624      */
31625     unselect : function(node){
31626         if(this.selMap[node.id]){
31627             node.ui.onSelectedChange(false);
31628             var sn = this.selNodes;
31629             var index = -1;
31630             if(sn.indexOf){
31631                 index = sn.indexOf(node);
31632             }else{
31633                 for(var i = 0, len = sn.length; i < len; i++){
31634                     if(sn[i] == node){
31635                         index = i;
31636                         break;
31637                     }
31638                 }
31639             }
31640             if(index != -1){
31641                 this.selNodes.splice(index, 1);
31642             }
31643             delete this.selMap[node.id];
31644             this.fireEvent("selectionchange", this, this.selNodes);
31645         }
31646     },
31647     
31648     /**
31649      * Clear all selections
31650      */
31651     clearSelections : function(suppressEvent){
31652         var sn = this.selNodes;
31653         if(sn.length > 0){
31654             for(var i = 0, len = sn.length; i < len; i++){
31655                 sn[i].ui.onSelectedChange(false);
31656             }
31657             this.selNodes = [];
31658             this.selMap = {};
31659             if(suppressEvent !== true){
31660                 this.fireEvent("selectionchange", this, this.selNodes);
31661             }
31662         }
31663     },
31664     
31665     /**
31666      * Returns true if the node is selected
31667      * @param {TreeNode} node The node to check
31668      * @return {Boolean}
31669      */
31670     isSelected : function(node){
31671         return this.selMap[node.id] ? true : false;  
31672     },
31673     
31674     /**
31675      * Returns an array of the selected nodes
31676      * @return {Array}
31677      */
31678     getSelectedNodes : function(){
31679         return this.selNodes;    
31680     },
31681
31682     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31683
31684     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31685
31686     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31687 });/*
31688  * Based on:
31689  * Ext JS Library 1.1.1
31690  * Copyright(c) 2006-2007, Ext JS, LLC.
31691  *
31692  * Originally Released Under LGPL - original licence link has changed is not relivant.
31693  *
31694  * Fork - LGPL
31695  * <script type="text/javascript">
31696  */
31697  
31698 /**
31699  * @class Roo.tree.TreeNode
31700  * @extends Roo.data.Node
31701  * @cfg {String} text The text for this node
31702  * @cfg {Boolean} expanded true to start the node expanded
31703  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31704  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31705  * @cfg {Boolean} disabled true to start the node disabled
31706  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31707  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31708  * @cfg {String} cls A css class to be added to the node
31709  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31710  * @cfg {String} href URL of the link used for the node (defaults to #)
31711  * @cfg {String} hrefTarget target frame for the link
31712  * @cfg {String} qtip An Ext QuickTip for the node
31713  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31714  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31715  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31716  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31717  * (defaults to undefined with no checkbox rendered)
31718  * @constructor
31719  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31720  */
31721 Roo.tree.TreeNode = function(attributes){
31722     attributes = attributes || {};
31723     if(typeof attributes == "string"){
31724         attributes = {text: attributes};
31725     }
31726     this.childrenRendered = false;
31727     this.rendered = false;
31728     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31729     this.expanded = attributes.expanded === true;
31730     this.isTarget = attributes.isTarget !== false;
31731     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31732     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31733
31734     /**
31735      * Read-only. The text for this node. To change it use setText().
31736      * @type String
31737      */
31738     this.text = attributes.text;
31739     /**
31740      * True if this node is disabled.
31741      * @type Boolean
31742      */
31743     this.disabled = attributes.disabled === true;
31744
31745     this.addEvents({
31746         /**
31747         * @event textchange
31748         * Fires when the text for this node is changed
31749         * @param {Node} this This node
31750         * @param {String} text The new text
31751         * @param {String} oldText The old text
31752         */
31753         "textchange" : true,
31754         /**
31755         * @event beforeexpand
31756         * Fires before this node is expanded, return false to cancel.
31757         * @param {Node} this This node
31758         * @param {Boolean} deep
31759         * @param {Boolean} anim
31760         */
31761         "beforeexpand" : true,
31762         /**
31763         * @event beforecollapse
31764         * Fires before this node is collapsed, return false to cancel.
31765         * @param {Node} this This node
31766         * @param {Boolean} deep
31767         * @param {Boolean} anim
31768         */
31769         "beforecollapse" : true,
31770         /**
31771         * @event expand
31772         * Fires when this node is expanded
31773         * @param {Node} this This node
31774         */
31775         "expand" : true,
31776         /**
31777         * @event disabledchange
31778         * Fires when the disabled status of this node changes
31779         * @param {Node} this This node
31780         * @param {Boolean} disabled
31781         */
31782         "disabledchange" : true,
31783         /**
31784         * @event collapse
31785         * Fires when this node is collapsed
31786         * @param {Node} this This node
31787         */
31788         "collapse" : true,
31789         /**
31790         * @event beforeclick
31791         * Fires before click processing. Return false to cancel the default action.
31792         * @param {Node} this This node
31793         * @param {Roo.EventObject} e The event object
31794         */
31795         "beforeclick":true,
31796         /**
31797         * @event checkchange
31798         * Fires when a node with a checkbox's checked property changes
31799         * @param {Node} this This node
31800         * @param {Boolean} checked
31801         */
31802         "checkchange":true,
31803         /**
31804         * @event click
31805         * Fires when this node is clicked
31806         * @param {Node} this This node
31807         * @param {Roo.EventObject} e The event object
31808         */
31809         "click":true,
31810         /**
31811         * @event dblclick
31812         * Fires when this node is double clicked
31813         * @param {Node} this This node
31814         * @param {Roo.EventObject} e The event object
31815         */
31816         "dblclick":true,
31817         /**
31818         * @event contextmenu
31819         * Fires when this node is right clicked
31820         * @param {Node} this This node
31821         * @param {Roo.EventObject} e The event object
31822         */
31823         "contextmenu":true,
31824         /**
31825         * @event beforechildrenrendered
31826         * Fires right before the child nodes for this node are rendered
31827         * @param {Node} this This node
31828         */
31829         "beforechildrenrendered":true
31830     });
31831
31832     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31833
31834     /**
31835      * Read-only. The UI for this node
31836      * @type TreeNodeUI
31837      */
31838     this.ui = new uiClass(this);
31839 };
31840 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31841     preventHScroll: true,
31842     /**
31843      * Returns true if this node is expanded
31844      * @return {Boolean}
31845      */
31846     isExpanded : function(){
31847         return this.expanded;
31848     },
31849
31850     /**
31851      * Returns the UI object for this node
31852      * @return {TreeNodeUI}
31853      */
31854     getUI : function(){
31855         return this.ui;
31856     },
31857
31858     // private override
31859     setFirstChild : function(node){
31860         var of = this.firstChild;
31861         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31862         if(this.childrenRendered && of && node != of){
31863             of.renderIndent(true, true);
31864         }
31865         if(this.rendered){
31866             this.renderIndent(true, true);
31867         }
31868     },
31869
31870     // private override
31871     setLastChild : function(node){
31872         var ol = this.lastChild;
31873         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31874         if(this.childrenRendered && ol && node != ol){
31875             ol.renderIndent(true, true);
31876         }
31877         if(this.rendered){
31878             this.renderIndent(true, true);
31879         }
31880     },
31881
31882     // these methods are overridden to provide lazy rendering support
31883     // private override
31884     appendChild : function(){
31885         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31886         if(node && this.childrenRendered){
31887             node.render();
31888         }
31889         this.ui.updateExpandIcon();
31890         return node;
31891     },
31892
31893     // private override
31894     removeChild : function(node){
31895         this.ownerTree.getSelectionModel().unselect(node);
31896         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31897         // if it's been rendered remove dom node
31898         if(this.childrenRendered){
31899             node.ui.remove();
31900         }
31901         if(this.childNodes.length < 1){
31902             this.collapse(false, false);
31903         }else{
31904             this.ui.updateExpandIcon();
31905         }
31906         if(!this.firstChild) {
31907             this.childrenRendered = false;
31908         }
31909         return node;
31910     },
31911
31912     // private override
31913     insertBefore : function(node, refNode){
31914         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31915         if(newNode && refNode && this.childrenRendered){
31916             node.render();
31917         }
31918         this.ui.updateExpandIcon();
31919         return newNode;
31920     },
31921
31922     /**
31923      * Sets the text for this node
31924      * @param {String} text
31925      */
31926     setText : function(text){
31927         var oldText = this.text;
31928         this.text = text;
31929         this.attributes.text = text;
31930         if(this.rendered){ // event without subscribing
31931             this.ui.onTextChange(this, text, oldText);
31932         }
31933         this.fireEvent("textchange", this, text, oldText);
31934     },
31935
31936     /**
31937      * Triggers selection of this node
31938      */
31939     select : function(){
31940         this.getOwnerTree().getSelectionModel().select(this);
31941     },
31942
31943     /**
31944      * Triggers deselection of this node
31945      */
31946     unselect : function(){
31947         this.getOwnerTree().getSelectionModel().unselect(this);
31948     },
31949
31950     /**
31951      * Returns true if this node is selected
31952      * @return {Boolean}
31953      */
31954     isSelected : function(){
31955         return this.getOwnerTree().getSelectionModel().isSelected(this);
31956     },
31957
31958     /**
31959      * Expand this node.
31960      * @param {Boolean} deep (optional) True to expand all children as well
31961      * @param {Boolean} anim (optional) false to cancel the default animation
31962      * @param {Function} callback (optional) A callback to be called when
31963      * expanding this node completes (does not wait for deep expand to complete).
31964      * Called with 1 parameter, this node.
31965      */
31966     expand : function(deep, anim, callback){
31967         if(!this.expanded){
31968             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31969                 return;
31970             }
31971             if(!this.childrenRendered){
31972                 this.renderChildren();
31973             }
31974             this.expanded = true;
31975             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31976                 this.ui.animExpand(function(){
31977                     this.fireEvent("expand", this);
31978                     if(typeof callback == "function"){
31979                         callback(this);
31980                     }
31981                     if(deep === true){
31982                         this.expandChildNodes(true);
31983                     }
31984                 }.createDelegate(this));
31985                 return;
31986             }else{
31987                 this.ui.expand();
31988                 this.fireEvent("expand", this);
31989                 if(typeof callback == "function"){
31990                     callback(this);
31991                 }
31992             }
31993         }else{
31994            if(typeof callback == "function"){
31995                callback(this);
31996            }
31997         }
31998         if(deep === true){
31999             this.expandChildNodes(true);
32000         }
32001     },
32002
32003     isHiddenRoot : function(){
32004         return this.isRoot && !this.getOwnerTree().rootVisible;
32005     },
32006
32007     /**
32008      * Collapse this node.
32009      * @param {Boolean} deep (optional) True to collapse all children as well
32010      * @param {Boolean} anim (optional) false to cancel the default animation
32011      */
32012     collapse : function(deep, anim){
32013         if(this.expanded && !this.isHiddenRoot()){
32014             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32015                 return;
32016             }
32017             this.expanded = false;
32018             if((this.getOwnerTree().animate && anim !== false) || anim){
32019                 this.ui.animCollapse(function(){
32020                     this.fireEvent("collapse", this);
32021                     if(deep === true){
32022                         this.collapseChildNodes(true);
32023                     }
32024                 }.createDelegate(this));
32025                 return;
32026             }else{
32027                 this.ui.collapse();
32028                 this.fireEvent("collapse", this);
32029             }
32030         }
32031         if(deep === true){
32032             var cs = this.childNodes;
32033             for(var i = 0, len = cs.length; i < len; i++) {
32034                 cs[i].collapse(true, false);
32035             }
32036         }
32037     },
32038
32039     // private
32040     delayedExpand : function(delay){
32041         if(!this.expandProcId){
32042             this.expandProcId = this.expand.defer(delay, this);
32043         }
32044     },
32045
32046     // private
32047     cancelExpand : function(){
32048         if(this.expandProcId){
32049             clearTimeout(this.expandProcId);
32050         }
32051         this.expandProcId = false;
32052     },
32053
32054     /**
32055      * Toggles expanded/collapsed state of the node
32056      */
32057     toggle : function(){
32058         if(this.expanded){
32059             this.collapse();
32060         }else{
32061             this.expand();
32062         }
32063     },
32064
32065     /**
32066      * Ensures all parent nodes are expanded
32067      */
32068     ensureVisible : function(callback){
32069         var tree = this.getOwnerTree();
32070         tree.expandPath(this.parentNode.getPath(), false, function(){
32071             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32072             Roo.callback(callback);
32073         }.createDelegate(this));
32074     },
32075
32076     /**
32077      * Expand all child nodes
32078      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32079      */
32080     expandChildNodes : function(deep){
32081         var cs = this.childNodes;
32082         for(var i = 0, len = cs.length; i < len; i++) {
32083                 cs[i].expand(deep);
32084         }
32085     },
32086
32087     /**
32088      * Collapse all child nodes
32089      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32090      */
32091     collapseChildNodes : function(deep){
32092         var cs = this.childNodes;
32093         for(var i = 0, len = cs.length; i < len; i++) {
32094                 cs[i].collapse(deep);
32095         }
32096     },
32097
32098     /**
32099      * Disables this node
32100      */
32101     disable : function(){
32102         this.disabled = true;
32103         this.unselect();
32104         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32105             this.ui.onDisableChange(this, true);
32106         }
32107         this.fireEvent("disabledchange", this, true);
32108     },
32109
32110     /**
32111      * Enables this node
32112      */
32113     enable : function(){
32114         this.disabled = false;
32115         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32116             this.ui.onDisableChange(this, false);
32117         }
32118         this.fireEvent("disabledchange", this, false);
32119     },
32120
32121     // private
32122     renderChildren : function(suppressEvent){
32123         if(suppressEvent !== false){
32124             this.fireEvent("beforechildrenrendered", this);
32125         }
32126         var cs = this.childNodes;
32127         for(var i = 0, len = cs.length; i < len; i++){
32128             cs[i].render(true);
32129         }
32130         this.childrenRendered = true;
32131     },
32132
32133     // private
32134     sort : function(fn, scope){
32135         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32136         if(this.childrenRendered){
32137             var cs = this.childNodes;
32138             for(var i = 0, len = cs.length; i < len; i++){
32139                 cs[i].render(true);
32140             }
32141         }
32142     },
32143
32144     // private
32145     render : function(bulkRender){
32146         this.ui.render(bulkRender);
32147         if(!this.rendered){
32148             this.rendered = true;
32149             if(this.expanded){
32150                 this.expanded = false;
32151                 this.expand(false, false);
32152             }
32153         }
32154     },
32155
32156     // private
32157     renderIndent : function(deep, refresh){
32158         if(refresh){
32159             this.ui.childIndent = null;
32160         }
32161         this.ui.renderIndent();
32162         if(deep === true && this.childrenRendered){
32163             var cs = this.childNodes;
32164             for(var i = 0, len = cs.length; i < len; i++){
32165                 cs[i].renderIndent(true, refresh);
32166             }
32167         }
32168     }
32169 });/*
32170  * Based on:
32171  * Ext JS Library 1.1.1
32172  * Copyright(c) 2006-2007, Ext JS, LLC.
32173  *
32174  * Originally Released Under LGPL - original licence link has changed is not relivant.
32175  *
32176  * Fork - LGPL
32177  * <script type="text/javascript">
32178  */
32179  
32180 /**
32181  * @class Roo.tree.AsyncTreeNode
32182  * @extends Roo.tree.TreeNode
32183  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32184  * @constructor
32185  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32186  */
32187  Roo.tree.AsyncTreeNode = function(config){
32188     this.loaded = false;
32189     this.loading = false;
32190     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32191     /**
32192     * @event beforeload
32193     * Fires before this node is loaded, return false to cancel
32194     * @param {Node} this This node
32195     */
32196     this.addEvents({'beforeload':true, 'load': true});
32197     /**
32198     * @event load
32199     * Fires when this node is loaded
32200     * @param {Node} this This node
32201     */
32202     /**
32203      * The loader used by this node (defaults to using the tree's defined loader)
32204      * @type TreeLoader
32205      * @property loader
32206      */
32207 };
32208 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32209     expand : function(deep, anim, callback){
32210         if(this.loading){ // if an async load is already running, waiting til it's done
32211             var timer;
32212             var f = function(){
32213                 if(!this.loading){ // done loading
32214                     clearInterval(timer);
32215                     this.expand(deep, anim, callback);
32216                 }
32217             }.createDelegate(this);
32218             timer = setInterval(f, 200);
32219             return;
32220         }
32221         if(!this.loaded){
32222             if(this.fireEvent("beforeload", this) === false){
32223                 return;
32224             }
32225             this.loading = true;
32226             this.ui.beforeLoad(this);
32227             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32228             if(loader){
32229                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32230                 return;
32231             }
32232         }
32233         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32234     },
32235     
32236     /**
32237      * Returns true if this node is currently loading
32238      * @return {Boolean}
32239      */
32240     isLoading : function(){
32241         return this.loading;  
32242     },
32243     
32244     loadComplete : function(deep, anim, callback){
32245         this.loading = false;
32246         this.loaded = true;
32247         this.ui.afterLoad(this);
32248         this.fireEvent("load", this);
32249         this.expand(deep, anim, callback);
32250     },
32251     
32252     /**
32253      * Returns true if this node has been loaded
32254      * @return {Boolean}
32255      */
32256     isLoaded : function(){
32257         return this.loaded;
32258     },
32259     
32260     hasChildNodes : function(){
32261         if(!this.isLeaf() && !this.loaded){
32262             return true;
32263         }else{
32264             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32265         }
32266     },
32267
32268     /**
32269      * Trigger a reload for this node
32270      * @param {Function} callback
32271      */
32272     reload : function(callback){
32273         this.collapse(false, false);
32274         while(this.firstChild){
32275             this.removeChild(this.firstChild);
32276         }
32277         this.childrenRendered = false;
32278         this.loaded = false;
32279         if(this.isHiddenRoot()){
32280             this.expanded = false;
32281         }
32282         this.expand(false, false, callback);
32283     }
32284 });/*
32285  * Based on:
32286  * Ext JS Library 1.1.1
32287  * Copyright(c) 2006-2007, Ext JS, LLC.
32288  *
32289  * Originally Released Under LGPL - original licence link has changed is not relivant.
32290  *
32291  * Fork - LGPL
32292  * <script type="text/javascript">
32293  */
32294  
32295 /**
32296  * @class Roo.tree.TreeNodeUI
32297  * @constructor
32298  * @param {Object} node The node to render
32299  * The TreeNode UI implementation is separate from the
32300  * tree implementation. Unless you are customizing the tree UI,
32301  * you should never have to use this directly.
32302  */
32303 Roo.tree.TreeNodeUI = function(node){
32304     this.node = node;
32305     this.rendered = false;
32306     this.animating = false;
32307     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32308 };
32309
32310 Roo.tree.TreeNodeUI.prototype = {
32311     removeChild : function(node){
32312         if(this.rendered){
32313             this.ctNode.removeChild(node.ui.getEl());
32314         }
32315     },
32316
32317     beforeLoad : function(){
32318          this.addClass("x-tree-node-loading");
32319     },
32320
32321     afterLoad : function(){
32322          this.removeClass("x-tree-node-loading");
32323     },
32324
32325     onTextChange : function(node, text, oldText){
32326         if(this.rendered){
32327             this.textNode.innerHTML = text;
32328         }
32329     },
32330
32331     onDisableChange : function(node, state){
32332         this.disabled = state;
32333         if(state){
32334             this.addClass("x-tree-node-disabled");
32335         }else{
32336             this.removeClass("x-tree-node-disabled");
32337         }
32338     },
32339
32340     onSelectedChange : function(state){
32341         if(state){
32342             this.focus();
32343             this.addClass("x-tree-selected");
32344         }else{
32345             //this.blur();
32346             this.removeClass("x-tree-selected");
32347         }
32348     },
32349
32350     onMove : function(tree, node, oldParent, newParent, index, refNode){
32351         this.childIndent = null;
32352         if(this.rendered){
32353             var targetNode = newParent.ui.getContainer();
32354             if(!targetNode){//target not rendered
32355                 this.holder = document.createElement("div");
32356                 this.holder.appendChild(this.wrap);
32357                 return;
32358             }
32359             var insertBefore = refNode ? refNode.ui.getEl() : null;
32360             if(insertBefore){
32361                 targetNode.insertBefore(this.wrap, insertBefore);
32362             }else{
32363                 targetNode.appendChild(this.wrap);
32364             }
32365             this.node.renderIndent(true);
32366         }
32367     },
32368
32369     addClass : function(cls){
32370         if(this.elNode){
32371             Roo.fly(this.elNode).addClass(cls);
32372         }
32373     },
32374
32375     removeClass : function(cls){
32376         if(this.elNode){
32377             Roo.fly(this.elNode).removeClass(cls);
32378         }
32379     },
32380
32381     remove : function(){
32382         if(this.rendered){
32383             this.holder = document.createElement("div");
32384             this.holder.appendChild(this.wrap);
32385         }
32386     },
32387
32388     fireEvent : function(){
32389         return this.node.fireEvent.apply(this.node, arguments);
32390     },
32391
32392     initEvents : function(){
32393         this.node.on("move", this.onMove, this);
32394         var E = Roo.EventManager;
32395         var a = this.anchor;
32396
32397         var el = Roo.fly(a, '_treeui');
32398
32399         if(Roo.isOpera){ // opera render bug ignores the CSS
32400             el.setStyle("text-decoration", "none");
32401         }
32402
32403         el.on("click", this.onClick, this);
32404         el.on("dblclick", this.onDblClick, this);
32405
32406         if(this.checkbox){
32407             Roo.EventManager.on(this.checkbox,
32408                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32409         }
32410
32411         el.on("contextmenu", this.onContextMenu, this);
32412
32413         var icon = Roo.fly(this.iconNode);
32414         icon.on("click", this.onClick, this);
32415         icon.on("dblclick", this.onDblClick, this);
32416         icon.on("contextmenu", this.onContextMenu, this);
32417         E.on(this.ecNode, "click", this.ecClick, this, true);
32418
32419         if(this.node.disabled){
32420             this.addClass("x-tree-node-disabled");
32421         }
32422         if(this.node.hidden){
32423             this.addClass("x-tree-node-disabled");
32424         }
32425         var ot = this.node.getOwnerTree();
32426         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32427         if(dd && (!this.node.isRoot || ot.rootVisible)){
32428             Roo.dd.Registry.register(this.elNode, {
32429                 node: this.node,
32430                 handles: this.getDDHandles(),
32431                 isHandle: false
32432             });
32433         }
32434     },
32435
32436     getDDHandles : function(){
32437         return [this.iconNode, this.textNode];
32438     },
32439
32440     hide : function(){
32441         if(this.rendered){
32442             this.wrap.style.display = "none";
32443         }
32444     },
32445
32446     show : function(){
32447         if(this.rendered){
32448             this.wrap.style.display = "";
32449         }
32450     },
32451
32452     onContextMenu : function(e){
32453         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32454             e.preventDefault();
32455             this.focus();
32456             this.fireEvent("contextmenu", this.node, e);
32457         }
32458     },
32459
32460     onClick : function(e){
32461         if(this.dropping){
32462             e.stopEvent();
32463             return;
32464         }
32465         if(this.fireEvent("beforeclick", this.node, e) !== false){
32466             if(!this.disabled && this.node.attributes.href){
32467                 this.fireEvent("click", this.node, e);
32468                 return;
32469             }
32470             e.preventDefault();
32471             if(this.disabled){
32472                 return;
32473             }
32474
32475             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32476                 this.node.toggle();
32477             }
32478
32479             this.fireEvent("click", this.node, e);
32480         }else{
32481             e.stopEvent();
32482         }
32483     },
32484
32485     onDblClick : function(e){
32486         e.preventDefault();
32487         if(this.disabled){
32488             return;
32489         }
32490         if(this.checkbox){
32491             this.toggleCheck();
32492         }
32493         if(!this.animating && this.node.hasChildNodes()){
32494             this.node.toggle();
32495         }
32496         this.fireEvent("dblclick", this.node, e);
32497     },
32498
32499     onCheckChange : function(){
32500         var checked = this.checkbox.checked;
32501         this.node.attributes.checked = checked;
32502         this.fireEvent('checkchange', this.node, checked);
32503     },
32504
32505     ecClick : function(e){
32506         if(!this.animating && this.node.hasChildNodes()){
32507             this.node.toggle();
32508         }
32509     },
32510
32511     startDrop : function(){
32512         this.dropping = true;
32513     },
32514
32515     // delayed drop so the click event doesn't get fired on a drop
32516     endDrop : function(){
32517        setTimeout(function(){
32518            this.dropping = false;
32519        }.createDelegate(this), 50);
32520     },
32521
32522     expand : function(){
32523         this.updateExpandIcon();
32524         this.ctNode.style.display = "";
32525     },
32526
32527     focus : function(){
32528         if(!this.node.preventHScroll){
32529             try{this.anchor.focus();
32530             }catch(e){}
32531         }else if(!Roo.isIE){
32532             try{
32533                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32534                 var l = noscroll.scrollLeft;
32535                 this.anchor.focus();
32536                 noscroll.scrollLeft = l;
32537             }catch(e){}
32538         }
32539     },
32540
32541     toggleCheck : function(value){
32542         var cb = this.checkbox;
32543         if(cb){
32544             cb.checked = (value === undefined ? !cb.checked : value);
32545         }
32546     },
32547
32548     blur : function(){
32549         try{
32550             this.anchor.blur();
32551         }catch(e){}
32552     },
32553
32554     animExpand : function(callback){
32555         var ct = Roo.get(this.ctNode);
32556         ct.stopFx();
32557         if(!this.node.hasChildNodes()){
32558             this.updateExpandIcon();
32559             this.ctNode.style.display = "";
32560             Roo.callback(callback);
32561             return;
32562         }
32563         this.animating = true;
32564         this.updateExpandIcon();
32565
32566         ct.slideIn('t', {
32567            callback : function(){
32568                this.animating = false;
32569                Roo.callback(callback);
32570             },
32571             scope: this,
32572             duration: this.node.ownerTree.duration || .25
32573         });
32574     },
32575
32576     highlight : function(){
32577         var tree = this.node.getOwnerTree();
32578         Roo.fly(this.wrap).highlight(
32579             tree.hlColor || "C3DAF9",
32580             {endColor: tree.hlBaseColor}
32581         );
32582     },
32583
32584     collapse : function(){
32585         this.updateExpandIcon();
32586         this.ctNode.style.display = "none";
32587     },
32588
32589     animCollapse : function(callback){
32590         var ct = Roo.get(this.ctNode);
32591         ct.enableDisplayMode('block');
32592         ct.stopFx();
32593
32594         this.animating = true;
32595         this.updateExpandIcon();
32596
32597         ct.slideOut('t', {
32598             callback : function(){
32599                this.animating = false;
32600                Roo.callback(callback);
32601             },
32602             scope: this,
32603             duration: this.node.ownerTree.duration || .25
32604         });
32605     },
32606
32607     getContainer : function(){
32608         return this.ctNode;
32609     },
32610
32611     getEl : function(){
32612         return this.wrap;
32613     },
32614
32615     appendDDGhost : function(ghostNode){
32616         ghostNode.appendChild(this.elNode.cloneNode(true));
32617     },
32618
32619     getDDRepairXY : function(){
32620         return Roo.lib.Dom.getXY(this.iconNode);
32621     },
32622
32623     onRender : function(){
32624         this.render();
32625     },
32626
32627     render : function(bulkRender){
32628         var n = this.node, a = n.attributes;
32629         var targetNode = n.parentNode ?
32630               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32631
32632         if(!this.rendered){
32633             this.rendered = true;
32634
32635             this.renderElements(n, a, targetNode, bulkRender);
32636
32637             if(a.qtip){
32638                if(this.textNode.setAttributeNS){
32639                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32640                    if(a.qtipTitle){
32641                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32642                    }
32643                }else{
32644                    this.textNode.setAttribute("ext:qtip", a.qtip);
32645                    if(a.qtipTitle){
32646                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32647                    }
32648                }
32649             }else if(a.qtipCfg){
32650                 a.qtipCfg.target = Roo.id(this.textNode);
32651                 Roo.QuickTips.register(a.qtipCfg);
32652             }
32653             this.initEvents();
32654             if(!this.node.expanded){
32655                 this.updateExpandIcon();
32656             }
32657         }else{
32658             if(bulkRender === true) {
32659                 targetNode.appendChild(this.wrap);
32660             }
32661         }
32662     },
32663
32664     renderElements : function(n, a, targetNode, bulkRender)
32665     {
32666         // add some indent caching, this helps performance when rendering a large tree
32667         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32668         var t = n.getOwnerTree();
32669         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32670         if (typeof(n.attributes.html) != 'undefined') {
32671             txt = n.attributes.html;
32672         }
32673         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32674         var cb = typeof a.checked == 'boolean';
32675         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32676         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32677             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32678             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32679             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32680             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32681             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32682              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32683                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32684             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32685             "</li>"];
32686
32687         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32688             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32689                                 n.nextSibling.ui.getEl(), buf.join(""));
32690         }else{
32691             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32692         }
32693
32694         this.elNode = this.wrap.childNodes[0];
32695         this.ctNode = this.wrap.childNodes[1];
32696         var cs = this.elNode.childNodes;
32697         this.indentNode = cs[0];
32698         this.ecNode = cs[1];
32699         this.iconNode = cs[2];
32700         var index = 3;
32701         if(cb){
32702             this.checkbox = cs[3];
32703             index++;
32704         }
32705         this.anchor = cs[index];
32706         this.textNode = cs[index].firstChild;
32707     },
32708
32709     getAnchor : function(){
32710         return this.anchor;
32711     },
32712
32713     getTextEl : function(){
32714         return this.textNode;
32715     },
32716
32717     getIconEl : function(){
32718         return this.iconNode;
32719     },
32720
32721     isChecked : function(){
32722         return this.checkbox ? this.checkbox.checked : false;
32723     },
32724
32725     updateExpandIcon : function(){
32726         if(this.rendered){
32727             var n = this.node, c1, c2;
32728             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32729             var hasChild = n.hasChildNodes();
32730             if(hasChild){
32731                 if(n.expanded){
32732                     cls += "-minus";
32733                     c1 = "x-tree-node-collapsed";
32734                     c2 = "x-tree-node-expanded";
32735                 }else{
32736                     cls += "-plus";
32737                     c1 = "x-tree-node-expanded";
32738                     c2 = "x-tree-node-collapsed";
32739                 }
32740                 if(this.wasLeaf){
32741                     this.removeClass("x-tree-node-leaf");
32742                     this.wasLeaf = false;
32743                 }
32744                 if(this.c1 != c1 || this.c2 != c2){
32745                     Roo.fly(this.elNode).replaceClass(c1, c2);
32746                     this.c1 = c1; this.c2 = c2;
32747                 }
32748             }else{
32749                 // this changes non-leafs into leafs if they have no children.
32750                 // it's not very rational behaviour..
32751                 
32752                 if(!this.wasLeaf && this.node.leaf){
32753                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32754                     delete this.c1;
32755                     delete this.c2;
32756                     this.wasLeaf = true;
32757                 }
32758             }
32759             var ecc = "x-tree-ec-icon "+cls;
32760             if(this.ecc != ecc){
32761                 this.ecNode.className = ecc;
32762                 this.ecc = ecc;
32763             }
32764         }
32765     },
32766
32767     getChildIndent : function(){
32768         if(!this.childIndent){
32769             var buf = [];
32770             var p = this.node;
32771             while(p){
32772                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32773                     if(!p.isLast()) {
32774                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32775                     } else {
32776                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32777                     }
32778                 }
32779                 p = p.parentNode;
32780             }
32781             this.childIndent = buf.join("");
32782         }
32783         return this.childIndent;
32784     },
32785
32786     renderIndent : function(){
32787         if(this.rendered){
32788             var indent = "";
32789             var p = this.node.parentNode;
32790             if(p){
32791                 indent = p.ui.getChildIndent();
32792             }
32793             if(this.indentMarkup != indent){ // don't rerender if not required
32794                 this.indentNode.innerHTML = indent;
32795                 this.indentMarkup = indent;
32796             }
32797             this.updateExpandIcon();
32798         }
32799     }
32800 };
32801
32802 Roo.tree.RootTreeNodeUI = function(){
32803     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32804 };
32805 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32806     render : function(){
32807         if(!this.rendered){
32808             var targetNode = this.node.ownerTree.innerCt.dom;
32809             this.node.expanded = true;
32810             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32811             this.wrap = this.ctNode = targetNode.firstChild;
32812         }
32813     },
32814     collapse : function(){
32815     },
32816     expand : function(){
32817     }
32818 });/*
32819  * Based on:
32820  * Ext JS Library 1.1.1
32821  * Copyright(c) 2006-2007, Ext JS, LLC.
32822  *
32823  * Originally Released Under LGPL - original licence link has changed is not relivant.
32824  *
32825  * Fork - LGPL
32826  * <script type="text/javascript">
32827  */
32828 /**
32829  * @class Roo.tree.TreeLoader
32830  * @extends Roo.util.Observable
32831  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32832  * nodes from a specified URL. The response must be a javascript Array definition
32833  * who's elements are node definition objects. eg:
32834  * <pre><code>
32835    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32836     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32837 </code></pre>
32838  * <br><br>
32839  * A server request is sent, and child nodes are loaded only when a node is expanded.
32840  * The loading node's id is passed to the server under the parameter name "node" to
32841  * enable the server to produce the correct child nodes.
32842  * <br><br>
32843  * To pass extra parameters, an event handler may be attached to the "beforeload"
32844  * event, and the parameters specified in the TreeLoader's baseParams property:
32845  * <pre><code>
32846     myTreeLoader.on("beforeload", function(treeLoader, node) {
32847         this.baseParams.category = node.attributes.category;
32848     }, this);
32849 </code></pre><
32850  * This would pass an HTTP parameter called "category" to the server containing
32851  * the value of the Node's "category" attribute.
32852  * @constructor
32853  * Creates a new Treeloader.
32854  * @param {Object} config A config object containing config properties.
32855  */
32856 Roo.tree.TreeLoader = function(config){
32857     this.baseParams = {};
32858     this.requestMethod = "POST";
32859     Roo.apply(this, config);
32860
32861     this.addEvents({
32862     
32863         /**
32864          * @event beforeload
32865          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32866          * @param {Object} This TreeLoader object.
32867          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32868          * @param {Object} callback The callback function specified in the {@link #load} call.
32869          */
32870         beforeload : true,
32871         /**
32872          * @event load
32873          * Fires when the node has been successfuly loaded.
32874          * @param {Object} This TreeLoader object.
32875          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32876          * @param {Object} response The response object containing the data from the server.
32877          */
32878         load : true,
32879         /**
32880          * @event loadexception
32881          * Fires if the network request failed.
32882          * @param {Object} This TreeLoader object.
32883          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32884          * @param {Object} response The response object containing the data from the server.
32885          */
32886         loadexception : true,
32887         /**
32888          * @event create
32889          * Fires before a node is created, enabling you to return custom Node types 
32890          * @param {Object} This TreeLoader object.
32891          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32892          */
32893         create : true
32894     });
32895
32896     Roo.tree.TreeLoader.superclass.constructor.call(this);
32897 };
32898
32899 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32900     /**
32901     * @cfg {String} dataUrl The URL from which to request a Json string which
32902     * specifies an array of node definition object representing the child nodes
32903     * to be loaded.
32904     */
32905     /**
32906     * @cfg {Object} baseParams (optional) An object containing properties which
32907     * specify HTTP parameters to be passed to each request for child nodes.
32908     */
32909     /**
32910     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32911     * created by this loader. If the attributes sent by the server have an attribute in this object,
32912     * they take priority.
32913     */
32914     /**
32915     * @cfg {Object} uiProviders (optional) An object containing properties which
32916     * 
32917     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32918     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32919     * <i>uiProvider</i> attribute of a returned child node is a string rather
32920     * than a reference to a TreeNodeUI implementation, this that string value
32921     * is used as a property name in the uiProviders object. You can define the provider named
32922     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32923     */
32924     uiProviders : {},
32925
32926     /**
32927     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32928     * child nodes before loading.
32929     */
32930     clearOnLoad : true,
32931
32932     /**
32933     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32934     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32935     * Grid query { data : [ .....] }
32936     */
32937     
32938     root : false,
32939      /**
32940     * @cfg {String} queryParam (optional) 
32941     * Name of the query as it will be passed on the querystring (defaults to 'node')
32942     * eg. the request will be ?node=[id]
32943     */
32944     
32945     
32946     queryParam: false,
32947     
32948     /**
32949      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32950      * This is called automatically when a node is expanded, but may be used to reload
32951      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32952      * @param {Roo.tree.TreeNode} node
32953      * @param {Function} callback
32954      */
32955     load : function(node, callback){
32956         if(this.clearOnLoad){
32957             while(node.firstChild){
32958                 node.removeChild(node.firstChild);
32959             }
32960         }
32961         if(node.attributes.children){ // preloaded json children
32962             var cs = node.attributes.children;
32963             for(var i = 0, len = cs.length; i < len; i++){
32964                 node.appendChild(this.createNode(cs[i]));
32965             }
32966             if(typeof callback == "function"){
32967                 callback();
32968             }
32969         }else if(this.dataUrl){
32970             this.requestData(node, callback);
32971         }
32972     },
32973
32974     getParams: function(node){
32975         var buf = [], bp = this.baseParams;
32976         for(var key in bp){
32977             if(typeof bp[key] != "function"){
32978                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32979             }
32980         }
32981         var n = this.queryParam === false ? 'node' : this.queryParam;
32982         buf.push(n + "=", encodeURIComponent(node.id));
32983         return buf.join("");
32984     },
32985
32986     requestData : function(node, callback){
32987         if(this.fireEvent("beforeload", this, node, callback) !== false){
32988             this.transId = Roo.Ajax.request({
32989                 method:this.requestMethod,
32990                 url: this.dataUrl||this.url,
32991                 success: this.handleResponse,
32992                 failure: this.handleFailure,
32993                 scope: this,
32994                 argument: {callback: callback, node: node},
32995                 params: this.getParams(node)
32996             });
32997         }else{
32998             // if the load is cancelled, make sure we notify
32999             // the node that we are done
33000             if(typeof callback == "function"){
33001                 callback();
33002             }
33003         }
33004     },
33005
33006     isLoading : function(){
33007         return this.transId ? true : false;
33008     },
33009
33010     abort : function(){
33011         if(this.isLoading()){
33012             Roo.Ajax.abort(this.transId);
33013         }
33014     },
33015
33016     // private
33017     createNode : function(attr)
33018     {
33019         // apply baseAttrs, nice idea Corey!
33020         if(this.baseAttrs){
33021             Roo.applyIf(attr, this.baseAttrs);
33022         }
33023         if(this.applyLoader !== false){
33024             attr.loader = this;
33025         }
33026         // uiProvider = depreciated..
33027         
33028         if(typeof(attr.uiProvider) == 'string'){
33029            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33030                 /**  eval:var:attr */ eval(attr.uiProvider);
33031         }
33032         if(typeof(this.uiProviders['default']) != 'undefined') {
33033             attr.uiProvider = this.uiProviders['default'];
33034         }
33035         
33036         this.fireEvent('create', this, attr);
33037         
33038         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33039         return(attr.leaf ?
33040                         new Roo.tree.TreeNode(attr) :
33041                         new Roo.tree.AsyncTreeNode(attr));
33042     },
33043
33044     processResponse : function(response, node, callback)
33045     {
33046         var json = response.responseText;
33047         try {
33048             
33049             var o = Roo.decode(json);
33050             
33051             if (!o.success) {
33052                 // it's a failure condition.
33053                 var a = response.argument;
33054                 this.fireEvent("loadexception", this, a.node, response);
33055                 Roo.log("Load failed - should have a handler really");
33056                 return;
33057             }
33058             
33059             if (this.root !== false) {
33060                 o = o[this.root];
33061             }
33062             
33063             for(var i = 0, len = o.length; i < len; i++){
33064                 var n = this.createNode(o[i]);
33065                 if(n){
33066                     node.appendChild(n);
33067                 }
33068             }
33069             if(typeof callback == "function"){
33070                 callback(this, node);
33071             }
33072         }catch(e){
33073             this.handleFailure(response);
33074         }
33075     },
33076
33077     handleResponse : function(response){
33078         this.transId = false;
33079         var a = response.argument;
33080         this.processResponse(response, a.node, a.callback);
33081         this.fireEvent("load", this, a.node, response);
33082     },
33083
33084     handleFailure : function(response)
33085     {
33086         // should handle failure better..
33087         this.transId = false;
33088         var a = response.argument;
33089         this.fireEvent("loadexception", this, a.node, response);
33090         if(typeof a.callback == "function"){
33091             a.callback(this, a.node);
33092         }
33093     }
33094 });/*
33095  * Based on:
33096  * Ext JS Library 1.1.1
33097  * Copyright(c) 2006-2007, Ext JS, LLC.
33098  *
33099  * Originally Released Under LGPL - original licence link has changed is not relivant.
33100  *
33101  * Fork - LGPL
33102  * <script type="text/javascript">
33103  */
33104
33105 /**
33106 * @class Roo.tree.TreeFilter
33107 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33108 * @param {TreePanel} tree
33109 * @param {Object} config (optional)
33110  */
33111 Roo.tree.TreeFilter = function(tree, config){
33112     this.tree = tree;
33113     this.filtered = {};
33114     Roo.apply(this, config);
33115 };
33116
33117 Roo.tree.TreeFilter.prototype = {
33118     clearBlank:false,
33119     reverse:false,
33120     autoClear:false,
33121     remove:false,
33122
33123      /**
33124      * Filter the data by a specific attribute.
33125      * @param {String/RegExp} value Either string that the attribute value
33126      * should start with or a RegExp to test against the attribute
33127      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33128      * @param {TreeNode} startNode (optional) The node to start the filter at.
33129      */
33130     filter : function(value, attr, startNode){
33131         attr = attr || "text";
33132         var f;
33133         if(typeof value == "string"){
33134             var vlen = value.length;
33135             // auto clear empty filter
33136             if(vlen == 0 && this.clearBlank){
33137                 this.clear();
33138                 return;
33139             }
33140             value = value.toLowerCase();
33141             f = function(n){
33142                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33143             };
33144         }else if(value.exec){ // regex?
33145             f = function(n){
33146                 return value.test(n.attributes[attr]);
33147             };
33148         }else{
33149             throw 'Illegal filter type, must be string or regex';
33150         }
33151         this.filterBy(f, null, startNode);
33152         },
33153
33154     /**
33155      * Filter by a function. The passed function will be called with each
33156      * node in the tree (or from the startNode). If the function returns true, the node is kept
33157      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33158      * @param {Function} fn The filter function
33159      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33160      */
33161     filterBy : function(fn, scope, startNode){
33162         startNode = startNode || this.tree.root;
33163         if(this.autoClear){
33164             this.clear();
33165         }
33166         var af = this.filtered, rv = this.reverse;
33167         var f = function(n){
33168             if(n == startNode){
33169                 return true;
33170             }
33171             if(af[n.id]){
33172                 return false;
33173             }
33174             var m = fn.call(scope || n, n);
33175             if(!m || rv){
33176                 af[n.id] = n;
33177                 n.ui.hide();
33178                 return false;
33179             }
33180             return true;
33181         };
33182         startNode.cascade(f);
33183         if(this.remove){
33184            for(var id in af){
33185                if(typeof id != "function"){
33186                    var n = af[id];
33187                    if(n && n.parentNode){
33188                        n.parentNode.removeChild(n);
33189                    }
33190                }
33191            }
33192         }
33193     },
33194
33195     /**
33196      * Clears the current filter. Note: with the "remove" option
33197      * set a filter cannot be cleared.
33198      */
33199     clear : function(){
33200         var t = this.tree;
33201         var af = this.filtered;
33202         for(var id in af){
33203             if(typeof id != "function"){
33204                 var n = af[id];
33205                 if(n){
33206                     n.ui.show();
33207                 }
33208             }
33209         }
33210         this.filtered = {};
33211     }
33212 };
33213 /*
33214  * Based on:
33215  * Ext JS Library 1.1.1
33216  * Copyright(c) 2006-2007, Ext JS, LLC.
33217  *
33218  * Originally Released Under LGPL - original licence link has changed is not relivant.
33219  *
33220  * Fork - LGPL
33221  * <script type="text/javascript">
33222  */
33223  
33224
33225 /**
33226  * @class Roo.tree.TreeSorter
33227  * Provides sorting of nodes in a TreePanel
33228  * 
33229  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33230  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33231  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33232  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33233  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33234  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33235  * @constructor
33236  * @param {TreePanel} tree
33237  * @param {Object} config
33238  */
33239 Roo.tree.TreeSorter = function(tree, config){
33240     Roo.apply(this, config);
33241     tree.on("beforechildrenrendered", this.doSort, this);
33242     tree.on("append", this.updateSort, this);
33243     tree.on("insert", this.updateSort, this);
33244     
33245     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33246     var p = this.property || "text";
33247     var sortType = this.sortType;
33248     var fs = this.folderSort;
33249     var cs = this.caseSensitive === true;
33250     var leafAttr = this.leafAttr || 'leaf';
33251
33252     this.sortFn = function(n1, n2){
33253         if(fs){
33254             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33255                 return 1;
33256             }
33257             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33258                 return -1;
33259             }
33260         }
33261         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33262         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33263         if(v1 < v2){
33264                         return dsc ? +1 : -1;
33265                 }else if(v1 > v2){
33266                         return dsc ? -1 : +1;
33267         }else{
33268                 return 0;
33269         }
33270     };
33271 };
33272
33273 Roo.tree.TreeSorter.prototype = {
33274     doSort : function(node){
33275         node.sort(this.sortFn);
33276     },
33277     
33278     compareNodes : function(n1, n2){
33279         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33280     },
33281     
33282     updateSort : function(tree, node){
33283         if(node.childrenRendered){
33284             this.doSort.defer(1, this, [node]);
33285         }
33286     }
33287 };/*
33288  * Based on:
33289  * Ext JS Library 1.1.1
33290  * Copyright(c) 2006-2007, Ext JS, LLC.
33291  *
33292  * Originally Released Under LGPL - original licence link has changed is not relivant.
33293  *
33294  * Fork - LGPL
33295  * <script type="text/javascript">
33296  */
33297
33298 if(Roo.dd.DropZone){
33299     
33300 Roo.tree.TreeDropZone = function(tree, config){
33301     this.allowParentInsert = false;
33302     this.allowContainerDrop = false;
33303     this.appendOnly = false;
33304     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33305     this.tree = tree;
33306     this.lastInsertClass = "x-tree-no-status";
33307     this.dragOverData = {};
33308 };
33309
33310 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33311     ddGroup : "TreeDD",
33312     
33313     expandDelay : 1000,
33314     
33315     expandNode : function(node){
33316         if(node.hasChildNodes() && !node.isExpanded()){
33317             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33318         }
33319     },
33320     
33321     queueExpand : function(node){
33322         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33323     },
33324     
33325     cancelExpand : function(){
33326         if(this.expandProcId){
33327             clearTimeout(this.expandProcId);
33328             this.expandProcId = false;
33329         }
33330     },
33331     
33332     isValidDropPoint : function(n, pt, dd, e, data){
33333         if(!n || !data){ return false; }
33334         var targetNode = n.node;
33335         var dropNode = data.node;
33336         // default drop rules
33337         if(!(targetNode && targetNode.isTarget && pt)){
33338             return false;
33339         }
33340         if(pt == "append" && targetNode.allowChildren === false){
33341             return false;
33342         }
33343         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33344             return false;
33345         }
33346         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33347             return false;
33348         }
33349         // reuse the object
33350         var overEvent = this.dragOverData;
33351         overEvent.tree = this.tree;
33352         overEvent.target = targetNode;
33353         overEvent.data = data;
33354         overEvent.point = pt;
33355         overEvent.source = dd;
33356         overEvent.rawEvent = e;
33357         overEvent.dropNode = dropNode;
33358         overEvent.cancel = false;  
33359         var result = this.tree.fireEvent("nodedragover", overEvent);
33360         return overEvent.cancel === false && result !== false;
33361     },
33362     
33363     getDropPoint : function(e, n, dd){
33364         var tn = n.node;
33365         if(tn.isRoot){
33366             return tn.allowChildren !== false ? "append" : false; // always append for root
33367         }
33368         var dragEl = n.ddel;
33369         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33370         var y = Roo.lib.Event.getPageY(e);
33371         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33372         
33373         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33374         var noAppend = tn.allowChildren === false;
33375         if(this.appendOnly || tn.parentNode.allowChildren === false){
33376             return noAppend ? false : "append";
33377         }
33378         var noBelow = false;
33379         if(!this.allowParentInsert){
33380             noBelow = tn.hasChildNodes() && tn.isExpanded();
33381         }
33382         var q = (b - t) / (noAppend ? 2 : 3);
33383         if(y >= t && y < (t + q)){
33384             return "above";
33385         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33386             return "below";
33387         }else{
33388             return "append";
33389         }
33390     },
33391     
33392     onNodeEnter : function(n, dd, e, data){
33393         this.cancelExpand();
33394     },
33395     
33396     onNodeOver : function(n, dd, e, data){
33397         var pt = this.getDropPoint(e, n, dd);
33398         var node = n.node;
33399         
33400         // auto node expand check
33401         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33402             this.queueExpand(node);
33403         }else if(pt != "append"){
33404             this.cancelExpand();
33405         }
33406         
33407         // set the insert point style on the target node
33408         var returnCls = this.dropNotAllowed;
33409         if(this.isValidDropPoint(n, pt, dd, e, data)){
33410            if(pt){
33411                var el = n.ddel;
33412                var cls;
33413                if(pt == "above"){
33414                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33415                    cls = "x-tree-drag-insert-above";
33416                }else if(pt == "below"){
33417                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33418                    cls = "x-tree-drag-insert-below";
33419                }else{
33420                    returnCls = "x-tree-drop-ok-append";
33421                    cls = "x-tree-drag-append";
33422                }
33423                if(this.lastInsertClass != cls){
33424                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33425                    this.lastInsertClass = cls;
33426                }
33427            }
33428        }
33429        return returnCls;
33430     },
33431     
33432     onNodeOut : function(n, dd, e, data){
33433         this.cancelExpand();
33434         this.removeDropIndicators(n);
33435     },
33436     
33437     onNodeDrop : function(n, dd, e, data){
33438         var point = this.getDropPoint(e, n, dd);
33439         var targetNode = n.node;
33440         targetNode.ui.startDrop();
33441         if(!this.isValidDropPoint(n, point, dd, e, data)){
33442             targetNode.ui.endDrop();
33443             return false;
33444         }
33445         // first try to find the drop node
33446         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33447         var dropEvent = {
33448             tree : this.tree,
33449             target: targetNode,
33450             data: data,
33451             point: point,
33452             source: dd,
33453             rawEvent: e,
33454             dropNode: dropNode,
33455             cancel: !dropNode   
33456         };
33457         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33458         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33459             targetNode.ui.endDrop();
33460             return false;
33461         }
33462         // allow target changing
33463         targetNode = dropEvent.target;
33464         if(point == "append" && !targetNode.isExpanded()){
33465             targetNode.expand(false, null, function(){
33466                 this.completeDrop(dropEvent);
33467             }.createDelegate(this));
33468         }else{
33469             this.completeDrop(dropEvent);
33470         }
33471         return true;
33472     },
33473     
33474     completeDrop : function(de){
33475         var ns = de.dropNode, p = de.point, t = de.target;
33476         if(!(ns instanceof Array)){
33477             ns = [ns];
33478         }
33479         var n;
33480         for(var i = 0, len = ns.length; i < len; i++){
33481             n = ns[i];
33482             if(p == "above"){
33483                 t.parentNode.insertBefore(n, t);
33484             }else if(p == "below"){
33485                 t.parentNode.insertBefore(n, t.nextSibling);
33486             }else{
33487                 t.appendChild(n);
33488             }
33489         }
33490         n.ui.focus();
33491         if(this.tree.hlDrop){
33492             n.ui.highlight();
33493         }
33494         t.ui.endDrop();
33495         this.tree.fireEvent("nodedrop", de);
33496     },
33497     
33498     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33499         if(this.tree.hlDrop){
33500             dropNode.ui.focus();
33501             dropNode.ui.highlight();
33502         }
33503         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33504     },
33505     
33506     getTree : function(){
33507         return this.tree;
33508     },
33509     
33510     removeDropIndicators : function(n){
33511         if(n && n.ddel){
33512             var el = n.ddel;
33513             Roo.fly(el).removeClass([
33514                     "x-tree-drag-insert-above",
33515                     "x-tree-drag-insert-below",
33516                     "x-tree-drag-append"]);
33517             this.lastInsertClass = "_noclass";
33518         }
33519     },
33520     
33521     beforeDragDrop : function(target, e, id){
33522         this.cancelExpand();
33523         return true;
33524     },
33525     
33526     afterRepair : function(data){
33527         if(data && Roo.enableFx){
33528             data.node.ui.highlight();
33529         }
33530         this.hideProxy();
33531     }    
33532 });
33533
33534 }
33535 /*
33536  * Based on:
33537  * Ext JS Library 1.1.1
33538  * Copyright(c) 2006-2007, Ext JS, LLC.
33539  *
33540  * Originally Released Under LGPL - original licence link has changed is not relivant.
33541  *
33542  * Fork - LGPL
33543  * <script type="text/javascript">
33544  */
33545  
33546
33547 if(Roo.dd.DragZone){
33548 Roo.tree.TreeDragZone = function(tree, config){
33549     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33550     this.tree = tree;
33551 };
33552
33553 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33554     ddGroup : "TreeDD",
33555     
33556     onBeforeDrag : function(data, e){
33557         var n = data.node;
33558         return n && n.draggable && !n.disabled;
33559     },
33560     
33561     onInitDrag : function(e){
33562         var data = this.dragData;
33563         this.tree.getSelectionModel().select(data.node);
33564         this.proxy.update("");
33565         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33566         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33567     },
33568     
33569     getRepairXY : function(e, data){
33570         return data.node.ui.getDDRepairXY();
33571     },
33572     
33573     onEndDrag : function(data, e){
33574         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33575     },
33576     
33577     onValidDrop : function(dd, e, id){
33578         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33579         this.hideProxy();
33580     },
33581     
33582     beforeInvalidDrop : function(e, id){
33583         // this scrolls the original position back into view
33584         var sm = this.tree.getSelectionModel();
33585         sm.clearSelections();
33586         sm.select(this.dragData.node);
33587     }
33588 });
33589 }/*
33590  * Based on:
33591  * Ext JS Library 1.1.1
33592  * Copyright(c) 2006-2007, Ext JS, LLC.
33593  *
33594  * Originally Released Under LGPL - original licence link has changed is not relivant.
33595  *
33596  * Fork - LGPL
33597  * <script type="text/javascript">
33598  */
33599 /**
33600  * @class Roo.tree.TreeEditor
33601  * @extends Roo.Editor
33602  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33603  * as the editor field.
33604  * @constructor
33605  * @param {Object} config (used to be the tree panel.)
33606  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33607  * 
33608  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33609  * @cfg {Roo.form.TextField|Object} field The field configuration
33610  *
33611  * 
33612  */
33613 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33614     var tree = config;
33615     var field;
33616     if (oldconfig) { // old style..
33617         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33618     } else {
33619         // new style..
33620         tree = config.tree;
33621         config.field = config.field  || {};
33622         config.field.xtype = 'TextField';
33623         field = Roo.factory(config.field, Roo.form);
33624     }
33625     config = config || {};
33626     
33627     
33628     this.addEvents({
33629         /**
33630          * @event beforenodeedit
33631          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33632          * false from the handler of this event.
33633          * @param {Editor} this
33634          * @param {Roo.tree.Node} node 
33635          */
33636         "beforenodeedit" : true
33637     });
33638     
33639     //Roo.log(config);
33640     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33641
33642     this.tree = tree;
33643
33644     tree.on('beforeclick', this.beforeNodeClick, this);
33645     tree.getTreeEl().on('mousedown', this.hide, this);
33646     this.on('complete', this.updateNode, this);
33647     this.on('beforestartedit', this.fitToTree, this);
33648     this.on('startedit', this.bindScroll, this, {delay:10});
33649     this.on('specialkey', this.onSpecialKey, this);
33650 };
33651
33652 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33653     /**
33654      * @cfg {String} alignment
33655      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33656      */
33657     alignment: "l-l",
33658     // inherit
33659     autoSize: false,
33660     /**
33661      * @cfg {Boolean} hideEl
33662      * True to hide the bound element while the editor is displayed (defaults to false)
33663      */
33664     hideEl : false,
33665     /**
33666      * @cfg {String} cls
33667      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33668      */
33669     cls: "x-small-editor x-tree-editor",
33670     /**
33671      * @cfg {Boolean} shim
33672      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33673      */
33674     shim:false,
33675     // inherit
33676     shadow:"frame",
33677     /**
33678      * @cfg {Number} maxWidth
33679      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33680      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33681      * scroll and client offsets into account prior to each edit.
33682      */
33683     maxWidth: 250,
33684
33685     editDelay : 350,
33686
33687     // private
33688     fitToTree : function(ed, el){
33689         var td = this.tree.getTreeEl().dom, nd = el.dom;
33690         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33691             td.scrollLeft = nd.offsetLeft;
33692         }
33693         var w = Math.min(
33694                 this.maxWidth,
33695                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33696         this.setSize(w, '');
33697         
33698         return this.fireEvent('beforenodeedit', this, this.editNode);
33699         
33700     },
33701
33702     // private
33703     triggerEdit : function(node){
33704         this.completeEdit();
33705         this.editNode = node;
33706         this.startEdit(node.ui.textNode, node.text);
33707     },
33708
33709     // private
33710     bindScroll : function(){
33711         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33712     },
33713
33714     // private
33715     beforeNodeClick : function(node, e){
33716         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33717         this.lastClick = new Date();
33718         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33719             e.stopEvent();
33720             this.triggerEdit(node);
33721             return false;
33722         }
33723         return true;
33724     },
33725
33726     // private
33727     updateNode : function(ed, value){
33728         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33729         this.editNode.setText(value);
33730     },
33731
33732     // private
33733     onHide : function(){
33734         Roo.tree.TreeEditor.superclass.onHide.call(this);
33735         if(this.editNode){
33736             this.editNode.ui.focus();
33737         }
33738     },
33739
33740     // private
33741     onSpecialKey : function(field, e){
33742         var k = e.getKey();
33743         if(k == e.ESC){
33744             e.stopEvent();
33745             this.cancelEdit();
33746         }else if(k == e.ENTER && !e.hasModifier()){
33747             e.stopEvent();
33748             this.completeEdit();
33749         }
33750     }
33751 });//<Script type="text/javascript">
33752 /*
33753  * Based on:
33754  * Ext JS Library 1.1.1
33755  * Copyright(c) 2006-2007, Ext JS, LLC.
33756  *
33757  * Originally Released Under LGPL - original licence link has changed is not relivant.
33758  *
33759  * Fork - LGPL
33760  * <script type="text/javascript">
33761  */
33762  
33763 /**
33764  * Not documented??? - probably should be...
33765  */
33766
33767 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33768     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33769     
33770     renderElements : function(n, a, targetNode, bulkRender){
33771         //consel.log("renderElements?");
33772         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33773
33774         var t = n.getOwnerTree();
33775         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33776         
33777         var cols = t.columns;
33778         var bw = t.borderWidth;
33779         var c = cols[0];
33780         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33781          var cb = typeof a.checked == "boolean";
33782         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33783         var colcls = 'x-t-' + tid + '-c0';
33784         var buf = [
33785             '<li class="x-tree-node">',
33786             
33787                 
33788                 '<div class="x-tree-node-el ', a.cls,'">',
33789                     // extran...
33790                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33791                 
33792                 
33793                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33794                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33795                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33796                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33797                            (a.iconCls ? ' '+a.iconCls : ''),
33798                            '" unselectable="on" />',
33799                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33800                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33801                              
33802                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33803                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33804                             '<span unselectable="on" qtip="' + tx + '">',
33805                              tx,
33806                              '</span></a>' ,
33807                     '</div>',
33808                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33809                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33810                  ];
33811         for(var i = 1, len = cols.length; i < len; i++){
33812             c = cols[i];
33813             colcls = 'x-t-' + tid + '-c' +i;
33814             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33815             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33816                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33817                       "</div>");
33818          }
33819          
33820          buf.push(
33821             '</a>',
33822             '<div class="x-clear"></div></div>',
33823             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33824             "</li>");
33825         
33826         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33827             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33828                                 n.nextSibling.ui.getEl(), buf.join(""));
33829         }else{
33830             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33831         }
33832         var el = this.wrap.firstChild;
33833         this.elRow = el;
33834         this.elNode = el.firstChild;
33835         this.ranchor = el.childNodes[1];
33836         this.ctNode = this.wrap.childNodes[1];
33837         var cs = el.firstChild.childNodes;
33838         this.indentNode = cs[0];
33839         this.ecNode = cs[1];
33840         this.iconNode = cs[2];
33841         var index = 3;
33842         if(cb){
33843             this.checkbox = cs[3];
33844             index++;
33845         }
33846         this.anchor = cs[index];
33847         
33848         this.textNode = cs[index].firstChild;
33849         
33850         //el.on("click", this.onClick, this);
33851         //el.on("dblclick", this.onDblClick, this);
33852         
33853         
33854        // console.log(this);
33855     },
33856     initEvents : function(){
33857         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33858         
33859             
33860         var a = this.ranchor;
33861
33862         var el = Roo.get(a);
33863
33864         if(Roo.isOpera){ // opera render bug ignores the CSS
33865             el.setStyle("text-decoration", "none");
33866         }
33867
33868         el.on("click", this.onClick, this);
33869         el.on("dblclick", this.onDblClick, this);
33870         el.on("contextmenu", this.onContextMenu, this);
33871         
33872     },
33873     
33874     /*onSelectedChange : function(state){
33875         if(state){
33876             this.focus();
33877             this.addClass("x-tree-selected");
33878         }else{
33879             //this.blur();
33880             this.removeClass("x-tree-selected");
33881         }
33882     },*/
33883     addClass : function(cls){
33884         if(this.elRow){
33885             Roo.fly(this.elRow).addClass(cls);
33886         }
33887         
33888     },
33889     
33890     
33891     removeClass : function(cls){
33892         if(this.elRow){
33893             Roo.fly(this.elRow).removeClass(cls);
33894         }
33895     }
33896
33897     
33898     
33899 });//<Script type="text/javascript">
33900
33901 /*
33902  * Based on:
33903  * Ext JS Library 1.1.1
33904  * Copyright(c) 2006-2007, Ext JS, LLC.
33905  *
33906  * Originally Released Under LGPL - original licence link has changed is not relivant.
33907  *
33908  * Fork - LGPL
33909  * <script type="text/javascript">
33910  */
33911  
33912
33913 /**
33914  * @class Roo.tree.ColumnTree
33915  * @extends Roo.data.TreePanel
33916  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33917  * @cfg {int} borderWidth  compined right/left border allowance
33918  * @constructor
33919  * @param {String/HTMLElement/Element} el The container element
33920  * @param {Object} config
33921  */
33922 Roo.tree.ColumnTree =  function(el, config)
33923 {
33924    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33925    this.addEvents({
33926         /**
33927         * @event resize
33928         * Fire this event on a container when it resizes
33929         * @param {int} w Width
33930         * @param {int} h Height
33931         */
33932        "resize" : true
33933     });
33934     this.on('resize', this.onResize, this);
33935 };
33936
33937 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33938     //lines:false,
33939     
33940     
33941     borderWidth: Roo.isBorderBox ? 0 : 2, 
33942     headEls : false,
33943     
33944     render : function(){
33945         // add the header.....
33946        
33947         Roo.tree.ColumnTree.superclass.render.apply(this);
33948         
33949         this.el.addClass('x-column-tree');
33950         
33951         this.headers = this.el.createChild(
33952             {cls:'x-tree-headers'},this.innerCt.dom);
33953    
33954         var cols = this.columns, c;
33955         var totalWidth = 0;
33956         this.headEls = [];
33957         var  len = cols.length;
33958         for(var i = 0; i < len; i++){
33959              c = cols[i];
33960              totalWidth += c.width;
33961             this.headEls.push(this.headers.createChild({
33962                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33963                  cn: {
33964                      cls:'x-tree-hd-text',
33965                      html: c.header
33966                  },
33967                  style:'width:'+(c.width-this.borderWidth)+'px;'
33968              }));
33969         }
33970         this.headers.createChild({cls:'x-clear'});
33971         // prevent floats from wrapping when clipped
33972         this.headers.setWidth(totalWidth);
33973         //this.innerCt.setWidth(totalWidth);
33974         this.innerCt.setStyle({ overflow: 'auto' });
33975         this.onResize(this.width, this.height);
33976              
33977         
33978     },
33979     onResize : function(w,h)
33980     {
33981         this.height = h;
33982         this.width = w;
33983         // resize cols..
33984         this.innerCt.setWidth(this.width);
33985         this.innerCt.setHeight(this.height-20);
33986         
33987         // headers...
33988         var cols = this.columns, c;
33989         var totalWidth = 0;
33990         var expEl = false;
33991         var len = cols.length;
33992         for(var i = 0; i < len; i++){
33993             c = cols[i];
33994             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33995                 // it's the expander..
33996                 expEl  = this.headEls[i];
33997                 continue;
33998             }
33999             totalWidth += c.width;
34000             
34001         }
34002         if (expEl) {
34003             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34004         }
34005         this.headers.setWidth(w-20);
34006
34007         
34008         
34009         
34010     }
34011 });
34012 /*
34013  * Based on:
34014  * Ext JS Library 1.1.1
34015  * Copyright(c) 2006-2007, Ext JS, LLC.
34016  *
34017  * Originally Released Under LGPL - original licence link has changed is not relivant.
34018  *
34019  * Fork - LGPL
34020  * <script type="text/javascript">
34021  */
34022  
34023 /**
34024  * @class Roo.menu.Menu
34025  * @extends Roo.util.Observable
34026  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34027  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34028  * @constructor
34029  * Creates a new Menu
34030  * @param {Object} config Configuration options
34031  */
34032 Roo.menu.Menu = function(config){
34033     Roo.apply(this, config);
34034     this.id = this.id || Roo.id();
34035     this.addEvents({
34036         /**
34037          * @event beforeshow
34038          * Fires before this menu is displayed
34039          * @param {Roo.menu.Menu} this
34040          */
34041         beforeshow : true,
34042         /**
34043          * @event beforehide
34044          * Fires before this menu is hidden
34045          * @param {Roo.menu.Menu} this
34046          */
34047         beforehide : true,
34048         /**
34049          * @event show
34050          * Fires after this menu is displayed
34051          * @param {Roo.menu.Menu} this
34052          */
34053         show : true,
34054         /**
34055          * @event hide
34056          * Fires after this menu is hidden
34057          * @param {Roo.menu.Menu} this
34058          */
34059         hide : true,
34060         /**
34061          * @event click
34062          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34063          * @param {Roo.menu.Menu} this
34064          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34065          * @param {Roo.EventObject} e
34066          */
34067         click : true,
34068         /**
34069          * @event mouseover
34070          * Fires when the mouse is hovering over this menu
34071          * @param {Roo.menu.Menu} this
34072          * @param {Roo.EventObject} e
34073          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34074          */
34075         mouseover : true,
34076         /**
34077          * @event mouseout
34078          * Fires when the mouse exits this menu
34079          * @param {Roo.menu.Menu} this
34080          * @param {Roo.EventObject} e
34081          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34082          */
34083         mouseout : true,
34084         /**
34085          * @event itemclick
34086          * Fires when a menu item contained in this menu is clicked
34087          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34088          * @param {Roo.EventObject} e
34089          */
34090         itemclick: true
34091     });
34092     if (this.registerMenu) {
34093         Roo.menu.MenuMgr.register(this);
34094     }
34095     
34096     var mis = this.items;
34097     this.items = new Roo.util.MixedCollection();
34098     if(mis){
34099         this.add.apply(this, mis);
34100     }
34101 };
34102
34103 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34104     /**
34105      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34106      */
34107     minWidth : 120,
34108     /**
34109      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34110      * for bottom-right shadow (defaults to "sides")
34111      */
34112     shadow : "sides",
34113     /**
34114      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34115      * this menu (defaults to "tl-tr?")
34116      */
34117     subMenuAlign : "tl-tr?",
34118     /**
34119      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34120      * relative to its element of origin (defaults to "tl-bl?")
34121      */
34122     defaultAlign : "tl-bl?",
34123     /**
34124      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34125      */
34126     allowOtherMenus : false,
34127     /**
34128      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34129      */
34130     registerMenu : true,
34131
34132     hidden:true,
34133
34134     // private
34135     render : function(){
34136         if(this.el){
34137             return;
34138         }
34139         var el = this.el = new Roo.Layer({
34140             cls: "x-menu",
34141             shadow:this.shadow,
34142             constrain: false,
34143             parentEl: this.parentEl || document.body,
34144             zindex:15000
34145         });
34146
34147         this.keyNav = new Roo.menu.MenuNav(this);
34148
34149         if(this.plain){
34150             el.addClass("x-menu-plain");
34151         }
34152         if(this.cls){
34153             el.addClass(this.cls);
34154         }
34155         // generic focus element
34156         this.focusEl = el.createChild({
34157             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34158         });
34159         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34160         ul.on("click", this.onClick, this);
34161         ul.on("mouseover", this.onMouseOver, this);
34162         ul.on("mouseout", this.onMouseOut, this);
34163         this.items.each(function(item){
34164             var li = document.createElement("li");
34165             li.className = "x-menu-list-item";
34166             ul.dom.appendChild(li);
34167             item.render(li, this);
34168         }, this);
34169         this.ul = ul;
34170         this.autoWidth();
34171     },
34172
34173     // private
34174     autoWidth : function(){
34175         var el = this.el, ul = this.ul;
34176         if(!el){
34177             return;
34178         }
34179         var w = this.width;
34180         if(w){
34181             el.setWidth(w);
34182         }else if(Roo.isIE){
34183             el.setWidth(this.minWidth);
34184             var t = el.dom.offsetWidth; // force recalc
34185             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34186         }
34187     },
34188
34189     // private
34190     delayAutoWidth : function(){
34191         if(this.rendered){
34192             if(!this.awTask){
34193                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34194             }
34195             this.awTask.delay(20);
34196         }
34197     },
34198
34199     // private
34200     findTargetItem : function(e){
34201         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34202         if(t && t.menuItemId){
34203             return this.items.get(t.menuItemId);
34204         }
34205     },
34206
34207     // private
34208     onClick : function(e){
34209         var t;
34210         if(t = this.findTargetItem(e)){
34211             t.onClick(e);
34212             this.fireEvent("click", this, t, e);
34213         }
34214     },
34215
34216     // private
34217     setActiveItem : function(item, autoExpand){
34218         if(item != this.activeItem){
34219             if(this.activeItem){
34220                 this.activeItem.deactivate();
34221             }
34222             this.activeItem = item;
34223             item.activate(autoExpand);
34224         }else if(autoExpand){
34225             item.expandMenu();
34226         }
34227     },
34228
34229     // private
34230     tryActivate : function(start, step){
34231         var items = this.items;
34232         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34233             var item = items.get(i);
34234             if(!item.disabled && item.canActivate){
34235                 this.setActiveItem(item, false);
34236                 return item;
34237             }
34238         }
34239         return false;
34240     },
34241
34242     // private
34243     onMouseOver : function(e){
34244         var t;
34245         if(t = this.findTargetItem(e)){
34246             if(t.canActivate && !t.disabled){
34247                 this.setActiveItem(t, true);
34248             }
34249         }
34250         this.fireEvent("mouseover", this, e, t);
34251     },
34252
34253     // private
34254     onMouseOut : function(e){
34255         var t;
34256         if(t = this.findTargetItem(e)){
34257             if(t == this.activeItem && t.shouldDeactivate(e)){
34258                 this.activeItem.deactivate();
34259                 delete this.activeItem;
34260             }
34261         }
34262         this.fireEvent("mouseout", this, e, t);
34263     },
34264
34265     /**
34266      * Read-only.  Returns true if the menu is currently displayed, else false.
34267      * @type Boolean
34268      */
34269     isVisible : function(){
34270         return this.el && !this.hidden;
34271     },
34272
34273     /**
34274      * Displays this menu relative to another element
34275      * @param {String/HTMLElement/Roo.Element} element The element to align to
34276      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34277      * the element (defaults to this.defaultAlign)
34278      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34279      */
34280     show : function(el, pos, parentMenu){
34281         this.parentMenu = parentMenu;
34282         if(!this.el){
34283             this.render();
34284         }
34285         this.fireEvent("beforeshow", this);
34286         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34287     },
34288
34289     /**
34290      * Displays this menu at a specific xy position
34291      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34292      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34293      */
34294     showAt : function(xy, parentMenu, /* private: */_e){
34295         this.parentMenu = parentMenu;
34296         if(!this.el){
34297             this.render();
34298         }
34299         if(_e !== false){
34300             this.fireEvent("beforeshow", this);
34301             xy = this.el.adjustForConstraints(xy);
34302         }
34303         this.el.setXY(xy);
34304         this.el.show();
34305         this.hidden = false;
34306         this.focus();
34307         this.fireEvent("show", this);
34308     },
34309
34310     focus : function(){
34311         if(!this.hidden){
34312             this.doFocus.defer(50, this);
34313         }
34314     },
34315
34316     doFocus : function(){
34317         if(!this.hidden){
34318             this.focusEl.focus();
34319         }
34320     },
34321
34322     /**
34323      * Hides this menu and optionally all parent menus
34324      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34325      */
34326     hide : function(deep){
34327         if(this.el && this.isVisible()){
34328             this.fireEvent("beforehide", this);
34329             if(this.activeItem){
34330                 this.activeItem.deactivate();
34331                 this.activeItem = null;
34332             }
34333             this.el.hide();
34334             this.hidden = true;
34335             this.fireEvent("hide", this);
34336         }
34337         if(deep === true && this.parentMenu){
34338             this.parentMenu.hide(true);
34339         }
34340     },
34341
34342     /**
34343      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34344      * Any of the following are valid:
34345      * <ul>
34346      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34347      * <li>An HTMLElement object which will be converted to a menu item</li>
34348      * <li>A menu item config object that will be created as a new menu item</li>
34349      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34350      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34351      * </ul>
34352      * Usage:
34353      * <pre><code>
34354 // Create the menu
34355 var menu = new Roo.menu.Menu();
34356
34357 // Create a menu item to add by reference
34358 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34359
34360 // Add a bunch of items at once using different methods.
34361 // Only the last item added will be returned.
34362 var item = menu.add(
34363     menuItem,                // add existing item by ref
34364     'Dynamic Item',          // new TextItem
34365     '-',                     // new separator
34366     { text: 'Config Item' }  // new item by config
34367 );
34368 </code></pre>
34369      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34370      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34371      */
34372     add : function(){
34373         var a = arguments, l = a.length, item;
34374         for(var i = 0; i < l; i++){
34375             var el = a[i];
34376             if ((typeof(el) == "object") && el.xtype && el.xns) {
34377                 el = Roo.factory(el, Roo.menu);
34378             }
34379             
34380             if(el.render){ // some kind of Item
34381                 item = this.addItem(el);
34382             }else if(typeof el == "string"){ // string
34383                 if(el == "separator" || el == "-"){
34384                     item = this.addSeparator();
34385                 }else{
34386                     item = this.addText(el);
34387                 }
34388             }else if(el.tagName || el.el){ // element
34389                 item = this.addElement(el);
34390             }else if(typeof el == "object"){ // must be menu item config?
34391                 item = this.addMenuItem(el);
34392             }
34393         }
34394         return item;
34395     },
34396
34397     /**
34398      * Returns this menu's underlying {@link Roo.Element} object
34399      * @return {Roo.Element} The element
34400      */
34401     getEl : function(){
34402         if(!this.el){
34403             this.render();
34404         }
34405         return this.el;
34406     },
34407
34408     /**
34409      * Adds a separator bar to the menu
34410      * @return {Roo.menu.Item} The menu item that was added
34411      */
34412     addSeparator : function(){
34413         return this.addItem(new Roo.menu.Separator());
34414     },
34415
34416     /**
34417      * Adds an {@link Roo.Element} object to the menu
34418      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34419      * @return {Roo.menu.Item} The menu item that was added
34420      */
34421     addElement : function(el){
34422         return this.addItem(new Roo.menu.BaseItem(el));
34423     },
34424
34425     /**
34426      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34427      * @param {Roo.menu.Item} item The menu item to add
34428      * @return {Roo.menu.Item} The menu item that was added
34429      */
34430     addItem : function(item){
34431         this.items.add(item);
34432         if(this.ul){
34433             var li = document.createElement("li");
34434             li.className = "x-menu-list-item";
34435             this.ul.dom.appendChild(li);
34436             item.render(li, this);
34437             this.delayAutoWidth();
34438         }
34439         return item;
34440     },
34441
34442     /**
34443      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34444      * @param {Object} config A MenuItem config object
34445      * @return {Roo.menu.Item} The menu item that was added
34446      */
34447     addMenuItem : function(config){
34448         if(!(config instanceof Roo.menu.Item)){
34449             if(typeof config.checked == "boolean"){ // must be check menu item config?
34450                 config = new Roo.menu.CheckItem(config);
34451             }else{
34452                 config = new Roo.menu.Item(config);
34453             }
34454         }
34455         return this.addItem(config);
34456     },
34457
34458     /**
34459      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34460      * @param {String} text The text to display in the menu item
34461      * @return {Roo.menu.Item} The menu item that was added
34462      */
34463     addText : function(text){
34464         return this.addItem(new Roo.menu.TextItem({ text : text }));
34465     },
34466
34467     /**
34468      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34469      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34470      * @param {Roo.menu.Item} item The menu item to add
34471      * @return {Roo.menu.Item} The menu item that was added
34472      */
34473     insert : function(index, item){
34474         this.items.insert(index, item);
34475         if(this.ul){
34476             var li = document.createElement("li");
34477             li.className = "x-menu-list-item";
34478             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34479             item.render(li, this);
34480             this.delayAutoWidth();
34481         }
34482         return item;
34483     },
34484
34485     /**
34486      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34487      * @param {Roo.menu.Item} item The menu item to remove
34488      */
34489     remove : function(item){
34490         this.items.removeKey(item.id);
34491         item.destroy();
34492     },
34493
34494     /**
34495      * Removes and destroys all items in the menu
34496      */
34497     removeAll : function(){
34498         var f;
34499         while(f = this.items.first()){
34500             this.remove(f);
34501         }
34502     }
34503 });
34504
34505 // MenuNav is a private utility class used internally by the Menu
34506 Roo.menu.MenuNav = function(menu){
34507     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34508     this.scope = this.menu = menu;
34509 };
34510
34511 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34512     doRelay : function(e, h){
34513         var k = e.getKey();
34514         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34515             this.menu.tryActivate(0, 1);
34516             return false;
34517         }
34518         return h.call(this.scope || this, e, this.menu);
34519     },
34520
34521     up : function(e, m){
34522         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34523             m.tryActivate(m.items.length-1, -1);
34524         }
34525     },
34526
34527     down : function(e, m){
34528         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34529             m.tryActivate(0, 1);
34530         }
34531     },
34532
34533     right : function(e, m){
34534         if(m.activeItem){
34535             m.activeItem.expandMenu(true);
34536         }
34537     },
34538
34539     left : function(e, m){
34540         m.hide();
34541         if(m.parentMenu && m.parentMenu.activeItem){
34542             m.parentMenu.activeItem.activate();
34543         }
34544     },
34545
34546     enter : function(e, m){
34547         if(m.activeItem){
34548             e.stopPropagation();
34549             m.activeItem.onClick(e);
34550             m.fireEvent("click", this, m.activeItem);
34551             return true;
34552         }
34553     }
34554 });/*
34555  * Based on:
34556  * Ext JS Library 1.1.1
34557  * Copyright(c) 2006-2007, Ext JS, LLC.
34558  *
34559  * Originally Released Under LGPL - original licence link has changed is not relivant.
34560  *
34561  * Fork - LGPL
34562  * <script type="text/javascript">
34563  */
34564  
34565 /**
34566  * @class Roo.menu.MenuMgr
34567  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34568  * @singleton
34569  */
34570 Roo.menu.MenuMgr = function(){
34571    var menus, active, groups = {}, attached = false, lastShow = new Date();
34572
34573    // private - called when first menu is created
34574    function init(){
34575        menus = {};
34576        active = new Roo.util.MixedCollection();
34577        Roo.get(document).addKeyListener(27, function(){
34578            if(active.length > 0){
34579                hideAll();
34580            }
34581        });
34582    }
34583
34584    // private
34585    function hideAll(){
34586        if(active && active.length > 0){
34587            var c = active.clone();
34588            c.each(function(m){
34589                m.hide();
34590            });
34591        }
34592    }
34593
34594    // private
34595    function onHide(m){
34596        active.remove(m);
34597        if(active.length < 1){
34598            Roo.get(document).un("mousedown", onMouseDown);
34599            attached = false;
34600        }
34601    }
34602
34603    // private
34604    function onShow(m){
34605        var last = active.last();
34606        lastShow = new Date();
34607        active.add(m);
34608        if(!attached){
34609            Roo.get(document).on("mousedown", onMouseDown);
34610            attached = true;
34611        }
34612        if(m.parentMenu){
34613           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34614           m.parentMenu.activeChild = m;
34615        }else if(last && last.isVisible()){
34616           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34617        }
34618    }
34619
34620    // private
34621    function onBeforeHide(m){
34622        if(m.activeChild){
34623            m.activeChild.hide();
34624        }
34625        if(m.autoHideTimer){
34626            clearTimeout(m.autoHideTimer);
34627            delete m.autoHideTimer;
34628        }
34629    }
34630
34631    // private
34632    function onBeforeShow(m){
34633        var pm = m.parentMenu;
34634        if(!pm && !m.allowOtherMenus){
34635            hideAll();
34636        }else if(pm && pm.activeChild && active != m){
34637            pm.activeChild.hide();
34638        }
34639    }
34640
34641    // private
34642    function onMouseDown(e){
34643        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34644            hideAll();
34645        }
34646    }
34647
34648    // private
34649    function onBeforeCheck(mi, state){
34650        if(state){
34651            var g = groups[mi.group];
34652            for(var i = 0, l = g.length; i < l; i++){
34653                if(g[i] != mi){
34654                    g[i].setChecked(false);
34655                }
34656            }
34657        }
34658    }
34659
34660    return {
34661
34662        /**
34663         * Hides all menus that are currently visible
34664         */
34665        hideAll : function(){
34666             hideAll();  
34667        },
34668
34669        // private
34670        register : function(menu){
34671            if(!menus){
34672                init();
34673            }
34674            menus[menu.id] = menu;
34675            menu.on("beforehide", onBeforeHide);
34676            menu.on("hide", onHide);
34677            menu.on("beforeshow", onBeforeShow);
34678            menu.on("show", onShow);
34679            var g = menu.group;
34680            if(g && menu.events["checkchange"]){
34681                if(!groups[g]){
34682                    groups[g] = [];
34683                }
34684                groups[g].push(menu);
34685                menu.on("checkchange", onCheck);
34686            }
34687        },
34688
34689         /**
34690          * Returns a {@link Roo.menu.Menu} object
34691          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34692          * be used to generate and return a new Menu instance.
34693          */
34694        get : function(menu){
34695            if(typeof menu == "string"){ // menu id
34696                return menus[menu];
34697            }else if(menu.events){  // menu instance
34698                return menu;
34699            }else if(typeof menu.length == 'number'){ // array of menu items?
34700                return new Roo.menu.Menu({items:menu});
34701            }else{ // otherwise, must be a config
34702                return new Roo.menu.Menu(menu);
34703            }
34704        },
34705
34706        // private
34707        unregister : function(menu){
34708            delete menus[menu.id];
34709            menu.un("beforehide", onBeforeHide);
34710            menu.un("hide", onHide);
34711            menu.un("beforeshow", onBeforeShow);
34712            menu.un("show", onShow);
34713            var g = menu.group;
34714            if(g && menu.events["checkchange"]){
34715                groups[g].remove(menu);
34716                menu.un("checkchange", onCheck);
34717            }
34718        },
34719
34720        // private
34721        registerCheckable : function(menuItem){
34722            var g = menuItem.group;
34723            if(g){
34724                if(!groups[g]){
34725                    groups[g] = [];
34726                }
34727                groups[g].push(menuItem);
34728                menuItem.on("beforecheckchange", onBeforeCheck);
34729            }
34730        },
34731
34732        // private
34733        unregisterCheckable : function(menuItem){
34734            var g = menuItem.group;
34735            if(g){
34736                groups[g].remove(menuItem);
34737                menuItem.un("beforecheckchange", onBeforeCheck);
34738            }
34739        }
34740    };
34741 }();/*
34742  * Based on:
34743  * Ext JS Library 1.1.1
34744  * Copyright(c) 2006-2007, Ext JS, LLC.
34745  *
34746  * Originally Released Under LGPL - original licence link has changed is not relivant.
34747  *
34748  * Fork - LGPL
34749  * <script type="text/javascript">
34750  */
34751  
34752
34753 /**
34754  * @class Roo.menu.BaseItem
34755  * @extends Roo.Component
34756  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34757  * management and base configuration options shared by all menu components.
34758  * @constructor
34759  * Creates a new BaseItem
34760  * @param {Object} config Configuration options
34761  */
34762 Roo.menu.BaseItem = function(config){
34763     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34764
34765     this.addEvents({
34766         /**
34767          * @event click
34768          * Fires when this item is clicked
34769          * @param {Roo.menu.BaseItem} this
34770          * @param {Roo.EventObject} e
34771          */
34772         click: true,
34773         /**
34774          * @event activate
34775          * Fires when this item is activated
34776          * @param {Roo.menu.BaseItem} this
34777          */
34778         activate : true,
34779         /**
34780          * @event deactivate
34781          * Fires when this item is deactivated
34782          * @param {Roo.menu.BaseItem} this
34783          */
34784         deactivate : true
34785     });
34786
34787     if(this.handler){
34788         this.on("click", this.handler, this.scope, true);
34789     }
34790 };
34791
34792 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34793     /**
34794      * @cfg {Function} handler
34795      * A function that will handle the click event of this menu item (defaults to undefined)
34796      */
34797     /**
34798      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34799      */
34800     canActivate : false,
34801     /**
34802      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34803      */
34804     activeClass : "x-menu-item-active",
34805     /**
34806      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34807      */
34808     hideOnClick : true,
34809     /**
34810      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34811      */
34812     hideDelay : 100,
34813
34814     // private
34815     ctype: "Roo.menu.BaseItem",
34816
34817     // private
34818     actionMode : "container",
34819
34820     // private
34821     render : function(container, parentMenu){
34822         this.parentMenu = parentMenu;
34823         Roo.menu.BaseItem.superclass.render.call(this, container);
34824         this.container.menuItemId = this.id;
34825     },
34826
34827     // private
34828     onRender : function(container, position){
34829         this.el = Roo.get(this.el);
34830         container.dom.appendChild(this.el.dom);
34831     },
34832
34833     // private
34834     onClick : function(e){
34835         if(!this.disabled && this.fireEvent("click", this, e) !== false
34836                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34837             this.handleClick(e);
34838         }else{
34839             e.stopEvent();
34840         }
34841     },
34842
34843     // private
34844     activate : function(){
34845         if(this.disabled){
34846             return false;
34847         }
34848         var li = this.container;
34849         li.addClass(this.activeClass);
34850         this.region = li.getRegion().adjust(2, 2, -2, -2);
34851         this.fireEvent("activate", this);
34852         return true;
34853     },
34854
34855     // private
34856     deactivate : function(){
34857         this.container.removeClass(this.activeClass);
34858         this.fireEvent("deactivate", this);
34859     },
34860
34861     // private
34862     shouldDeactivate : function(e){
34863         return !this.region || !this.region.contains(e.getPoint());
34864     },
34865
34866     // private
34867     handleClick : function(e){
34868         if(this.hideOnClick){
34869             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34870         }
34871     },
34872
34873     // private
34874     expandMenu : function(autoActivate){
34875         // do nothing
34876     },
34877
34878     // private
34879     hideMenu : function(){
34880         // do nothing
34881     }
34882 });/*
34883  * Based on:
34884  * Ext JS Library 1.1.1
34885  * Copyright(c) 2006-2007, Ext JS, LLC.
34886  *
34887  * Originally Released Under LGPL - original licence link has changed is not relivant.
34888  *
34889  * Fork - LGPL
34890  * <script type="text/javascript">
34891  */
34892  
34893 /**
34894  * @class Roo.menu.Adapter
34895  * @extends Roo.menu.BaseItem
34896  * 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.
34897  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34898  * @constructor
34899  * Creates a new Adapter
34900  * @param {Object} config Configuration options
34901  */
34902 Roo.menu.Adapter = function(component, config){
34903     Roo.menu.Adapter.superclass.constructor.call(this, config);
34904     this.component = component;
34905 };
34906 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34907     // private
34908     canActivate : true,
34909
34910     // private
34911     onRender : function(container, position){
34912         this.component.render(container);
34913         this.el = this.component.getEl();
34914     },
34915
34916     // private
34917     activate : function(){
34918         if(this.disabled){
34919             return false;
34920         }
34921         this.component.focus();
34922         this.fireEvent("activate", this);
34923         return true;
34924     },
34925
34926     // private
34927     deactivate : function(){
34928         this.fireEvent("deactivate", this);
34929     },
34930
34931     // private
34932     disable : function(){
34933         this.component.disable();
34934         Roo.menu.Adapter.superclass.disable.call(this);
34935     },
34936
34937     // private
34938     enable : function(){
34939         this.component.enable();
34940         Roo.menu.Adapter.superclass.enable.call(this);
34941     }
34942 });/*
34943  * Based on:
34944  * Ext JS Library 1.1.1
34945  * Copyright(c) 2006-2007, Ext JS, LLC.
34946  *
34947  * Originally Released Under LGPL - original licence link has changed is not relivant.
34948  *
34949  * Fork - LGPL
34950  * <script type="text/javascript">
34951  */
34952
34953 /**
34954  * @class Roo.menu.TextItem
34955  * @extends Roo.menu.BaseItem
34956  * Adds a static text string to a menu, usually used as either a heading or group separator.
34957  * Note: old style constructor with text is still supported.
34958  * 
34959  * @constructor
34960  * Creates a new TextItem
34961  * @param {Object} cfg Configuration
34962  */
34963 Roo.menu.TextItem = function(cfg){
34964     if (typeof(cfg) == 'string') {
34965         this.text = cfg;
34966     } else {
34967         Roo.apply(this,cfg);
34968     }
34969     
34970     Roo.menu.TextItem.superclass.constructor.call(this);
34971 };
34972
34973 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34974     /**
34975      * @cfg {Boolean} text Text to show on item.
34976      */
34977     text : '',
34978     
34979     /**
34980      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34981      */
34982     hideOnClick : false,
34983     /**
34984      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34985      */
34986     itemCls : "x-menu-text",
34987
34988     // private
34989     onRender : function(){
34990         var s = document.createElement("span");
34991         s.className = this.itemCls;
34992         s.innerHTML = this.text;
34993         this.el = s;
34994         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34995     }
34996 });/*
34997  * Based on:
34998  * Ext JS Library 1.1.1
34999  * Copyright(c) 2006-2007, Ext JS, LLC.
35000  *
35001  * Originally Released Under LGPL - original licence link has changed is not relivant.
35002  *
35003  * Fork - LGPL
35004  * <script type="text/javascript">
35005  */
35006
35007 /**
35008  * @class Roo.menu.Separator
35009  * @extends Roo.menu.BaseItem
35010  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35011  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35012  * @constructor
35013  * @param {Object} config Configuration options
35014  */
35015 Roo.menu.Separator = function(config){
35016     Roo.menu.Separator.superclass.constructor.call(this, config);
35017 };
35018
35019 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35020     /**
35021      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35022      */
35023     itemCls : "x-menu-sep",
35024     /**
35025      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35026      */
35027     hideOnClick : false,
35028
35029     // private
35030     onRender : function(li){
35031         var s = document.createElement("span");
35032         s.className = this.itemCls;
35033         s.innerHTML = "&#160;";
35034         this.el = s;
35035         li.addClass("x-menu-sep-li");
35036         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35037     }
35038 });/*
35039  * Based on:
35040  * Ext JS Library 1.1.1
35041  * Copyright(c) 2006-2007, Ext JS, LLC.
35042  *
35043  * Originally Released Under LGPL - original licence link has changed is not relivant.
35044  *
35045  * Fork - LGPL
35046  * <script type="text/javascript">
35047  */
35048 /**
35049  * @class Roo.menu.Item
35050  * @extends Roo.menu.BaseItem
35051  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35052  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35053  * activation and click handling.
35054  * @constructor
35055  * Creates a new Item
35056  * @param {Object} config Configuration options
35057  */
35058 Roo.menu.Item = function(config){
35059     Roo.menu.Item.superclass.constructor.call(this, config);
35060     if(this.menu){
35061         this.menu = Roo.menu.MenuMgr.get(this.menu);
35062     }
35063 };
35064 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35065     
35066     /**
35067      * @cfg {String} text
35068      * The text to show on the menu item.
35069      */
35070     text: '',
35071      /**
35072      * @cfg {String} HTML to render in menu
35073      * The text to show on the menu item (HTML version).
35074      */
35075     html: '',
35076     /**
35077      * @cfg {String} icon
35078      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35079      */
35080     icon: undefined,
35081     /**
35082      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35083      */
35084     itemCls : "x-menu-item",
35085     /**
35086      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35087      */
35088     canActivate : true,
35089     /**
35090      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35091      */
35092     showDelay: 200,
35093     // doc'd in BaseItem
35094     hideDelay: 200,
35095
35096     // private
35097     ctype: "Roo.menu.Item",
35098     
35099     // private
35100     onRender : function(container, position){
35101         var el = document.createElement("a");
35102         el.hideFocus = true;
35103         el.unselectable = "on";
35104         el.href = this.href || "#";
35105         if(this.hrefTarget){
35106             el.target = this.hrefTarget;
35107         }
35108         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35109         
35110         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35111         
35112         el.innerHTML = String.format(
35113                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35114                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35115         this.el = el;
35116         Roo.menu.Item.superclass.onRender.call(this, container, position);
35117     },
35118
35119     /**
35120      * Sets the text to display in this menu item
35121      * @param {String} text The text to display
35122      * @param {Boolean} isHTML true to indicate text is pure html.
35123      */
35124     setText : function(text, isHTML){
35125         if (isHTML) {
35126             this.html = text;
35127         } else {
35128             this.text = text;
35129             this.html = '';
35130         }
35131         if(this.rendered){
35132             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35133      
35134             this.el.update(String.format(
35135                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35136                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35137             this.parentMenu.autoWidth();
35138         }
35139     },
35140
35141     // private
35142     handleClick : function(e){
35143         if(!this.href){ // if no link defined, stop the event automatically
35144             e.stopEvent();
35145         }
35146         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35147     },
35148
35149     // private
35150     activate : function(autoExpand){
35151         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35152             this.focus();
35153             if(autoExpand){
35154                 this.expandMenu();
35155             }
35156         }
35157         return true;
35158     },
35159
35160     // private
35161     shouldDeactivate : function(e){
35162         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35163             if(this.menu && this.menu.isVisible()){
35164                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35165             }
35166             return true;
35167         }
35168         return false;
35169     },
35170
35171     // private
35172     deactivate : function(){
35173         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35174         this.hideMenu();
35175     },
35176
35177     // private
35178     expandMenu : function(autoActivate){
35179         if(!this.disabled && this.menu){
35180             clearTimeout(this.hideTimer);
35181             delete this.hideTimer;
35182             if(!this.menu.isVisible() && !this.showTimer){
35183                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35184             }else if (this.menu.isVisible() && autoActivate){
35185                 this.menu.tryActivate(0, 1);
35186             }
35187         }
35188     },
35189
35190     // private
35191     deferExpand : function(autoActivate){
35192         delete this.showTimer;
35193         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35194         if(autoActivate){
35195             this.menu.tryActivate(0, 1);
35196         }
35197     },
35198
35199     // private
35200     hideMenu : function(){
35201         clearTimeout(this.showTimer);
35202         delete this.showTimer;
35203         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35204             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35205         }
35206     },
35207
35208     // private
35209     deferHide : function(){
35210         delete this.hideTimer;
35211         this.menu.hide();
35212     }
35213 });/*
35214  * Based on:
35215  * Ext JS Library 1.1.1
35216  * Copyright(c) 2006-2007, Ext JS, LLC.
35217  *
35218  * Originally Released Under LGPL - original licence link has changed is not relivant.
35219  *
35220  * Fork - LGPL
35221  * <script type="text/javascript">
35222  */
35223  
35224 /**
35225  * @class Roo.menu.CheckItem
35226  * @extends Roo.menu.Item
35227  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35228  * @constructor
35229  * Creates a new CheckItem
35230  * @param {Object} config Configuration options
35231  */
35232 Roo.menu.CheckItem = function(config){
35233     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35234     this.addEvents({
35235         /**
35236          * @event beforecheckchange
35237          * Fires before the checked value is set, providing an opportunity to cancel if needed
35238          * @param {Roo.menu.CheckItem} this
35239          * @param {Boolean} checked The new checked value that will be set
35240          */
35241         "beforecheckchange" : true,
35242         /**
35243          * @event checkchange
35244          * Fires after the checked value has been set
35245          * @param {Roo.menu.CheckItem} this
35246          * @param {Boolean} checked The checked value that was set
35247          */
35248         "checkchange" : true
35249     });
35250     if(this.checkHandler){
35251         this.on('checkchange', this.checkHandler, this.scope);
35252     }
35253 };
35254 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35255     /**
35256      * @cfg {String} group
35257      * All check items with the same group name will automatically be grouped into a single-select
35258      * radio button group (defaults to '')
35259      */
35260     /**
35261      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35262      */
35263     itemCls : "x-menu-item x-menu-check-item",
35264     /**
35265      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35266      */
35267     groupClass : "x-menu-group-item",
35268
35269     /**
35270      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35271      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35272      * initialized with checked = true will be rendered as checked.
35273      */
35274     checked: false,
35275
35276     // private
35277     ctype: "Roo.menu.CheckItem",
35278
35279     // private
35280     onRender : function(c){
35281         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35282         if(this.group){
35283             this.el.addClass(this.groupClass);
35284         }
35285         Roo.menu.MenuMgr.registerCheckable(this);
35286         if(this.checked){
35287             this.checked = false;
35288             this.setChecked(true, true);
35289         }
35290     },
35291
35292     // private
35293     destroy : function(){
35294         if(this.rendered){
35295             Roo.menu.MenuMgr.unregisterCheckable(this);
35296         }
35297         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35298     },
35299
35300     /**
35301      * Set the checked state of this item
35302      * @param {Boolean} checked The new checked value
35303      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35304      */
35305     setChecked : function(state, suppressEvent){
35306         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35307             if(this.container){
35308                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35309             }
35310             this.checked = state;
35311             if(suppressEvent !== true){
35312                 this.fireEvent("checkchange", this, state);
35313             }
35314         }
35315     },
35316
35317     // private
35318     handleClick : function(e){
35319        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35320            this.setChecked(!this.checked);
35321        }
35322        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35323     }
35324 });/*
35325  * Based on:
35326  * Ext JS Library 1.1.1
35327  * Copyright(c) 2006-2007, Ext JS, LLC.
35328  *
35329  * Originally Released Under LGPL - original licence link has changed is not relivant.
35330  *
35331  * Fork - LGPL
35332  * <script type="text/javascript">
35333  */
35334  
35335 /**
35336  * @class Roo.menu.DateItem
35337  * @extends Roo.menu.Adapter
35338  * A menu item that wraps the {@link Roo.DatPicker} component.
35339  * @constructor
35340  * Creates a new DateItem
35341  * @param {Object} config Configuration options
35342  */
35343 Roo.menu.DateItem = function(config){
35344     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35345     /** The Roo.DatePicker object @type Roo.DatePicker */
35346     this.picker = this.component;
35347     this.addEvents({select: true});
35348     
35349     this.picker.on("render", function(picker){
35350         picker.getEl().swallowEvent("click");
35351         picker.container.addClass("x-menu-date-item");
35352     });
35353
35354     this.picker.on("select", this.onSelect, this);
35355 };
35356
35357 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35358     // private
35359     onSelect : function(picker, date){
35360         this.fireEvent("select", this, date, picker);
35361         Roo.menu.DateItem.superclass.handleClick.call(this);
35362     }
35363 });/*
35364  * Based on:
35365  * Ext JS Library 1.1.1
35366  * Copyright(c) 2006-2007, Ext JS, LLC.
35367  *
35368  * Originally Released Under LGPL - original licence link has changed is not relivant.
35369  *
35370  * Fork - LGPL
35371  * <script type="text/javascript">
35372  */
35373  
35374 /**
35375  * @class Roo.menu.ColorItem
35376  * @extends Roo.menu.Adapter
35377  * A menu item that wraps the {@link Roo.ColorPalette} component.
35378  * @constructor
35379  * Creates a new ColorItem
35380  * @param {Object} config Configuration options
35381  */
35382 Roo.menu.ColorItem = function(config){
35383     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35384     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35385     this.palette = this.component;
35386     this.relayEvents(this.palette, ["select"]);
35387     if(this.selectHandler){
35388         this.on('select', this.selectHandler, this.scope);
35389     }
35390 };
35391 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35392  * Based on:
35393  * Ext JS Library 1.1.1
35394  * Copyright(c) 2006-2007, Ext JS, LLC.
35395  *
35396  * Originally Released Under LGPL - original licence link has changed is not relivant.
35397  *
35398  * Fork - LGPL
35399  * <script type="text/javascript">
35400  */
35401  
35402
35403 /**
35404  * @class Roo.menu.DateMenu
35405  * @extends Roo.menu.Menu
35406  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35407  * @constructor
35408  * Creates a new DateMenu
35409  * @param {Object} config Configuration options
35410  */
35411 Roo.menu.DateMenu = function(config){
35412     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35413     this.plain = true;
35414     var di = new Roo.menu.DateItem(config);
35415     this.add(di);
35416     /**
35417      * The {@link Roo.DatePicker} instance for this DateMenu
35418      * @type DatePicker
35419      */
35420     this.picker = di.picker;
35421     /**
35422      * @event select
35423      * @param {DatePicker} picker
35424      * @param {Date} date
35425      */
35426     this.relayEvents(di, ["select"]);
35427
35428     this.on('beforeshow', function(){
35429         if(this.picker){
35430             this.picker.hideMonthPicker(true);
35431         }
35432     }, this);
35433 };
35434 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35435     cls:'x-date-menu'
35436 });/*
35437  * Based on:
35438  * Ext JS Library 1.1.1
35439  * Copyright(c) 2006-2007, Ext JS, LLC.
35440  *
35441  * Originally Released Under LGPL - original licence link has changed is not relivant.
35442  *
35443  * Fork - LGPL
35444  * <script type="text/javascript">
35445  */
35446  
35447
35448 /**
35449  * @class Roo.menu.ColorMenu
35450  * @extends Roo.menu.Menu
35451  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35452  * @constructor
35453  * Creates a new ColorMenu
35454  * @param {Object} config Configuration options
35455  */
35456 Roo.menu.ColorMenu = function(config){
35457     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35458     this.plain = true;
35459     var ci = new Roo.menu.ColorItem(config);
35460     this.add(ci);
35461     /**
35462      * The {@link Roo.ColorPalette} instance for this ColorMenu
35463      * @type ColorPalette
35464      */
35465     this.palette = ci.palette;
35466     /**
35467      * @event select
35468      * @param {ColorPalette} palette
35469      * @param {String} color
35470      */
35471     this.relayEvents(ci, ["select"]);
35472 };
35473 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35474  * Based on:
35475  * Ext JS Library 1.1.1
35476  * Copyright(c) 2006-2007, Ext JS, LLC.
35477  *
35478  * Originally Released Under LGPL - original licence link has changed is not relivant.
35479  *
35480  * Fork - LGPL
35481  * <script type="text/javascript">
35482  */
35483  
35484 /**
35485  * @class Roo.form.Field
35486  * @extends Roo.BoxComponent
35487  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35488  * @constructor
35489  * Creates a new Field
35490  * @param {Object} config Configuration options
35491  */
35492 Roo.form.Field = function(config){
35493     Roo.form.Field.superclass.constructor.call(this, config);
35494 };
35495
35496 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35497     /**
35498      * @cfg {String} fieldLabel Label to use when rendering a form.
35499      */
35500        /**
35501      * @cfg {String} qtip Mouse over tip
35502      */
35503      
35504     /**
35505      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35506      */
35507     invalidClass : "x-form-invalid",
35508     /**
35509      * @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")
35510      */
35511     invalidText : "The value in this field is invalid",
35512     /**
35513      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35514      */
35515     focusClass : "x-form-focus",
35516     /**
35517      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35518       automatic validation (defaults to "keyup").
35519      */
35520     validationEvent : "keyup",
35521     /**
35522      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35523      */
35524     validateOnBlur : true,
35525     /**
35526      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35527      */
35528     validationDelay : 250,
35529     /**
35530      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35531      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35532      */
35533     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35534     /**
35535      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35536      */
35537     fieldClass : "x-form-field",
35538     /**
35539      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35540      *<pre>
35541 Value         Description
35542 -----------   ----------------------------------------------------------------------
35543 qtip          Display a quick tip when the user hovers over the field
35544 title         Display a default browser title attribute popup
35545 under         Add a block div beneath the field containing the error text
35546 side          Add an error icon to the right of the field with a popup on hover
35547 [element id]  Add the error text directly to the innerHTML of the specified element
35548 </pre>
35549      */
35550     msgTarget : 'qtip',
35551     /**
35552      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35553      */
35554     msgFx : 'normal',
35555
35556     /**
35557      * @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.
35558      */
35559     readOnly : false,
35560
35561     /**
35562      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35563      */
35564     disabled : false,
35565
35566     /**
35567      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35568      */
35569     inputType : undefined,
35570     
35571     /**
35572      * @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).
35573          */
35574         tabIndex : undefined,
35575         
35576     // private
35577     isFormField : true,
35578
35579     // private
35580     hasFocus : false,
35581     /**
35582      * @property {Roo.Element} fieldEl
35583      * Element Containing the rendered Field (with label etc.)
35584      */
35585     /**
35586      * @cfg {Mixed} value A value to initialize this field with.
35587      */
35588     value : undefined,
35589
35590     /**
35591      * @cfg {String} name The field's HTML name attribute.
35592      */
35593     /**
35594      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35595      */
35596
35597         // private ??
35598         initComponent : function(){
35599         Roo.form.Field.superclass.initComponent.call(this);
35600         this.addEvents({
35601             /**
35602              * @event focus
35603              * Fires when this field receives input focus.
35604              * @param {Roo.form.Field} this
35605              */
35606             focus : true,
35607             /**
35608              * @event blur
35609              * Fires when this field loses input focus.
35610              * @param {Roo.form.Field} this
35611              */
35612             blur : true,
35613             /**
35614              * @event specialkey
35615              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35616              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35617              * @param {Roo.form.Field} this
35618              * @param {Roo.EventObject} e The event object
35619              */
35620             specialkey : true,
35621             /**
35622              * @event change
35623              * Fires just before the field blurs if the field value has changed.
35624              * @param {Roo.form.Field} this
35625              * @param {Mixed} newValue The new value
35626              * @param {Mixed} oldValue The original value
35627              */
35628             change : true,
35629             /**
35630              * @event invalid
35631              * Fires after the field has been marked as invalid.
35632              * @param {Roo.form.Field} this
35633              * @param {String} msg The validation message
35634              */
35635             invalid : true,
35636             /**
35637              * @event valid
35638              * Fires after the field has been validated with no errors.
35639              * @param {Roo.form.Field} this
35640              */
35641             valid : true,
35642              /**
35643              * @event keyup
35644              * Fires after the key up
35645              * @param {Roo.form.Field} this
35646              * @param {Roo.EventObject}  e The event Object
35647              */
35648             keyup : true
35649         });
35650     },
35651
35652     /**
35653      * Returns the name attribute of the field if available
35654      * @return {String} name The field name
35655      */
35656     getName: function(){
35657          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35658     },
35659
35660     // private
35661     onRender : function(ct, position){
35662         Roo.form.Field.superclass.onRender.call(this, ct, position);
35663         if(!this.el){
35664             var cfg = this.getAutoCreate();
35665             if(!cfg.name){
35666                 cfg.name = this.name || this.id;
35667             }
35668             if(this.inputType){
35669                 cfg.type = this.inputType;
35670             }
35671             this.el = ct.createChild(cfg, position);
35672         }
35673         var type = this.el.dom.type;
35674         if(type){
35675             if(type == 'password'){
35676                 type = 'text';
35677             }
35678             this.el.addClass('x-form-'+type);
35679         }
35680         if(this.readOnly){
35681             this.el.dom.readOnly = true;
35682         }
35683         if(this.tabIndex !== undefined){
35684             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35685         }
35686
35687         this.el.addClass([this.fieldClass, this.cls]);
35688         this.initValue();
35689     },
35690
35691     /**
35692      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35693      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35694      * @return {Roo.form.Field} this
35695      */
35696     applyTo : function(target){
35697         this.allowDomMove = false;
35698         this.el = Roo.get(target);
35699         this.render(this.el.dom.parentNode);
35700         return this;
35701     },
35702
35703     // private
35704     initValue : function(){
35705         if(this.value !== undefined){
35706             this.setValue(this.value);
35707         }else if(this.el.dom.value.length > 0){
35708             this.setValue(this.el.dom.value);
35709         }
35710     },
35711
35712     /**
35713      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35714      */
35715     isDirty : function() {
35716         if(this.disabled) {
35717             return false;
35718         }
35719         return String(this.getValue()) !== String(this.originalValue);
35720     },
35721
35722     // private
35723     afterRender : function(){
35724         Roo.form.Field.superclass.afterRender.call(this);
35725         this.initEvents();
35726     },
35727
35728     // private
35729     fireKey : function(e){
35730         //Roo.log('field ' + e.getKey());
35731         if(e.isNavKeyPress()){
35732             this.fireEvent("specialkey", this, e);
35733         }
35734     },
35735
35736     /**
35737      * Resets the current field value to the originally loaded value and clears any validation messages
35738      */
35739     reset : function(){
35740         this.setValue(this.originalValue);
35741         this.clearInvalid();
35742     },
35743
35744     // private
35745     initEvents : function(){
35746         // safari killled keypress - so keydown is now used..
35747         this.el.on("keydown" , this.fireKey,  this);
35748         this.el.on("focus", this.onFocus,  this);
35749         this.el.on("blur", this.onBlur,  this);
35750         this.el.relayEvent('keyup', this);
35751
35752         // reference to original value for reset
35753         this.originalValue = this.getValue();
35754     },
35755
35756     // private
35757     onFocus : function(){
35758         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35759             this.el.addClass(this.focusClass);
35760         }
35761         if(!this.hasFocus){
35762             this.hasFocus = true;
35763             this.startValue = this.getValue();
35764             this.fireEvent("focus", this);
35765         }
35766     },
35767
35768     beforeBlur : Roo.emptyFn,
35769
35770     // private
35771     onBlur : function(){
35772         this.beforeBlur();
35773         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35774             this.el.removeClass(this.focusClass);
35775         }
35776         this.hasFocus = false;
35777         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35778             this.validate();
35779         }
35780         var v = this.getValue();
35781         if(String(v) !== String(this.startValue)){
35782             this.fireEvent('change', this, v, this.startValue);
35783         }
35784         this.fireEvent("blur", this);
35785     },
35786
35787     /**
35788      * Returns whether or not the field value is currently valid
35789      * @param {Boolean} preventMark True to disable marking the field invalid
35790      * @return {Boolean} True if the value is valid, else false
35791      */
35792     isValid : function(preventMark){
35793         if(this.disabled){
35794             return true;
35795         }
35796         var restore = this.preventMark;
35797         this.preventMark = preventMark === true;
35798         var v = this.validateValue(this.processValue(this.getRawValue()));
35799         this.preventMark = restore;
35800         return v;
35801     },
35802
35803     /**
35804      * Validates the field value
35805      * @return {Boolean} True if the value is valid, else false
35806      */
35807     validate : function(){
35808         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35809             this.clearInvalid();
35810             return true;
35811         }
35812         return false;
35813     },
35814
35815     processValue : function(value){
35816         return value;
35817     },
35818
35819     // private
35820     // Subclasses should provide the validation implementation by overriding this
35821     validateValue : function(value){
35822         return true;
35823     },
35824
35825     /**
35826      * Mark this field as invalid
35827      * @param {String} msg The validation message
35828      */
35829     markInvalid : function(msg){
35830         if(!this.rendered || this.preventMark){ // not rendered
35831             return;
35832         }
35833         this.el.addClass(this.invalidClass);
35834         msg = msg || this.invalidText;
35835         switch(this.msgTarget){
35836             case 'qtip':
35837                 this.el.dom.qtip = msg;
35838                 this.el.dom.qclass = 'x-form-invalid-tip';
35839                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35840                     Roo.QuickTips.enable();
35841                 }
35842                 break;
35843             case 'title':
35844                 this.el.dom.title = msg;
35845                 break;
35846             case 'under':
35847                 if(!this.errorEl){
35848                     var elp = this.el.findParent('.x-form-element', 5, true);
35849                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35850                     this.errorEl.setWidth(elp.getWidth(true)-20);
35851                 }
35852                 this.errorEl.update(msg);
35853                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35854                 break;
35855             case 'side':
35856                 if(!this.errorIcon){
35857                     var elp = this.el.findParent('.x-form-element', 5, true);
35858                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35859                 }
35860                 this.alignErrorIcon();
35861                 this.errorIcon.dom.qtip = msg;
35862                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35863                 this.errorIcon.show();
35864                 this.on('resize', this.alignErrorIcon, this);
35865                 break;
35866             default:
35867                 var t = Roo.getDom(this.msgTarget);
35868                 t.innerHTML = msg;
35869                 t.style.display = this.msgDisplay;
35870                 break;
35871         }
35872         this.fireEvent('invalid', this, msg);
35873     },
35874
35875     // private
35876     alignErrorIcon : function(){
35877         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35878     },
35879
35880     /**
35881      * Clear any invalid styles/messages for this field
35882      */
35883     clearInvalid : function(){
35884         if(!this.rendered || this.preventMark){ // not rendered
35885             return;
35886         }
35887         this.el.removeClass(this.invalidClass);
35888         switch(this.msgTarget){
35889             case 'qtip':
35890                 this.el.dom.qtip = '';
35891                 break;
35892             case 'title':
35893                 this.el.dom.title = '';
35894                 break;
35895             case 'under':
35896                 if(this.errorEl){
35897                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35898                 }
35899                 break;
35900             case 'side':
35901                 if(this.errorIcon){
35902                     this.errorIcon.dom.qtip = '';
35903                     this.errorIcon.hide();
35904                     this.un('resize', this.alignErrorIcon, this);
35905                 }
35906                 break;
35907             default:
35908                 var t = Roo.getDom(this.msgTarget);
35909                 t.innerHTML = '';
35910                 t.style.display = 'none';
35911                 break;
35912         }
35913         this.fireEvent('valid', this);
35914     },
35915
35916     /**
35917      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35918      * @return {Mixed} value The field value
35919      */
35920     getRawValue : function(){
35921         var v = this.el.getValue();
35922         if(v === this.emptyText){
35923             v = '';
35924         }
35925         return v;
35926     },
35927
35928     /**
35929      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35930      * @return {Mixed} value The field value
35931      */
35932     getValue : function(){
35933         var v = this.el.getValue();
35934         if(v === this.emptyText || v === undefined){
35935             v = '';
35936         }
35937         return v;
35938     },
35939
35940     /**
35941      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35942      * @param {Mixed} value The value to set
35943      */
35944     setRawValue : function(v){
35945         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35946     },
35947
35948     /**
35949      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35950      * @param {Mixed} value The value to set
35951      */
35952     setValue : function(v){
35953         this.value = v;
35954         if(this.rendered){
35955             this.el.dom.value = (v === null || v === undefined ? '' : v);
35956             this.validate();
35957         }
35958     },
35959
35960     adjustSize : function(w, h){
35961         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35962         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35963         return s;
35964     },
35965
35966     adjustWidth : function(tag, w){
35967         tag = tag.toLowerCase();
35968         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35969             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35970                 if(tag == 'input'){
35971                     return w + 2;
35972                 }
35973                 if(tag = 'textarea'){
35974                     return w-2;
35975                 }
35976             }else if(Roo.isOpera){
35977                 if(tag == 'input'){
35978                     return w + 2;
35979                 }
35980                 if(tag = 'textarea'){
35981                     return w-2;
35982                 }
35983             }
35984         }
35985         return w;
35986     }
35987 });
35988
35989
35990 // anything other than normal should be considered experimental
35991 Roo.form.Field.msgFx = {
35992     normal : {
35993         show: function(msgEl, f){
35994             msgEl.setDisplayed('block');
35995         },
35996
35997         hide : function(msgEl, f){
35998             msgEl.setDisplayed(false).update('');
35999         }
36000     },
36001
36002     slide : {
36003         show: function(msgEl, f){
36004             msgEl.slideIn('t', {stopFx:true});
36005         },
36006
36007         hide : function(msgEl, f){
36008             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36009         }
36010     },
36011
36012     slideRight : {
36013         show: function(msgEl, f){
36014             msgEl.fixDisplay();
36015             msgEl.alignTo(f.el, 'tl-tr');
36016             msgEl.slideIn('l', {stopFx:true});
36017         },
36018
36019         hide : function(msgEl, f){
36020             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36021         }
36022     }
36023 };/*
36024  * Based on:
36025  * Ext JS Library 1.1.1
36026  * Copyright(c) 2006-2007, Ext JS, LLC.
36027  *
36028  * Originally Released Under LGPL - original licence link has changed is not relivant.
36029  *
36030  * Fork - LGPL
36031  * <script type="text/javascript">
36032  */
36033  
36034
36035 /**
36036  * @class Roo.form.TextField
36037  * @extends Roo.form.Field
36038  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36039  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36040  * @constructor
36041  * Creates a new TextField
36042  * @param {Object} config Configuration options
36043  */
36044 Roo.form.TextField = function(config){
36045     Roo.form.TextField.superclass.constructor.call(this, config);
36046     this.addEvents({
36047         /**
36048          * @event autosize
36049          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36050          * according to the default logic, but this event provides a hook for the developer to apply additional
36051          * logic at runtime to resize the field if needed.
36052              * @param {Roo.form.Field} this This text field
36053              * @param {Number} width The new field width
36054              */
36055         autosize : true
36056     });
36057 };
36058
36059 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36060     /**
36061      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36062      */
36063     grow : false,
36064     /**
36065      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36066      */
36067     growMin : 30,
36068     /**
36069      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36070      */
36071     growMax : 800,
36072     /**
36073      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36074      */
36075     vtype : null,
36076     /**
36077      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36078      */
36079     maskRe : null,
36080     /**
36081      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36082      */
36083     disableKeyFilter : false,
36084     /**
36085      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36086      */
36087     allowBlank : true,
36088     /**
36089      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36090      */
36091     minLength : 0,
36092     /**
36093      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36094      */
36095     maxLength : Number.MAX_VALUE,
36096     /**
36097      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36098      */
36099     minLengthText : "The minimum length for this field is {0}",
36100     /**
36101      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36102      */
36103     maxLengthText : "The maximum length for this field is {0}",
36104     /**
36105      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36106      */
36107     selectOnFocus : false,
36108     /**
36109      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36110      */
36111     blankText : "This field is required",
36112     /**
36113      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36114      * If available, this function will be called only after the basic validators all return true, and will be passed the
36115      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36116      */
36117     validator : null,
36118     /**
36119      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36120      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36121      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36122      */
36123     regex : null,
36124     /**
36125      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36126      */
36127     regexText : "",
36128     /**
36129      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36130      */
36131     emptyText : null,
36132     /**
36133      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36134      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36135      */
36136     emptyClass : 'x-form-empty-field',
36137
36138     // private
36139     initEvents : function(){
36140         Roo.form.TextField.superclass.initEvents.call(this);
36141         if(this.validationEvent == 'keyup'){
36142             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36143             this.el.on('keyup', this.filterValidation, this);
36144         }
36145         else if(this.validationEvent !== false){
36146             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36147         }
36148         if(this.selectOnFocus || this.emptyText){
36149             this.on("focus", this.preFocus, this);
36150             if(this.emptyText){
36151                 this.on('blur', this.postBlur, this);
36152                 this.applyEmptyText();
36153             }
36154         }
36155         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36156             this.el.on("keypress", this.filterKeys, this);
36157         }
36158         if(this.grow){
36159             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36160             this.el.on("click", this.autoSize,  this);
36161         }
36162     },
36163
36164     processValue : function(value){
36165         if(this.stripCharsRe){
36166             var newValue = value.replace(this.stripCharsRe, '');
36167             if(newValue !== value){
36168                 this.setRawValue(newValue);
36169                 return newValue;
36170             }
36171         }
36172         return value;
36173     },
36174
36175     filterValidation : function(e){
36176         if(!e.isNavKeyPress()){
36177             this.validationTask.delay(this.validationDelay);
36178         }
36179     },
36180
36181     // private
36182     onKeyUp : function(e){
36183         if(!e.isNavKeyPress()){
36184             this.autoSize();
36185         }
36186     },
36187
36188     /**
36189      * Resets the current field value to the originally-loaded value and clears any validation messages.
36190      * Also adds emptyText and emptyClass if the original value was blank.
36191      */
36192     reset : function(){
36193         Roo.form.TextField.superclass.reset.call(this);
36194         this.applyEmptyText();
36195     },
36196
36197     applyEmptyText : function(){
36198         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36199             this.setRawValue(this.emptyText);
36200             this.el.addClass(this.emptyClass);
36201         }
36202     },
36203
36204     // private
36205     preFocus : function(){
36206         if(this.emptyText){
36207             if(this.el.dom.value == this.emptyText){
36208                 this.setRawValue('');
36209             }
36210             this.el.removeClass(this.emptyClass);
36211         }
36212         if(this.selectOnFocus){
36213             this.el.dom.select();
36214         }
36215     },
36216
36217     // private
36218     postBlur : function(){
36219         this.applyEmptyText();
36220     },
36221
36222     // private
36223     filterKeys : function(e){
36224         var k = e.getKey();
36225         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36226             return;
36227         }
36228         var c = e.getCharCode(), cc = String.fromCharCode(c);
36229         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36230             return;
36231         }
36232         if(!this.maskRe.test(cc)){
36233             e.stopEvent();
36234         }
36235     },
36236
36237     setValue : function(v){
36238         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36239             this.el.removeClass(this.emptyClass);
36240         }
36241         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36242         this.applyEmptyText();
36243         this.autoSize();
36244     },
36245
36246     /**
36247      * Validates a value according to the field's validation rules and marks the field as invalid
36248      * if the validation fails
36249      * @param {Mixed} value The value to validate
36250      * @return {Boolean} True if the value is valid, else false
36251      */
36252     validateValue : function(value){
36253         if(value.length < 1 || value === this.emptyText){ // if it's blank
36254              if(this.allowBlank){
36255                 this.clearInvalid();
36256                 return true;
36257              }else{
36258                 this.markInvalid(this.blankText);
36259                 return false;
36260              }
36261         }
36262         if(value.length < this.minLength){
36263             this.markInvalid(String.format(this.minLengthText, this.minLength));
36264             return false;
36265         }
36266         if(value.length > this.maxLength){
36267             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36268             return false;
36269         }
36270         if(this.vtype){
36271             var vt = Roo.form.VTypes;
36272             if(!vt[this.vtype](value, this)){
36273                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36274                 return false;
36275             }
36276         }
36277         if(typeof this.validator == "function"){
36278             var msg = this.validator(value);
36279             if(msg !== true){
36280                 this.markInvalid(msg);
36281                 return false;
36282             }
36283         }
36284         if(this.regex && !this.regex.test(value)){
36285             this.markInvalid(this.regexText);
36286             return false;
36287         }
36288         return true;
36289     },
36290
36291     /**
36292      * Selects text in this field
36293      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36294      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36295      */
36296     selectText : function(start, end){
36297         var v = this.getRawValue();
36298         if(v.length > 0){
36299             start = start === undefined ? 0 : start;
36300             end = end === undefined ? v.length : end;
36301             var d = this.el.dom;
36302             if(d.setSelectionRange){
36303                 d.setSelectionRange(start, end);
36304             }else if(d.createTextRange){
36305                 var range = d.createTextRange();
36306                 range.moveStart("character", start);
36307                 range.moveEnd("character", v.length-end);
36308                 range.select();
36309             }
36310         }
36311     },
36312
36313     /**
36314      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36315      * This only takes effect if grow = true, and fires the autosize event.
36316      */
36317     autoSize : function(){
36318         if(!this.grow || !this.rendered){
36319             return;
36320         }
36321         if(!this.metrics){
36322             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36323         }
36324         var el = this.el;
36325         var v = el.dom.value;
36326         var d = document.createElement('div');
36327         d.appendChild(document.createTextNode(v));
36328         v = d.innerHTML;
36329         d = null;
36330         v += "&#160;";
36331         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36332         this.el.setWidth(w);
36333         this.fireEvent("autosize", this, w);
36334     }
36335 });/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345  
36346 /**
36347  * @class Roo.form.Hidden
36348  * @extends Roo.form.TextField
36349  * Simple Hidden element used on forms 
36350  * 
36351  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36352  * 
36353  * @constructor
36354  * Creates a new Hidden form element.
36355  * @param {Object} config Configuration options
36356  */
36357
36358
36359
36360 // easy hidden field...
36361 Roo.form.Hidden = function(config){
36362     Roo.form.Hidden.superclass.constructor.call(this, config);
36363 };
36364   
36365 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36366     fieldLabel:      '',
36367     inputType:      'hidden',
36368     width:          50,
36369     allowBlank:     true,
36370     labelSeparator: '',
36371     hidden:         true,
36372     itemCls :       'x-form-item-display-none'
36373
36374
36375 });
36376
36377
36378 /*
36379  * Based on:
36380  * Ext JS Library 1.1.1
36381  * Copyright(c) 2006-2007, Ext JS, LLC.
36382  *
36383  * Originally Released Under LGPL - original licence link has changed is not relivant.
36384  *
36385  * Fork - LGPL
36386  * <script type="text/javascript">
36387  */
36388  
36389 /**
36390  * @class Roo.form.TriggerField
36391  * @extends Roo.form.TextField
36392  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36393  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36394  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36395  * for which you can provide a custom implementation.  For example:
36396  * <pre><code>
36397 var trigger = new Roo.form.TriggerField();
36398 trigger.onTriggerClick = myTriggerFn;
36399 trigger.applyTo('my-field');
36400 </code></pre>
36401  *
36402  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36403  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36404  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36405  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36406  * @constructor
36407  * Create a new TriggerField.
36408  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36409  * to the base TextField)
36410  */
36411 Roo.form.TriggerField = function(config){
36412     this.mimicing = false;
36413     Roo.form.TriggerField.superclass.constructor.call(this, config);
36414 };
36415
36416 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36417     /**
36418      * @cfg {String} triggerClass A CSS class to apply to the trigger
36419      */
36420     /**
36421      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36422      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36423      */
36424     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36425     /**
36426      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36427      */
36428     hideTrigger:false,
36429
36430     /** @cfg {Boolean} grow @hide */
36431     /** @cfg {Number} growMin @hide */
36432     /** @cfg {Number} growMax @hide */
36433
36434     /**
36435      * @hide 
36436      * @method
36437      */
36438     autoSize: Roo.emptyFn,
36439     // private
36440     monitorTab : true,
36441     // private
36442     deferHeight : true,
36443
36444     
36445     actionMode : 'wrap',
36446     // private
36447     onResize : function(w, h){
36448         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36449         if(typeof w == 'number'){
36450             var x = w - this.trigger.getWidth();
36451             this.el.setWidth(this.adjustWidth('input', x));
36452             this.trigger.setStyle('left', x+'px');
36453         }
36454     },
36455
36456     // private
36457     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36458
36459     // private
36460     getResizeEl : function(){
36461         return this.wrap;
36462     },
36463
36464     // private
36465     getPositionEl : function(){
36466         return this.wrap;
36467     },
36468
36469     // private
36470     alignErrorIcon : function(){
36471         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36472     },
36473
36474     // private
36475     onRender : function(ct, position){
36476         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36477         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36478         this.trigger = this.wrap.createChild(this.triggerConfig ||
36479                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36480         if(this.hideTrigger){
36481             this.trigger.setDisplayed(false);
36482         }
36483         this.initTrigger();
36484         if(!this.width){
36485             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36486         }
36487     },
36488
36489     // private
36490     initTrigger : function(){
36491         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36492         this.trigger.addClassOnOver('x-form-trigger-over');
36493         this.trigger.addClassOnClick('x-form-trigger-click');
36494     },
36495
36496     // private
36497     onDestroy : function(){
36498         if(this.trigger){
36499             this.trigger.removeAllListeners();
36500             this.trigger.remove();
36501         }
36502         if(this.wrap){
36503             this.wrap.remove();
36504         }
36505         Roo.form.TriggerField.superclass.onDestroy.call(this);
36506     },
36507
36508     // private
36509     onFocus : function(){
36510         Roo.form.TriggerField.superclass.onFocus.call(this);
36511         if(!this.mimicing){
36512             this.wrap.addClass('x-trigger-wrap-focus');
36513             this.mimicing = true;
36514             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36515             if(this.monitorTab){
36516                 this.el.on("keydown", this.checkTab, this);
36517             }
36518         }
36519     },
36520
36521     // private
36522     checkTab : function(e){
36523         if(e.getKey() == e.TAB){
36524             this.triggerBlur();
36525         }
36526     },
36527
36528     // private
36529     onBlur : function(){
36530         // do nothing
36531     },
36532
36533     // private
36534     mimicBlur : function(e, t){
36535         if(!this.wrap.contains(t) && this.validateBlur()){
36536             this.triggerBlur();
36537         }
36538     },
36539
36540     // private
36541     triggerBlur : function(){
36542         this.mimicing = false;
36543         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36544         if(this.monitorTab){
36545             this.el.un("keydown", this.checkTab, this);
36546         }
36547         this.wrap.removeClass('x-trigger-wrap-focus');
36548         Roo.form.TriggerField.superclass.onBlur.call(this);
36549     },
36550
36551     // private
36552     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36553     validateBlur : function(e, t){
36554         return true;
36555     },
36556
36557     // private
36558     onDisable : function(){
36559         Roo.form.TriggerField.superclass.onDisable.call(this);
36560         if(this.wrap){
36561             this.wrap.addClass('x-item-disabled');
36562         }
36563     },
36564
36565     // private
36566     onEnable : function(){
36567         Roo.form.TriggerField.superclass.onEnable.call(this);
36568         if(this.wrap){
36569             this.wrap.removeClass('x-item-disabled');
36570         }
36571     },
36572
36573     // private
36574     onShow : function(){
36575         var ae = this.getActionEl();
36576         
36577         if(ae){
36578             ae.dom.style.display = '';
36579             ae.dom.style.visibility = 'visible';
36580         }
36581     },
36582
36583     // private
36584     
36585     onHide : function(){
36586         var ae = this.getActionEl();
36587         ae.dom.style.display = 'none';
36588     },
36589
36590     /**
36591      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36592      * by an implementing function.
36593      * @method
36594      * @param {EventObject} e
36595      */
36596     onTriggerClick : Roo.emptyFn
36597 });
36598
36599 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36600 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36601 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36602 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36603     initComponent : function(){
36604         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36605
36606         this.triggerConfig = {
36607             tag:'span', cls:'x-form-twin-triggers', cn:[
36608             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36609             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36610         ]};
36611     },
36612
36613     getTrigger : function(index){
36614         return this.triggers[index];
36615     },
36616
36617     initTrigger : function(){
36618         var ts = this.trigger.select('.x-form-trigger', true);
36619         this.wrap.setStyle('overflow', 'hidden');
36620         var triggerField = this;
36621         ts.each(function(t, all, index){
36622             t.hide = function(){
36623                 var w = triggerField.wrap.getWidth();
36624                 this.dom.style.display = 'none';
36625                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36626             };
36627             t.show = function(){
36628                 var w = triggerField.wrap.getWidth();
36629                 this.dom.style.display = '';
36630                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36631             };
36632             var triggerIndex = 'Trigger'+(index+1);
36633
36634             if(this['hide'+triggerIndex]){
36635                 t.dom.style.display = 'none';
36636             }
36637             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36638             t.addClassOnOver('x-form-trigger-over');
36639             t.addClassOnClick('x-form-trigger-click');
36640         }, this);
36641         this.triggers = ts.elements;
36642     },
36643
36644     onTrigger1Click : Roo.emptyFn,
36645     onTrigger2Click : Roo.emptyFn
36646 });/*
36647  * Based on:
36648  * Ext JS Library 1.1.1
36649  * Copyright(c) 2006-2007, Ext JS, LLC.
36650  *
36651  * Originally Released Under LGPL - original licence link has changed is not relivant.
36652  *
36653  * Fork - LGPL
36654  * <script type="text/javascript">
36655  */
36656  
36657 /**
36658  * @class Roo.form.TextArea
36659  * @extends Roo.form.TextField
36660  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36661  * support for auto-sizing.
36662  * @constructor
36663  * Creates a new TextArea
36664  * @param {Object} config Configuration options
36665  */
36666 Roo.form.TextArea = function(config){
36667     Roo.form.TextArea.superclass.constructor.call(this, config);
36668     // these are provided exchanges for backwards compat
36669     // minHeight/maxHeight were replaced by growMin/growMax to be
36670     // compatible with TextField growing config values
36671     if(this.minHeight !== undefined){
36672         this.growMin = this.minHeight;
36673     }
36674     if(this.maxHeight !== undefined){
36675         this.growMax = this.maxHeight;
36676     }
36677 };
36678
36679 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36680     /**
36681      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36682      */
36683     growMin : 60,
36684     /**
36685      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36686      */
36687     growMax: 1000,
36688     /**
36689      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36690      * in the field (equivalent to setting overflow: hidden, defaults to false)
36691      */
36692     preventScrollbars: false,
36693     /**
36694      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36695      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36696      */
36697
36698     // private
36699     onRender : function(ct, position){
36700         if(!this.el){
36701             this.defaultAutoCreate = {
36702                 tag: "textarea",
36703                 style:"width:300px;height:60px;",
36704                 autocomplete: "off"
36705             };
36706         }
36707         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36708         if(this.grow){
36709             this.textSizeEl = Roo.DomHelper.append(document.body, {
36710                 tag: "pre", cls: "x-form-grow-sizer"
36711             });
36712             if(this.preventScrollbars){
36713                 this.el.setStyle("overflow", "hidden");
36714             }
36715             this.el.setHeight(this.growMin);
36716         }
36717     },
36718
36719     onDestroy : function(){
36720         if(this.textSizeEl){
36721             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36722         }
36723         Roo.form.TextArea.superclass.onDestroy.call(this);
36724     },
36725
36726     // private
36727     onKeyUp : function(e){
36728         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36729             this.autoSize();
36730         }
36731     },
36732
36733     /**
36734      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36735      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36736      */
36737     autoSize : function(){
36738         if(!this.grow || !this.textSizeEl){
36739             return;
36740         }
36741         var el = this.el;
36742         var v = el.dom.value;
36743         var ts = this.textSizeEl;
36744
36745         ts.innerHTML = '';
36746         ts.appendChild(document.createTextNode(v));
36747         v = ts.innerHTML;
36748
36749         Roo.fly(ts).setWidth(this.el.getWidth());
36750         if(v.length < 1){
36751             v = "&#160;&#160;";
36752         }else{
36753             if(Roo.isIE){
36754                 v = v.replace(/\n/g, '<p>&#160;</p>');
36755             }
36756             v += "&#160;\n&#160;";
36757         }
36758         ts.innerHTML = v;
36759         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36760         if(h != this.lastHeight){
36761             this.lastHeight = h;
36762             this.el.setHeight(h);
36763             this.fireEvent("autosize", this, h);
36764         }
36765     }
36766 });/*
36767  * Based on:
36768  * Ext JS Library 1.1.1
36769  * Copyright(c) 2006-2007, Ext JS, LLC.
36770  *
36771  * Originally Released Under LGPL - original licence link has changed is not relivant.
36772  *
36773  * Fork - LGPL
36774  * <script type="text/javascript">
36775  */
36776  
36777
36778 /**
36779  * @class Roo.form.NumberField
36780  * @extends Roo.form.TextField
36781  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36782  * @constructor
36783  * Creates a new NumberField
36784  * @param {Object} config Configuration options
36785  */
36786 Roo.form.NumberField = function(config){
36787     Roo.form.NumberField.superclass.constructor.call(this, config);
36788 };
36789
36790 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36791     /**
36792      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36793      */
36794     fieldClass: "x-form-field x-form-num-field",
36795     /**
36796      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36797      */
36798     allowDecimals : true,
36799     /**
36800      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36801      */
36802     decimalSeparator : ".",
36803     /**
36804      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36805      */
36806     decimalPrecision : 2,
36807     /**
36808      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36809      */
36810     allowNegative : true,
36811     /**
36812      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36813      */
36814     minValue : Number.NEGATIVE_INFINITY,
36815     /**
36816      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36817      */
36818     maxValue : Number.MAX_VALUE,
36819     /**
36820      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36821      */
36822     minText : "The minimum value for this field is {0}",
36823     /**
36824      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36825      */
36826     maxText : "The maximum value for this field is {0}",
36827     /**
36828      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36829      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36830      */
36831     nanText : "{0} is not a valid number",
36832
36833     // private
36834     initEvents : function(){
36835         Roo.form.NumberField.superclass.initEvents.call(this);
36836         var allowed = "0123456789";
36837         if(this.allowDecimals){
36838             allowed += this.decimalSeparator;
36839         }
36840         if(this.allowNegative){
36841             allowed += "-";
36842         }
36843         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36844         var keyPress = function(e){
36845             var k = e.getKey();
36846             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36847                 return;
36848             }
36849             var c = e.getCharCode();
36850             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36851                 e.stopEvent();
36852             }
36853         };
36854         this.el.on("keypress", keyPress, this);
36855     },
36856
36857     // private
36858     validateValue : function(value){
36859         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36860             return false;
36861         }
36862         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36863              return true;
36864         }
36865         var num = this.parseValue(value);
36866         if(isNaN(num)){
36867             this.markInvalid(String.format(this.nanText, value));
36868             return false;
36869         }
36870         if(num < this.minValue){
36871             this.markInvalid(String.format(this.minText, this.minValue));
36872             return false;
36873         }
36874         if(num > this.maxValue){
36875             this.markInvalid(String.format(this.maxText, this.maxValue));
36876             return false;
36877         }
36878         return true;
36879     },
36880
36881     getValue : function(){
36882         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36883     },
36884
36885     // private
36886     parseValue : function(value){
36887         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36888         return isNaN(value) ? '' : value;
36889     },
36890
36891     // private
36892     fixPrecision : function(value){
36893         var nan = isNaN(value);
36894         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36895             return nan ? '' : value;
36896         }
36897         return parseFloat(value).toFixed(this.decimalPrecision);
36898     },
36899
36900     setValue : function(v){
36901         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36902     },
36903
36904     // private
36905     decimalPrecisionFcn : function(v){
36906         return Math.floor(v);
36907     },
36908
36909     beforeBlur : function(){
36910         var v = this.parseValue(this.getRawValue());
36911         if(v){
36912             this.setValue(this.fixPrecision(v));
36913         }
36914     }
36915 });/*
36916  * Based on:
36917  * Ext JS Library 1.1.1
36918  * Copyright(c) 2006-2007, Ext JS, LLC.
36919  *
36920  * Originally Released Under LGPL - original licence link has changed is not relivant.
36921  *
36922  * Fork - LGPL
36923  * <script type="text/javascript">
36924  */
36925  
36926 /**
36927  * @class Roo.form.DateField
36928  * @extends Roo.form.TriggerField
36929  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36930 * @constructor
36931 * Create a new DateField
36932 * @param {Object} config
36933  */
36934 Roo.form.DateField = function(config){
36935     Roo.form.DateField.superclass.constructor.call(this, config);
36936     
36937       this.addEvents({
36938          
36939         /**
36940          * @event select
36941          * Fires when a date is selected
36942              * @param {Roo.form.DateField} combo This combo box
36943              * @param {Date} date The date selected
36944              */
36945         'select' : true
36946          
36947     });
36948     
36949     
36950     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36951     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36952     this.ddMatch = null;
36953     if(this.disabledDates){
36954         var dd = this.disabledDates;
36955         var re = "(?:";
36956         for(var i = 0; i < dd.length; i++){
36957             re += dd[i];
36958             if(i != dd.length-1) re += "|";
36959         }
36960         this.ddMatch = new RegExp(re + ")");
36961     }
36962 };
36963
36964 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36965     /**
36966      * @cfg {String} format
36967      * The default date format string which can be overriden for localization support.  The format must be
36968      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36969      */
36970     format : "m/d/y",
36971     /**
36972      * @cfg {String} altFormats
36973      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36974      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36975      */
36976     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36977     /**
36978      * @cfg {Array} disabledDays
36979      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36980      */
36981     disabledDays : null,
36982     /**
36983      * @cfg {String} disabledDaysText
36984      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36985      */
36986     disabledDaysText : "Disabled",
36987     /**
36988      * @cfg {Array} disabledDates
36989      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36990      * expression so they are very powerful. Some examples:
36991      * <ul>
36992      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36993      * <li>["03/08", "09/16"] would disable those days for every year</li>
36994      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36995      * <li>["03/../2006"] would disable every day in March 2006</li>
36996      * <li>["^03"] would disable every day in every March</li>
36997      * </ul>
36998      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36999      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37000      */
37001     disabledDates : null,
37002     /**
37003      * @cfg {String} disabledDatesText
37004      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37005      */
37006     disabledDatesText : "Disabled",
37007     /**
37008      * @cfg {Date/String} minValue
37009      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37010      * valid format (defaults to null).
37011      */
37012     minValue : null,
37013     /**
37014      * @cfg {Date/String} maxValue
37015      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37016      * valid format (defaults to null).
37017      */
37018     maxValue : null,
37019     /**
37020      * @cfg {String} minText
37021      * The error text to display when the date in the cell is before minValue (defaults to
37022      * 'The date in this field must be after {minValue}').
37023      */
37024     minText : "The date in this field must be equal to or after {0}",
37025     /**
37026      * @cfg {String} maxText
37027      * The error text to display when the date in the cell is after maxValue (defaults to
37028      * 'The date in this field must be before {maxValue}').
37029      */
37030     maxText : "The date in this field must be equal to or before {0}",
37031     /**
37032      * @cfg {String} invalidText
37033      * The error text to display when the date in the field is invalid (defaults to
37034      * '{value} is not a valid date - it must be in the format {format}').
37035      */
37036     invalidText : "{0} is not a valid date - it must be in the format {1}",
37037     /**
37038      * @cfg {String} triggerClass
37039      * An additional CSS class used to style the trigger button.  The trigger will always get the
37040      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37041      * which displays a calendar icon).
37042      */
37043     triggerClass : 'x-form-date-trigger',
37044     
37045
37046     /**
37047      * @cfg {bool} useIso
37048      * if enabled, then the date field will use a hidden field to store the 
37049      * real value as iso formated date. default (false)
37050      */ 
37051     useIso : false,
37052     /**
37053      * @cfg {String/Object} autoCreate
37054      * A DomHelper element spec, or true for a default element spec (defaults to
37055      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37056      */ 
37057     // private
37058     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37059     
37060     // private
37061     hiddenField: false,
37062     
37063     onRender : function(ct, position)
37064     {
37065         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37066         if (this.useIso) {
37067             this.el.dom.removeAttribute('name'); 
37068             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37069                     'before', true);
37070             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37071             // prevent input submission
37072             this.hiddenName = this.name;
37073         }
37074             
37075             
37076     },
37077     
37078     // private
37079     validateValue : function(value)
37080     {
37081         value = this.formatDate(value);
37082         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37083             return false;
37084         }
37085         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37086              return true;
37087         }
37088         var svalue = value;
37089         value = this.parseDate(value);
37090         if(!value){
37091             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37092             return false;
37093         }
37094         var time = value.getTime();
37095         if(this.minValue && time < this.minValue.getTime()){
37096             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37097             return false;
37098         }
37099         if(this.maxValue && time > this.maxValue.getTime()){
37100             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37101             return false;
37102         }
37103         if(this.disabledDays){
37104             var day = value.getDay();
37105             for(var i = 0; i < this.disabledDays.length; i++) {
37106                 if(day === this.disabledDays[i]){
37107                     this.markInvalid(this.disabledDaysText);
37108                     return false;
37109                 }
37110             }
37111         }
37112         var fvalue = this.formatDate(value);
37113         if(this.ddMatch && this.ddMatch.test(fvalue)){
37114             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37115             return false;
37116         }
37117         return true;
37118     },
37119
37120     // private
37121     // Provides logic to override the default TriggerField.validateBlur which just returns true
37122     validateBlur : function(){
37123         return !this.menu || !this.menu.isVisible();
37124     },
37125
37126     /**
37127      * Returns the current date value of the date field.
37128      * @return {Date} The date value
37129      */
37130     getValue : function(){
37131         
37132         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37133     },
37134
37135     /**
37136      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37137      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37138      * (the default format used is "m/d/y").
37139      * <br />Usage:
37140      * <pre><code>
37141 //All of these calls set the same date value (May 4, 2006)
37142
37143 //Pass a date object:
37144 var dt = new Date('5/4/06');
37145 dateField.setValue(dt);
37146
37147 //Pass a date string (default format):
37148 dateField.setValue('5/4/06');
37149
37150 //Pass a date string (custom format):
37151 dateField.format = 'Y-m-d';
37152 dateField.setValue('2006-5-4');
37153 </code></pre>
37154      * @param {String/Date} date The date or valid date string
37155      */
37156     setValue : function(date){
37157         if (this.hiddenField) {
37158             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37159         }
37160         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37161     },
37162
37163     // private
37164     parseDate : function(value){
37165         if(!value || value instanceof Date){
37166             return value;
37167         }
37168         var v = Date.parseDate(value, this.format);
37169         if(!v && this.altFormats){
37170             if(!this.altFormatsArray){
37171                 this.altFormatsArray = this.altFormats.split("|");
37172             }
37173             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37174                 v = Date.parseDate(value, this.altFormatsArray[i]);
37175             }
37176         }
37177         return v;
37178     },
37179
37180     // private
37181     formatDate : function(date, fmt){
37182         return (!date || !(date instanceof Date)) ?
37183                date : date.dateFormat(fmt || this.format);
37184     },
37185
37186     // private
37187     menuListeners : {
37188         select: function(m, d){
37189             this.setValue(d);
37190             this.fireEvent('select', this, d);
37191         },
37192         show : function(){ // retain focus styling
37193             this.onFocus();
37194         },
37195         hide : function(){
37196             this.focus.defer(10, this);
37197             var ml = this.menuListeners;
37198             this.menu.un("select", ml.select,  this);
37199             this.menu.un("show", ml.show,  this);
37200             this.menu.un("hide", ml.hide,  this);
37201         }
37202     },
37203
37204     // private
37205     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37206     onTriggerClick : function(){
37207         if(this.disabled){
37208             return;
37209         }
37210         if(this.menu == null){
37211             this.menu = new Roo.menu.DateMenu();
37212         }
37213         Roo.apply(this.menu.picker,  {
37214             showClear: this.allowBlank,
37215             minDate : this.minValue,
37216             maxDate : this.maxValue,
37217             disabledDatesRE : this.ddMatch,
37218             disabledDatesText : this.disabledDatesText,
37219             disabledDays : this.disabledDays,
37220             disabledDaysText : this.disabledDaysText,
37221             format : this.format,
37222             minText : String.format(this.minText, this.formatDate(this.minValue)),
37223             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37224         });
37225         this.menu.on(Roo.apply({}, this.menuListeners, {
37226             scope:this
37227         }));
37228         this.menu.picker.setValue(this.getValue() || new Date());
37229         this.menu.show(this.el, "tl-bl?");
37230     },
37231
37232     beforeBlur : function(){
37233         var v = this.parseDate(this.getRawValue());
37234         if(v){
37235             this.setValue(v);
37236         }
37237     }
37238
37239     /** @cfg {Boolean} grow @hide */
37240     /** @cfg {Number} growMin @hide */
37241     /** @cfg {Number} growMax @hide */
37242     /**
37243      * @hide
37244      * @method autoSize
37245      */
37246 });/*
37247  * Based on:
37248  * Ext JS Library 1.1.1
37249  * Copyright(c) 2006-2007, Ext JS, LLC.
37250  *
37251  * Originally Released Under LGPL - original licence link has changed is not relivant.
37252  *
37253  * Fork - LGPL
37254  * <script type="text/javascript">
37255  */
37256  
37257
37258 /**
37259  * @class Roo.form.ComboBox
37260  * @extends Roo.form.TriggerField
37261  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37262  * @constructor
37263  * Create a new ComboBox.
37264  * @param {Object} config Configuration options
37265  */
37266 Roo.form.ComboBox = function(config){
37267     Roo.form.ComboBox.superclass.constructor.call(this, config);
37268     this.addEvents({
37269         /**
37270          * @event expand
37271          * Fires when the dropdown list is expanded
37272              * @param {Roo.form.ComboBox} combo This combo box
37273              */
37274         'expand' : true,
37275         /**
37276          * @event collapse
37277          * Fires when the dropdown list is collapsed
37278              * @param {Roo.form.ComboBox} combo This combo box
37279              */
37280         'collapse' : true,
37281         /**
37282          * @event beforeselect
37283          * Fires before a list item is selected. Return false to cancel the selection.
37284              * @param {Roo.form.ComboBox} combo This combo box
37285              * @param {Roo.data.Record} record The data record returned from the underlying store
37286              * @param {Number} index The index of the selected item in the dropdown list
37287              */
37288         'beforeselect' : true,
37289         /**
37290          * @event select
37291          * Fires when a list item is selected
37292              * @param {Roo.form.ComboBox} combo This combo box
37293              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37294              * @param {Number} index The index of the selected item in the dropdown list
37295              */
37296         'select' : true,
37297         /**
37298          * @event beforequery
37299          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37300          * The event object passed has these properties:
37301              * @param {Roo.form.ComboBox} combo This combo box
37302              * @param {String} query The query
37303              * @param {Boolean} forceAll true to force "all" query
37304              * @param {Boolean} cancel true to cancel the query
37305              * @param {Object} e The query event object
37306              */
37307         'beforequery': true,
37308          /**
37309          * @event add
37310          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37311              * @param {Roo.form.ComboBox} combo This combo box
37312              */
37313         'add' : true,
37314         /**
37315          * @event edit
37316          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37317              * @param {Roo.form.ComboBox} combo This combo box
37318              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37319              */
37320         'edit' : true
37321         
37322         
37323     });
37324     if(this.transform){
37325         this.allowDomMove = false;
37326         var s = Roo.getDom(this.transform);
37327         if(!this.hiddenName){
37328             this.hiddenName = s.name;
37329         }
37330         if(!this.store){
37331             this.mode = 'local';
37332             var d = [], opts = s.options;
37333             for(var i = 0, len = opts.length;i < len; i++){
37334                 var o = opts[i];
37335                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37336                 if(o.selected) {
37337                     this.value = value;
37338                 }
37339                 d.push([value, o.text]);
37340             }
37341             this.store = new Roo.data.SimpleStore({
37342                 'id': 0,
37343                 fields: ['value', 'text'],
37344                 data : d
37345             });
37346             this.valueField = 'value';
37347             this.displayField = 'text';
37348         }
37349         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37350         if(!this.lazyRender){
37351             this.target = true;
37352             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37353             s.parentNode.removeChild(s); // remove it
37354             this.render(this.el.parentNode);
37355         }else{
37356             s.parentNode.removeChild(s); // remove it
37357         }
37358
37359     }
37360     if (this.store) {
37361         this.store = Roo.factory(this.store, Roo.data);
37362     }
37363     
37364     this.selectedIndex = -1;
37365     if(this.mode == 'local'){
37366         if(config.queryDelay === undefined){
37367             this.queryDelay = 10;
37368         }
37369         if(config.minChars === undefined){
37370             this.minChars = 0;
37371         }
37372     }
37373 };
37374
37375 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37376     /**
37377      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37378      */
37379     /**
37380      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37381      * rendering into an Roo.Editor, defaults to false)
37382      */
37383     /**
37384      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37385      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37386      */
37387     /**
37388      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37389      */
37390     /**
37391      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37392      * the dropdown list (defaults to undefined, with no header element)
37393      */
37394
37395      /**
37396      * @cfg {String/Roo.Template} tpl The template to use to render the output
37397      */
37398      
37399     // private
37400     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37401     /**
37402      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37403      */
37404     listWidth: undefined,
37405     /**
37406      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37407      * mode = 'remote' or 'text' if mode = 'local')
37408      */
37409     displayField: undefined,
37410     /**
37411      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37412      * mode = 'remote' or 'value' if mode = 'local'). 
37413      * Note: use of a valueField requires the user make a selection
37414      * in order for a value to be mapped.
37415      */
37416     valueField: undefined,
37417     /**
37418      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37419      * field's data value (defaults to the underlying DOM element's name)
37420      */
37421     hiddenName: undefined,
37422     /**
37423      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37424      */
37425     listClass: '',
37426     /**
37427      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37428      */
37429     selectedClass: 'x-combo-selected',
37430     /**
37431      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37432      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37433      * which displays a downward arrow icon).
37434      */
37435     triggerClass : 'x-form-arrow-trigger',
37436     /**
37437      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37438      */
37439     shadow:'sides',
37440     /**
37441      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37442      * anchor positions (defaults to 'tl-bl')
37443      */
37444     listAlign: 'tl-bl?',
37445     /**
37446      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37447      */
37448     maxHeight: 300,
37449     /**
37450      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37451      * query specified by the allQuery config option (defaults to 'query')
37452      */
37453     triggerAction: 'query',
37454     /**
37455      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37456      * (defaults to 4, does not apply if editable = false)
37457      */
37458     minChars : 4,
37459     /**
37460      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37461      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37462      */
37463     typeAhead: false,
37464     /**
37465      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37466      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37467      */
37468     queryDelay: 500,
37469     /**
37470      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37471      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37472      */
37473     pageSize: 0,
37474     /**
37475      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37476      * when editable = true (defaults to false)
37477      */
37478     selectOnFocus:false,
37479     /**
37480      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37481      */
37482     queryParam: 'query',
37483     /**
37484      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37485      * when mode = 'remote' (defaults to 'Loading...')
37486      */
37487     loadingText: 'Loading...',
37488     /**
37489      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37490      */
37491     resizable: false,
37492     /**
37493      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37494      */
37495     handleHeight : 8,
37496     /**
37497      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37498      * traditional select (defaults to true)
37499      */
37500     editable: true,
37501     /**
37502      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37503      */
37504     allQuery: '',
37505     /**
37506      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37507      */
37508     mode: 'remote',
37509     /**
37510      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37511      * listWidth has a higher value)
37512      */
37513     minListWidth : 70,
37514     /**
37515      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37516      * allow the user to set arbitrary text into the field (defaults to false)
37517      */
37518     forceSelection:false,
37519     /**
37520      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37521      * if typeAhead = true (defaults to 250)
37522      */
37523     typeAheadDelay : 250,
37524     /**
37525      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37526      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37527      */
37528     valueNotFoundText : undefined,
37529     /**
37530      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37531      */
37532     blockFocus : false,
37533     
37534     /**
37535      * @cfg {Boolean} disableClear Disable showing of clear button.
37536      */
37537     disableClear : false,
37538     /**
37539      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37540      */
37541     alwaysQuery : false,
37542     
37543     //private
37544     addicon : false,
37545     editicon: false,
37546     
37547     
37548     // private
37549     onRender : function(ct, position){
37550         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37551         if(this.hiddenName){
37552             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37553                     'before', true);
37554             this.hiddenField.value =
37555                 this.hiddenValue !== undefined ? this.hiddenValue :
37556                 this.value !== undefined ? this.value : '';
37557
37558             // prevent input submission
37559             this.el.dom.removeAttribute('name');
37560         }
37561         if(Roo.isGecko){
37562             this.el.dom.setAttribute('autocomplete', 'off');
37563         }
37564
37565         var cls = 'x-combo-list';
37566
37567         this.list = new Roo.Layer({
37568             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37569         });
37570
37571         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37572         this.list.setWidth(lw);
37573         this.list.swallowEvent('mousewheel');
37574         this.assetHeight = 0;
37575
37576         if(this.title){
37577             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37578             this.assetHeight += this.header.getHeight();
37579         }
37580
37581         this.innerList = this.list.createChild({cls:cls+'-inner'});
37582         this.innerList.on('mouseover', this.onViewOver, this);
37583         this.innerList.on('mousemove', this.onViewMove, this);
37584         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37585         
37586         if(this.allowBlank && !this.pageSize && !this.disableClear){
37587             this.footer = this.list.createChild({cls:cls+'-ft'});
37588             this.pageTb = new Roo.Toolbar(this.footer);
37589            
37590         }
37591         if(this.pageSize){
37592             this.footer = this.list.createChild({cls:cls+'-ft'});
37593             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37594                     {pageSize: this.pageSize});
37595             
37596         }
37597         
37598         if (this.pageTb && this.allowBlank && !this.disableClear) {
37599             var _this = this;
37600             this.pageTb.add(new Roo.Toolbar.Fill(), {
37601                 cls: 'x-btn-icon x-btn-clear',
37602                 text: '&#160;',
37603                 handler: function()
37604                 {
37605                     _this.collapse();
37606                     _this.clearValue();
37607                     _this.onSelect(false, -1);
37608                 }
37609             });
37610         }
37611         if (this.footer) {
37612             this.assetHeight += this.footer.getHeight();
37613         }
37614         
37615
37616         if(!this.tpl){
37617             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37618         }
37619
37620         this.view = new Roo.View(this.innerList, this.tpl, {
37621             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37622         });
37623
37624         this.view.on('click', this.onViewClick, this);
37625
37626         this.store.on('beforeload', this.onBeforeLoad, this);
37627         this.store.on('load', this.onLoad, this);
37628         this.store.on('loadexception', this.collapse, this);
37629
37630         if(this.resizable){
37631             this.resizer = new Roo.Resizable(this.list,  {
37632                pinned:true, handles:'se'
37633             });
37634             this.resizer.on('resize', function(r, w, h){
37635                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37636                 this.listWidth = w;
37637                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37638                 this.restrictHeight();
37639             }, this);
37640             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37641         }
37642         if(!this.editable){
37643             this.editable = true;
37644             this.setEditable(false);
37645         }  
37646         
37647         
37648         if (typeof(this.events.add.listeners) != 'undefined') {
37649             
37650             this.addicon = this.wrap.createChild(
37651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37652        
37653             this.addicon.on('click', function(e) {
37654                 this.fireEvent('add', this);
37655             }, this);
37656         }
37657         if (typeof(this.events.edit.listeners) != 'undefined') {
37658             
37659             this.editicon = this.wrap.createChild(
37660                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37661             if (this.addicon) {
37662                 this.editicon.setStyle('margin-left', '40px');
37663             }
37664             this.editicon.on('click', function(e) {
37665                 
37666                 // we fire even  if inothing is selected..
37667                 this.fireEvent('edit', this, this.lastData );
37668                 
37669             }, this);
37670         }
37671         
37672         
37673         
37674     },
37675
37676     // private
37677     initEvents : function(){
37678         Roo.form.ComboBox.superclass.initEvents.call(this);
37679
37680         this.keyNav = new Roo.KeyNav(this.el, {
37681             "up" : function(e){
37682                 this.inKeyMode = true;
37683                 this.selectPrev();
37684             },
37685
37686             "down" : function(e){
37687                 if(!this.isExpanded()){
37688                     this.onTriggerClick();
37689                 }else{
37690                     this.inKeyMode = true;
37691                     this.selectNext();
37692                 }
37693             },
37694
37695             "enter" : function(e){
37696                 this.onViewClick();
37697                 //return true;
37698             },
37699
37700             "esc" : function(e){
37701                 this.collapse();
37702             },
37703
37704             "tab" : function(e){
37705                 this.onViewClick(false);
37706                 this.fireEvent("specialkey", this, e);
37707                 return true;
37708             },
37709
37710             scope : this,
37711
37712             doRelay : function(foo, bar, hname){
37713                 if(hname == 'down' || this.scope.isExpanded()){
37714                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37715                 }
37716                 return true;
37717             },
37718
37719             forceKeyDown: true
37720         });
37721         this.queryDelay = Math.max(this.queryDelay || 10,
37722                 this.mode == 'local' ? 10 : 250);
37723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37724         if(this.typeAhead){
37725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37726         }
37727         if(this.editable !== false){
37728             this.el.on("keyup", this.onKeyUp, this);
37729         }
37730         if(this.forceSelection){
37731             this.on('blur', this.doForce, this);
37732         }
37733     },
37734
37735     onDestroy : function(){
37736         if(this.view){
37737             this.view.setStore(null);
37738             this.view.el.removeAllListeners();
37739             this.view.el.remove();
37740             this.view.purgeListeners();
37741         }
37742         if(this.list){
37743             this.list.destroy();
37744         }
37745         if(this.store){
37746             this.store.un('beforeload', this.onBeforeLoad, this);
37747             this.store.un('load', this.onLoad, this);
37748             this.store.un('loadexception', this.collapse, this);
37749         }
37750         Roo.form.ComboBox.superclass.onDestroy.call(this);
37751     },
37752
37753     // private
37754     fireKey : function(e){
37755         if(e.isNavKeyPress() && !this.list.isVisible()){
37756             this.fireEvent("specialkey", this, e);
37757         }
37758     },
37759
37760     // private
37761     onResize: function(w, h){
37762         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37763         
37764         if(typeof w != 'number'){
37765             // we do not handle it!?!?
37766             return;
37767         }
37768         var tw = this.trigger.getWidth();
37769         tw += this.addicon ? this.addicon.getWidth() : 0;
37770         tw += this.editicon ? this.editicon.getWidth() : 0;
37771         var x = w - tw;
37772         this.el.setWidth( this.adjustWidth('input', x));
37773             
37774         this.trigger.setStyle('left', x+'px');
37775         
37776         if(this.list && this.listWidth === undefined){
37777             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37778             this.list.setWidth(lw);
37779             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37780         }
37781         
37782     
37783         
37784     },
37785
37786     /**
37787      * Allow or prevent the user from directly editing the field text.  If false is passed,
37788      * the user will only be able to select from the items defined in the dropdown list.  This method
37789      * is the runtime equivalent of setting the 'editable' config option at config time.
37790      * @param {Boolean} value True to allow the user to directly edit the field text
37791      */
37792     setEditable : function(value){
37793         if(value == this.editable){
37794             return;
37795         }
37796         this.editable = value;
37797         if(!value){
37798             this.el.dom.setAttribute('readOnly', true);
37799             this.el.on('mousedown', this.onTriggerClick,  this);
37800             this.el.addClass('x-combo-noedit');
37801         }else{
37802             this.el.dom.setAttribute('readOnly', false);
37803             this.el.un('mousedown', this.onTriggerClick,  this);
37804             this.el.removeClass('x-combo-noedit');
37805         }
37806     },
37807
37808     // private
37809     onBeforeLoad : function(){
37810         if(!this.hasFocus){
37811             return;
37812         }
37813         this.innerList.update(this.loadingText ?
37814                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37815         this.restrictHeight();
37816         this.selectedIndex = -1;
37817     },
37818
37819     // private
37820     onLoad : function(){
37821         if(!this.hasFocus){
37822             return;
37823         }
37824         if(this.store.getCount() > 0){
37825             this.expand();
37826             this.restrictHeight();
37827             if(this.lastQuery == this.allQuery){
37828                 if(this.editable){
37829                     this.el.dom.select();
37830                 }
37831                 if(!this.selectByValue(this.value, true)){
37832                     this.select(0, true);
37833                 }
37834             }else{
37835                 this.selectNext();
37836                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37837                     this.taTask.delay(this.typeAheadDelay);
37838                 }
37839             }
37840         }else{
37841             this.onEmptyResults();
37842         }
37843         //this.el.focus();
37844     },
37845
37846     // private
37847     onTypeAhead : function(){
37848         if(this.store.getCount() > 0){
37849             var r = this.store.getAt(0);
37850             var newValue = r.data[this.displayField];
37851             var len = newValue.length;
37852             var selStart = this.getRawValue().length;
37853             if(selStart != len){
37854                 this.setRawValue(newValue);
37855                 this.selectText(selStart, newValue.length);
37856             }
37857         }
37858     },
37859
37860     // private
37861     onSelect : function(record, index){
37862         if(this.fireEvent('beforeselect', this, record, index) !== false){
37863             this.setFromData(index > -1 ? record.data : false);
37864             this.collapse();
37865             this.fireEvent('select', this, record, index);
37866         }
37867     },
37868
37869     /**
37870      * Returns the currently selected field value or empty string if no value is set.
37871      * @return {String} value The selected value
37872      */
37873     getValue : function(){
37874         if(this.valueField){
37875             return typeof this.value != 'undefined' ? this.value : '';
37876         }else{
37877             return Roo.form.ComboBox.superclass.getValue.call(this);
37878         }
37879     },
37880
37881     /**
37882      * Clears any text/value currently set in the field
37883      */
37884     clearValue : function(){
37885         if(this.hiddenField){
37886             this.hiddenField.value = '';
37887         }
37888         this.value = '';
37889         this.setRawValue('');
37890         this.lastSelectionText = '';
37891         this.applyEmptyText();
37892     },
37893
37894     /**
37895      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37896      * will be displayed in the field.  If the value does not match the data value of an existing item,
37897      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37898      * Otherwise the field will be blank (although the value will still be set).
37899      * @param {String} value The value to match
37900      */
37901     setValue : function(v){
37902         var text = v;
37903         if(this.valueField){
37904             var r = this.findRecord(this.valueField, v);
37905             if(r){
37906                 text = r.data[this.displayField];
37907             }else if(this.valueNotFoundText !== undefined){
37908                 text = this.valueNotFoundText;
37909             }
37910         }
37911         this.lastSelectionText = text;
37912         if(this.hiddenField){
37913             this.hiddenField.value = v;
37914         }
37915         Roo.form.ComboBox.superclass.setValue.call(this, text);
37916         this.value = v;
37917     },
37918     /**
37919      * @property {Object} the last set data for the element
37920      */
37921     
37922     lastData : false,
37923     /**
37924      * Sets the value of the field based on a object which is related to the record format for the store.
37925      * @param {Object} value the value to set as. or false on reset?
37926      */
37927     setFromData : function(o){
37928         var dv = ''; // display value
37929         var vv = ''; // value value..
37930         this.lastData = o;
37931         if (this.displayField) {
37932             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37933         } else {
37934             // this is an error condition!!!
37935             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37936         }
37937         
37938         if(this.valueField){
37939             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37940         }
37941         if(this.hiddenField){
37942             this.hiddenField.value = vv;
37943             
37944             this.lastSelectionText = dv;
37945             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37946             this.value = vv;
37947             return;
37948         }
37949         // no hidden field.. - we store the value in 'value', but still display
37950         // display field!!!!
37951         this.lastSelectionText = dv;
37952         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37953         this.value = vv;
37954         
37955         
37956     },
37957     // private
37958     reset : function(){
37959         // overridden so that last data is reset..
37960         this.setValue(this.originalValue);
37961         this.clearInvalid();
37962         this.lastData = false;
37963     },
37964     // private
37965     findRecord : function(prop, value){
37966         var record;
37967         if(this.store.getCount() > 0){
37968             this.store.each(function(r){
37969                 if(r.data[prop] == value){
37970                     record = r;
37971                     return false;
37972                 }
37973             });
37974         }
37975         return record;
37976     },
37977
37978     // private
37979     onViewMove : function(e, t){
37980         this.inKeyMode = false;
37981     },
37982
37983     // private
37984     onViewOver : function(e, t){
37985         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37986             return;
37987         }
37988         var item = this.view.findItemFromChild(t);
37989         if(item){
37990             var index = this.view.indexOf(item);
37991             this.select(index, false);
37992         }
37993     },
37994
37995     // private
37996     onViewClick : function(doFocus)
37997     {
37998         var index = this.view.getSelectedIndexes()[0];
37999         var r = this.store.getAt(index);
38000         if(r){
38001             this.onSelect(r, index);
38002         }
38003         if(doFocus !== false && !this.blockFocus){
38004             this.el.focus();
38005         }
38006     },
38007
38008     // private
38009     restrictHeight : function(){
38010         this.innerList.dom.style.height = '';
38011         var inner = this.innerList.dom;
38012         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38013         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38014         this.list.beginUpdate();
38015         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38016         this.list.alignTo(this.el, this.listAlign);
38017         this.list.endUpdate();
38018     },
38019
38020     // private
38021     onEmptyResults : function(){
38022         this.collapse();
38023     },
38024
38025     /**
38026      * Returns true if the dropdown list is expanded, else false.
38027      */
38028     isExpanded : function(){
38029         return this.list.isVisible();
38030     },
38031
38032     /**
38033      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38034      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38035      * @param {String} value The data value of the item to select
38036      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38037      * selected item if it is not currently in view (defaults to true)
38038      * @return {Boolean} True if the value matched an item in the list, else false
38039      */
38040     selectByValue : function(v, scrollIntoView){
38041         if(v !== undefined && v !== null){
38042             var r = this.findRecord(this.valueField || this.displayField, v);
38043             if(r){
38044                 this.select(this.store.indexOf(r), scrollIntoView);
38045                 return true;
38046             }
38047         }
38048         return false;
38049     },
38050
38051     /**
38052      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38053      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38054      * @param {Number} index The zero-based index of the list item to select
38055      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38056      * selected item if it is not currently in view (defaults to true)
38057      */
38058     select : function(index, scrollIntoView){
38059         this.selectedIndex = index;
38060         this.view.select(index);
38061         if(scrollIntoView !== false){
38062             var el = this.view.getNode(index);
38063             if(el){
38064                 this.innerList.scrollChildIntoView(el, false);
38065             }
38066         }
38067     },
38068
38069     // private
38070     selectNext : function(){
38071         var ct = this.store.getCount();
38072         if(ct > 0){
38073             if(this.selectedIndex == -1){
38074                 this.select(0);
38075             }else if(this.selectedIndex < ct-1){
38076                 this.select(this.selectedIndex+1);
38077             }
38078         }
38079     },
38080
38081     // private
38082     selectPrev : function(){
38083         var ct = this.store.getCount();
38084         if(ct > 0){
38085             if(this.selectedIndex == -1){
38086                 this.select(0);
38087             }else if(this.selectedIndex != 0){
38088                 this.select(this.selectedIndex-1);
38089             }
38090         }
38091     },
38092
38093     // private
38094     onKeyUp : function(e){
38095         if(this.editable !== false && !e.isSpecialKey()){
38096             this.lastKey = e.getKey();
38097             this.dqTask.delay(this.queryDelay);
38098         }
38099     },
38100
38101     // private
38102     validateBlur : function(){
38103         return !this.list || !this.list.isVisible();   
38104     },
38105
38106     // private
38107     initQuery : function(){
38108         this.doQuery(this.getRawValue());
38109     },
38110
38111     // private
38112     doForce : function(){
38113         if(this.el.dom.value.length > 0){
38114             this.el.dom.value =
38115                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38116             this.applyEmptyText();
38117         }
38118     },
38119
38120     /**
38121      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38122      * query allowing the query action to be canceled if needed.
38123      * @param {String} query The SQL query to execute
38124      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38125      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38126      * saved in the current store (defaults to false)
38127      */
38128     doQuery : function(q, forceAll){
38129         if(q === undefined || q === null){
38130             q = '';
38131         }
38132         var qe = {
38133             query: q,
38134             forceAll: forceAll,
38135             combo: this,
38136             cancel:false
38137         };
38138         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38139             return false;
38140         }
38141         q = qe.query;
38142         forceAll = qe.forceAll;
38143         if(forceAll === true || (q.length >= this.minChars)){
38144             if(this.lastQuery != q || this.alwaysQuery){
38145                 this.lastQuery = q;
38146                 if(this.mode == 'local'){
38147                     this.selectedIndex = -1;
38148                     if(forceAll){
38149                         this.store.clearFilter();
38150                     }else{
38151                         this.store.filter(this.displayField, q);
38152                     }
38153                     this.onLoad();
38154                 }else{
38155                     this.store.baseParams[this.queryParam] = q;
38156                     this.store.load({
38157                         params: this.getParams(q)
38158                     });
38159                     this.expand();
38160                 }
38161             }else{
38162                 this.selectedIndex = -1;
38163                 this.onLoad();   
38164             }
38165         }
38166     },
38167
38168     // private
38169     getParams : function(q){
38170         var p = {};
38171         //p[this.queryParam] = q;
38172         if(this.pageSize){
38173             p.start = 0;
38174             p.limit = this.pageSize;
38175         }
38176         return p;
38177     },
38178
38179     /**
38180      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38181      */
38182     collapse : function(){
38183         if(!this.isExpanded()){
38184             return;
38185         }
38186         this.list.hide();
38187         Roo.get(document).un('mousedown', this.collapseIf, this);
38188         Roo.get(document).un('mousewheel', this.collapseIf, this);
38189         if (!this.editable) {
38190             Roo.get(document).un('keydown', this.listKeyPress, this);
38191         }
38192         this.fireEvent('collapse', this);
38193     },
38194
38195     // private
38196     collapseIf : function(e){
38197         if(!e.within(this.wrap) && !e.within(this.list)){
38198             this.collapse();
38199         }
38200     },
38201
38202     /**
38203      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38204      */
38205     expand : function(){
38206         if(this.isExpanded() || !this.hasFocus){
38207             return;
38208         }
38209         this.list.alignTo(this.el, this.listAlign);
38210         this.list.show();
38211         Roo.get(document).on('mousedown', this.collapseIf, this);
38212         Roo.get(document).on('mousewheel', this.collapseIf, this);
38213         if (!this.editable) {
38214             Roo.get(document).on('keydown', this.listKeyPress, this);
38215         }
38216         
38217         this.fireEvent('expand', this);
38218     },
38219
38220     // private
38221     // Implements the default empty TriggerField.onTriggerClick function
38222     onTriggerClick : function(){
38223         if(this.disabled){
38224             return;
38225         }
38226         if(this.isExpanded()){
38227             this.collapse();
38228             if (!this.blockFocus) {
38229                 this.el.focus();
38230             }
38231             
38232         }else {
38233             this.hasFocus = true;
38234             if(this.triggerAction == 'all') {
38235                 this.doQuery(this.allQuery, true);
38236             } else {
38237                 this.doQuery(this.getRawValue());
38238             }
38239             if (!this.blockFocus) {
38240                 this.el.focus();
38241             }
38242         }
38243     },
38244     listKeyPress : function(e)
38245     {
38246         //Roo.log('listkeypress');
38247         // scroll to first matching element based on key pres..
38248         if (e.isSpecialKey()) {
38249             return false;
38250         }
38251         var k = String.fromCharCode(e.getKey()).toUpperCase();
38252         //Roo.log(k);
38253         var match  = false;
38254         var csel = this.view.getSelectedNodes();
38255         var cselitem = false;
38256         if (csel.length) {
38257             var ix = this.view.indexOf(csel[0]);
38258             cselitem  = this.store.getAt(ix);
38259             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38260                 cselitem = false;
38261             }
38262             
38263         }
38264         
38265         this.store.each(function(v) { 
38266             if (cselitem) {
38267                 // start at existing selection.
38268                 if (cselitem.id == v.id) {
38269                     cselitem = false;
38270                 }
38271                 return;
38272             }
38273                 
38274             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38275                 match = this.store.indexOf(v);
38276                 return false;
38277             }
38278         }, this);
38279         
38280         if (match === false) {
38281             return true; // no more action?
38282         }
38283         // scroll to?
38284         this.view.select(match);
38285         var sn = Roo.get(this.view.getSelectedNodes()[0])
38286         sn.scrollIntoView(sn.dom.parentNode, false);
38287     }
38288
38289     /** 
38290     * @cfg {Boolean} grow 
38291     * @hide 
38292     */
38293     /** 
38294     * @cfg {Number} growMin 
38295     * @hide 
38296     */
38297     /** 
38298     * @cfg {Number} growMax 
38299     * @hide 
38300     */
38301     /**
38302      * @hide
38303      * @method autoSize
38304      */
38305 });/*
38306  * Based on:
38307  * Ext JS Library 1.1.1
38308  * Copyright(c) 2006-2007, Ext JS, LLC.
38309  *
38310  * Originally Released Under LGPL - original licence link has changed is not relivant.
38311  *
38312  * Fork - LGPL
38313  * <script type="text/javascript">
38314  */
38315 /**
38316  * @class Roo.form.Checkbox
38317  * @extends Roo.form.Field
38318  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38319  * @constructor
38320  * Creates a new Checkbox
38321  * @param {Object} config Configuration options
38322  */
38323 Roo.form.Checkbox = function(config){
38324     Roo.form.Checkbox.superclass.constructor.call(this, config);
38325     this.addEvents({
38326         /**
38327          * @event check
38328          * Fires when the checkbox is checked or unchecked.
38329              * @param {Roo.form.Checkbox} this This checkbox
38330              * @param {Boolean} checked The new checked value
38331              */
38332         check : true
38333     });
38334 };
38335
38336 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38337     /**
38338      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38339      */
38340     focusClass : undefined,
38341     /**
38342      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38343      */
38344     fieldClass: "x-form-field",
38345     /**
38346      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38347      */
38348     checked: false,
38349     /**
38350      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38351      * {tag: "input", type: "checkbox", autocomplete: "off"})
38352      */
38353     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38354     /**
38355      * @cfg {String} boxLabel The text that appears beside the checkbox
38356      */
38357     boxLabel : "",
38358     /**
38359      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38360      */  
38361     inputValue : '1',
38362     /**
38363      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38364      */
38365      valueOff: '0', // value when not checked..
38366
38367     actionMode : 'viewEl', 
38368     //
38369     // private
38370     itemCls : 'x-menu-check-item x-form-item',
38371     groupClass : 'x-menu-group-item',
38372     inputType : 'hidden',
38373     
38374     
38375     inSetChecked: false, // check that we are not calling self...
38376     
38377     inputElement: false, // real input element?
38378     basedOn: false, // ????
38379     
38380     isFormField: true, // not sure where this is needed!!!!
38381
38382     onResize : function(){
38383         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38384         if(!this.boxLabel){
38385             this.el.alignTo(this.wrap, 'c-c');
38386         }
38387     },
38388
38389     initEvents : function(){
38390         Roo.form.Checkbox.superclass.initEvents.call(this);
38391         this.el.on("click", this.onClick,  this);
38392         this.el.on("change", this.onClick,  this);
38393     },
38394
38395
38396     getResizeEl : function(){
38397         return this.wrap;
38398     },
38399
38400     getPositionEl : function(){
38401         return this.wrap;
38402     },
38403
38404     // private
38405     onRender : function(ct, position){
38406         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38407         /*
38408         if(this.inputValue !== undefined){
38409             this.el.dom.value = this.inputValue;
38410         }
38411         */
38412         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38413         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38414         var viewEl = this.wrap.createChild({ 
38415             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38416         this.viewEl = viewEl;   
38417         this.wrap.on('click', this.onClick,  this); 
38418         
38419         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38420         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38421         
38422         
38423         
38424         if(this.boxLabel){
38425             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38426         //    viewEl.on('click', this.onClick,  this); 
38427         }
38428         //if(this.checked){
38429             this.setChecked(this.checked);
38430         //}else{
38431             //this.checked = this.el.dom;
38432         //}
38433
38434     },
38435
38436     // private
38437     initValue : Roo.emptyFn,
38438
38439     /**
38440      * Returns the checked state of the checkbox.
38441      * @return {Boolean} True if checked, else false
38442      */
38443     getValue : function(){
38444         if(this.el){
38445             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38446         }
38447         return this.valueOff;
38448         
38449     },
38450
38451         // private
38452     onClick : function(){ 
38453         this.setChecked(!this.checked);
38454
38455         //if(this.el.dom.checked != this.checked){
38456         //    this.setValue(this.el.dom.checked);
38457        // }
38458     },
38459
38460     /**
38461      * Sets the checked state of the checkbox.
38462      * On is always based on a string comparison between inputValue and the param.
38463      * @param {Boolean/String} value - the value to set 
38464      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38465      */
38466     setValue : function(v,suppressEvent){
38467         
38468         
38469         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38470         //if(this.el && this.el.dom){
38471         //    this.el.dom.checked = this.checked;
38472         //    this.el.dom.defaultChecked = this.checked;
38473         //}
38474         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38475         //this.fireEvent("check", this, this.checked);
38476     },
38477     // private..
38478     setChecked : function(state,suppressEvent)
38479     {
38480         if (this.inSetChecked) {
38481             this.checked = state;
38482             return;
38483         }
38484         
38485     
38486         if(this.wrap){
38487             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38488         }
38489         this.checked = state;
38490         if(suppressEvent !== true){
38491             this.fireEvent('check', this, state);
38492         }
38493         this.inSetChecked = true;
38494         this.el.dom.value = state ? this.inputValue : this.valueOff;
38495         this.inSetChecked = false;
38496         
38497     },
38498     // handle setting of hidden value by some other method!!?!?
38499     setFromHidden: function()
38500     {
38501         if(!this.el){
38502             return;
38503         }
38504         //console.log("SET FROM HIDDEN");
38505         //alert('setFrom hidden');
38506         this.setValue(this.el.dom.value);
38507     },
38508     
38509     onDestroy : function()
38510     {
38511         if(this.viewEl){
38512             Roo.get(this.viewEl).remove();
38513         }
38514          
38515         Roo.form.Checkbox.superclass.onDestroy.call(this);
38516     }
38517
38518 });/*
38519  * Based on:
38520  * Ext JS Library 1.1.1
38521  * Copyright(c) 2006-2007, Ext JS, LLC.
38522  *
38523  * Originally Released Under LGPL - original licence link has changed is not relivant.
38524  *
38525  * Fork - LGPL
38526  * <script type="text/javascript">
38527  */
38528  
38529 /**
38530  * @class Roo.form.Radio
38531  * @extends Roo.form.Checkbox
38532  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38533  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38534  * @constructor
38535  * Creates a new Radio
38536  * @param {Object} config Configuration options
38537  */
38538 Roo.form.Radio = function(){
38539     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38540 };
38541 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38542     inputType: 'radio',
38543
38544     /**
38545      * If this radio is part of a group, it will return the selected value
38546      * @return {String}
38547      */
38548     getGroupValue : function(){
38549         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38550     }
38551 });//<script type="text/javascript">
38552
38553 /*
38554  * Ext JS Library 1.1.1
38555  * Copyright(c) 2006-2007, Ext JS, LLC.
38556  * licensing@extjs.com
38557  * 
38558  * http://www.extjs.com/license
38559  */
38560  
38561  /*
38562   * 
38563   * Known bugs:
38564   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38565   * - IE ? - no idea how much works there.
38566   * 
38567   * 
38568   * 
38569   */
38570  
38571
38572 /**
38573  * @class Ext.form.HtmlEditor
38574  * @extends Ext.form.Field
38575  * Provides a lightweight HTML Editor component.
38576  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38577  * 
38578  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38579  * supported by this editor.</b><br/><br/>
38580  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38581  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38582  */
38583 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38584       /**
38585      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38586      */
38587     toolbars : false,
38588     /**
38589      * @cfg {String} createLinkText The default text for the create link prompt
38590      */
38591     createLinkText : 'Please enter the URL for the link:',
38592     /**
38593      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38594      */
38595     defaultLinkValue : 'http:/'+'/',
38596    
38597      /**
38598      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38599      *                        Roo.resizable.
38600      */
38601     resizable : false,
38602      /**
38603      * @cfg {Number} height (in pixels)
38604      */   
38605     height: 300,
38606    /**
38607      * @cfg {Number} width (in pixels)
38608      */   
38609     width: 500,
38610     
38611     /**
38612      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38613      * 
38614      */
38615     stylesheets: false,
38616     
38617     // id of frame..
38618     frameId: false,
38619     
38620     // private properties
38621     validationEvent : false,
38622     deferHeight: true,
38623     initialized : false,
38624     activated : false,
38625     sourceEditMode : false,
38626     onFocus : Roo.emptyFn,
38627     iframePad:3,
38628     hideMode:'offsets',
38629     
38630     defaultAutoCreate : { // modified by initCompnoent..
38631         tag: "textarea",
38632         style:"width:500px;height:300px;",
38633         autocomplete: "off"
38634     },
38635
38636     // private
38637     initComponent : function(){
38638         this.addEvents({
38639             /**
38640              * @event initialize
38641              * Fires when the editor is fully initialized (including the iframe)
38642              * @param {HtmlEditor} this
38643              */
38644             initialize: true,
38645             /**
38646              * @event activate
38647              * Fires when the editor is first receives the focus. Any insertion must wait
38648              * until after this event.
38649              * @param {HtmlEditor} this
38650              */
38651             activate: true,
38652              /**
38653              * @event beforesync
38654              * Fires before the textarea is updated with content from the editor iframe. Return false
38655              * to cancel the sync.
38656              * @param {HtmlEditor} this
38657              * @param {String} html
38658              */
38659             beforesync: true,
38660              /**
38661              * @event beforepush
38662              * Fires before the iframe editor is updated with content from the textarea. Return false
38663              * to cancel the push.
38664              * @param {HtmlEditor} this
38665              * @param {String} html
38666              */
38667             beforepush: true,
38668              /**
38669              * @event sync
38670              * Fires when the textarea is updated with content from the editor iframe.
38671              * @param {HtmlEditor} this
38672              * @param {String} html
38673              */
38674             sync: true,
38675              /**
38676              * @event push
38677              * Fires when the iframe editor is updated with content from the textarea.
38678              * @param {HtmlEditor} this
38679              * @param {String} html
38680              */
38681             push: true,
38682              /**
38683              * @event editmodechange
38684              * Fires when the editor switches edit modes
38685              * @param {HtmlEditor} this
38686              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38687              */
38688             editmodechange: true,
38689             /**
38690              * @event editorevent
38691              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38692              * @param {HtmlEditor} this
38693              */
38694             editorevent: true
38695         });
38696         this.defaultAutoCreate =  {
38697             tag: "textarea",
38698             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38699             autocomplete: "off"
38700         };
38701     },
38702
38703     /**
38704      * Protected method that will not generally be called directly. It
38705      * is called when the editor creates its toolbar. Override this method if you need to
38706      * add custom toolbar buttons.
38707      * @param {HtmlEditor} editor
38708      */
38709     createToolbar : function(editor){
38710         if (!editor.toolbars || !editor.toolbars.length) {
38711             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38712         }
38713         
38714         for (var i =0 ; i < editor.toolbars.length;i++) {
38715             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38716             editor.toolbars[i].init(editor);
38717         }
38718          
38719         
38720     },
38721
38722     /**
38723      * Protected method that will not generally be called directly. It
38724      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38725      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38726      */
38727     getDocMarkup : function(){
38728         // body styles..
38729         var st = '';
38730         if (this.stylesheets === false) {
38731             
38732             Roo.get(document.head).select('style').each(function(node) {
38733                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38734             });
38735             
38736             Roo.get(document.head).select('link').each(function(node) { 
38737                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38738             });
38739             
38740         } else if (!this.stylesheets.length) {
38741                 // simple..
38742                 st = '<style type="text/css">' +
38743                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38744                    '</style>';
38745         } else {
38746             Roo.each(this.stylesheets, function(s) {
38747                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38748             });
38749             
38750         }
38751         
38752         return '<html><head>' + st  +
38753             //<style type="text/css">' +
38754             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38755             //'</style>' +
38756             ' </head><body></body></html>';
38757     },
38758
38759     // private
38760     onRender : function(ct, position)
38761     {
38762         var _t = this;
38763         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38764         this.el.dom.style.border = '0 none';
38765         this.el.dom.setAttribute('tabIndex', -1);
38766         this.el.addClass('x-hidden');
38767         if(Roo.isIE){ // fix IE 1px bogus margin
38768             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38769         }
38770         this.wrap = this.el.wrap({
38771             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38772         });
38773         
38774         if (this.resizable) {
38775             this.resizeEl = new Roo.Resizable(this.wrap, {
38776                 pinned : true,
38777                 wrap: true,
38778                 dynamic : true,
38779                 minHeight : this.height,
38780                 height: this.height,
38781                 handles : this.resizable,
38782                 width: this.width,
38783                 listeners : {
38784                     resize : function(r, w, h) {
38785                         _t.onResize(w,h); // -something
38786                     }
38787                 }
38788             });
38789             
38790         }
38791
38792         this.frameId = Roo.id();
38793         
38794         this.createToolbar(this);
38795         
38796       
38797         
38798         var iframe = this.wrap.createChild({
38799             tag: 'iframe',
38800             id: this.frameId,
38801             name: this.frameId,
38802             frameBorder : 'no',
38803             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38804         }, this.el
38805         );
38806         
38807        // console.log(iframe);
38808         //this.wrap.dom.appendChild(iframe);
38809
38810         this.iframe = iframe.dom;
38811
38812          this.assignDocWin();
38813         
38814         this.doc.designMode = 'on';
38815        
38816         this.doc.open();
38817         this.doc.write(this.getDocMarkup());
38818         this.doc.close();
38819
38820         
38821         var task = { // must defer to wait for browser to be ready
38822             run : function(){
38823                 //console.log("run task?" + this.doc.readyState);
38824                 this.assignDocWin();
38825                 if(this.doc.body || this.doc.readyState == 'complete'){
38826                     try {
38827                         this.doc.designMode="on";
38828                     } catch (e) {
38829                         return;
38830                     }
38831                     Roo.TaskMgr.stop(task);
38832                     this.initEditor.defer(10, this);
38833                 }
38834             },
38835             interval : 10,
38836             duration:10000,
38837             scope: this
38838         };
38839         Roo.TaskMgr.start(task);
38840
38841         if(!this.width){
38842             this.setSize(this.wrap.getSize());
38843         }
38844         if (this.resizeEl) {
38845             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38846             // should trigger onReize..
38847         }
38848     },
38849
38850     // private
38851     onResize : function(w, h)
38852     {
38853         //Roo.log('resize: ' +w + ',' + h );
38854         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38855         if(this.el && this.iframe){
38856             if(typeof w == 'number'){
38857                 var aw = w - this.wrap.getFrameWidth('lr');
38858                 this.el.setWidth(this.adjustWidth('textarea', aw));
38859                 this.iframe.style.width = aw + 'px';
38860             }
38861             if(typeof h == 'number'){
38862                 var tbh = 0;
38863                 for (var i =0; i < this.toolbars.length;i++) {
38864                     // fixme - ask toolbars for heights?
38865                     tbh += this.toolbars[i].tb.el.getHeight();
38866                     if (this.toolbars[i].footer) {
38867                         tbh += this.toolbars[i].footer.el.getHeight();
38868                     }
38869                 }
38870                 
38871                 
38872                 
38873                 
38874                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38875                 ah -= 5; // knock a few pixes off for look..
38876                 this.el.setHeight(this.adjustWidth('textarea', ah));
38877                 this.iframe.style.height = ah + 'px';
38878                 if(this.doc){
38879                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38880                 }
38881             }
38882         }
38883     },
38884
38885     /**
38886      * Toggles the editor between standard and source edit mode.
38887      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38888      */
38889     toggleSourceEdit : function(sourceEditMode){
38890         
38891         this.sourceEditMode = sourceEditMode === true;
38892         
38893         if(this.sourceEditMode){
38894           
38895             this.syncValue();
38896             this.iframe.className = 'x-hidden';
38897             this.el.removeClass('x-hidden');
38898             this.el.dom.removeAttribute('tabIndex');
38899             this.el.focus();
38900         }else{
38901              
38902             this.pushValue();
38903             this.iframe.className = '';
38904             this.el.addClass('x-hidden');
38905             this.el.dom.setAttribute('tabIndex', -1);
38906             this.deferFocus();
38907         }
38908         this.setSize(this.wrap.getSize());
38909         this.fireEvent('editmodechange', this, this.sourceEditMode);
38910     },
38911
38912     // private used internally
38913     createLink : function(){
38914         var url = prompt(this.createLinkText, this.defaultLinkValue);
38915         if(url && url != 'http:/'+'/'){
38916             this.relayCmd('createlink', url);
38917         }
38918     },
38919
38920     // private (for BoxComponent)
38921     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38922
38923     // private (for BoxComponent)
38924     getResizeEl : function(){
38925         return this.wrap;
38926     },
38927
38928     // private (for BoxComponent)
38929     getPositionEl : function(){
38930         return this.wrap;
38931     },
38932
38933     // private
38934     initEvents : function(){
38935         this.originalValue = this.getValue();
38936     },
38937
38938     /**
38939      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38940      * @method
38941      */
38942     markInvalid : Roo.emptyFn,
38943     /**
38944      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38945      * @method
38946      */
38947     clearInvalid : Roo.emptyFn,
38948
38949     setValue : function(v){
38950         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38951         this.pushValue();
38952     },
38953
38954     /**
38955      * Protected method that will not generally be called directly. If you need/want
38956      * custom HTML cleanup, this is the method you should override.
38957      * @param {String} html The HTML to be cleaned
38958      * return {String} The cleaned HTML
38959      */
38960     cleanHtml : function(html){
38961         html = String(html);
38962         if(html.length > 5){
38963             if(Roo.isSafari){ // strip safari nonsense
38964                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38965             }
38966         }
38967         if(html == '&nbsp;'){
38968             html = '';
38969         }
38970         return html;
38971     },
38972
38973     /**
38974      * Protected method that will not generally be called directly. Syncs the contents
38975      * of the editor iframe with the textarea.
38976      */
38977     syncValue : function(){
38978         if(this.initialized){
38979             var bd = (this.doc.body || this.doc.documentElement);
38980             this.cleanUpPaste();
38981             var html = bd.innerHTML;
38982             if(Roo.isSafari){
38983                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38984                 var m = bs.match(/text-align:(.*?);/i);
38985                 if(m && m[1]){
38986                     html = '<div style="'+m[0]+'">' + html + '</div>';
38987                 }
38988             }
38989             html = this.cleanHtml(html);
38990             if(this.fireEvent('beforesync', this, html) !== false){
38991                 this.el.dom.value = html;
38992                 this.fireEvent('sync', this, html);
38993             }
38994         }
38995     },
38996
38997     /**
38998      * Protected method that will not generally be called directly. Pushes the value of the textarea
38999      * into the iframe editor.
39000      */
39001     pushValue : function(){
39002         if(this.initialized){
39003             var v = this.el.dom.value;
39004             if(v.length < 1){
39005                 v = '&#160;';
39006             }
39007             
39008             if(this.fireEvent('beforepush', this, v) !== false){
39009                 var d = (this.doc.body || this.doc.documentElement);
39010                 d.innerHTML = v;
39011                 this.cleanUpPaste();
39012                 this.el.dom.value = d.innerHTML;
39013                 this.fireEvent('push', this, v);
39014             }
39015         }
39016     },
39017
39018     // private
39019     deferFocus : function(){
39020         this.focus.defer(10, this);
39021     },
39022
39023     // doc'ed in Field
39024     focus : function(){
39025         if(this.win && !this.sourceEditMode){
39026             this.win.focus();
39027         }else{
39028             this.el.focus();
39029         }
39030     },
39031     
39032     assignDocWin: function()
39033     {
39034         var iframe = this.iframe;
39035         
39036          if(Roo.isIE){
39037             this.doc = iframe.contentWindow.document;
39038             this.win = iframe.contentWindow;
39039         } else {
39040             if (!Roo.get(this.frameId)) {
39041                 return;
39042             }
39043             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39044             this.win = Roo.get(this.frameId).dom.contentWindow;
39045         }
39046     },
39047     
39048     // private
39049     initEditor : function(){
39050         //console.log("INIT EDITOR");
39051         this.assignDocWin();
39052         
39053         
39054         
39055         this.doc.designMode="on";
39056         this.doc.open();
39057         this.doc.write(this.getDocMarkup());
39058         this.doc.close();
39059         
39060         var dbody = (this.doc.body || this.doc.documentElement);
39061         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39062         // this copies styles from the containing element into thsi one..
39063         // not sure why we need all of this..
39064         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39065         ss['background-attachment'] = 'fixed'; // w3c
39066         dbody.bgProperties = 'fixed'; // ie
39067         Roo.DomHelper.applyStyles(dbody, ss);
39068         Roo.EventManager.on(this.doc, {
39069             //'mousedown': this.onEditorEvent,
39070             'mouseup': this.onEditorEvent,
39071             'dblclick': this.onEditorEvent,
39072             'click': this.onEditorEvent,
39073             'keyup': this.onEditorEvent,
39074             buffer:100,
39075             scope: this
39076         });
39077         if(Roo.isGecko){
39078             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39079         }
39080         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39081             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39082         }
39083         this.initialized = true;
39084
39085         this.fireEvent('initialize', this);
39086         this.pushValue();
39087     },
39088
39089     // private
39090     onDestroy : function(){
39091         
39092         
39093         
39094         if(this.rendered){
39095             
39096             for (var i =0; i < this.toolbars.length;i++) {
39097                 // fixme - ask toolbars for heights?
39098                 this.toolbars[i].onDestroy();
39099             }
39100             
39101             this.wrap.dom.innerHTML = '';
39102             this.wrap.remove();
39103         }
39104     },
39105
39106     // private
39107     onFirstFocus : function(){
39108         
39109         this.assignDocWin();
39110         
39111         
39112         this.activated = true;
39113         for (var i =0; i < this.toolbars.length;i++) {
39114             this.toolbars[i].onFirstFocus();
39115         }
39116        
39117         if(Roo.isGecko){ // prevent silly gecko errors
39118             this.win.focus();
39119             var s = this.win.getSelection();
39120             if(!s.focusNode || s.focusNode.nodeType != 3){
39121                 var r = s.getRangeAt(0);
39122                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39123                 r.collapse(true);
39124                 this.deferFocus();
39125             }
39126             try{
39127                 this.execCmd('useCSS', true);
39128                 this.execCmd('styleWithCSS', false);
39129             }catch(e){}
39130         }
39131         this.fireEvent('activate', this);
39132     },
39133
39134     // private
39135     adjustFont: function(btn){
39136         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39137         //if(Roo.isSafari){ // safari
39138         //    adjust *= 2;
39139        // }
39140         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39141         if(Roo.isSafari){ // safari
39142             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39143             v =  (v < 10) ? 10 : v;
39144             v =  (v > 48) ? 48 : v;
39145             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39146             
39147         }
39148         
39149         
39150         v = Math.max(1, v+adjust);
39151         
39152         this.execCmd('FontSize', v  );
39153     },
39154
39155     onEditorEvent : function(e){
39156         this.fireEvent('editorevent', this, e);
39157       //  this.updateToolbar();
39158         this.syncValue();
39159     },
39160
39161     insertTag : function(tg)
39162     {
39163         // could be a bit smarter... -> wrap the current selected tRoo..
39164         
39165         this.execCmd("formatblock",   tg);
39166         
39167     },
39168     
39169     insertText : function(txt)
39170     {
39171         
39172         
39173         range = this.createRange();
39174         range.deleteContents();
39175                //alert(Sender.getAttribute('label'));
39176                
39177         range.insertNode(this.doc.createTextNode(txt));
39178     } ,
39179     
39180     // private
39181     relayBtnCmd : function(btn){
39182         this.relayCmd(btn.cmd);
39183     },
39184
39185     /**
39186      * Executes a Midas editor command on the editor document and performs necessary focus and
39187      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39188      * @param {String} cmd The Midas command
39189      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39190      */
39191     relayCmd : function(cmd, value){
39192         this.win.focus();
39193         this.execCmd(cmd, value);
39194         this.fireEvent('editorevent', this);
39195         //this.updateToolbar();
39196         this.deferFocus();
39197     },
39198
39199     /**
39200      * Executes a Midas editor command directly on the editor document.
39201      * For visual commands, you should use {@link #relayCmd} instead.
39202      * <b>This should only be called after the editor is initialized.</b>
39203      * @param {String} cmd The Midas command
39204      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39205      */
39206     execCmd : function(cmd, value){
39207         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39208         this.syncValue();
39209     },
39210
39211    
39212     /**
39213      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39214      * to insert tRoo.
39215      * @param {String} text
39216      */
39217     insertAtCursor : function(text){
39218         if(!this.activated){
39219             return;
39220         }
39221         if(Roo.isIE){
39222             this.win.focus();
39223             var r = this.doc.selection.createRange();
39224             if(r){
39225                 r.collapse(true);
39226                 r.pasteHTML(text);
39227                 this.syncValue();
39228                 this.deferFocus();
39229             }
39230         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39231             this.win.focus();
39232             this.execCmd('InsertHTML', text);
39233             this.deferFocus();
39234         }
39235     },
39236  // private
39237     mozKeyPress : function(e){
39238         if(e.ctrlKey){
39239             var c = e.getCharCode(), cmd;
39240           
39241             if(c > 0){
39242                 c = String.fromCharCode(c).toLowerCase();
39243                 switch(c){
39244                     case 'b':
39245                         cmd = 'bold';
39246                     break;
39247                     case 'i':
39248                         cmd = 'italic';
39249                     break;
39250                     case 'u':
39251                         cmd = 'underline';
39252                     case 'v':
39253                         this.cleanUpPaste.defer(100, this);
39254                         return;
39255                     break;
39256                 }
39257                 if(cmd){
39258                     this.win.focus();
39259                     this.execCmd(cmd);
39260                     this.deferFocus();
39261                     e.preventDefault();
39262                 }
39263                 
39264             }
39265         }
39266     },
39267
39268     // private
39269     fixKeys : function(){ // load time branching for fastest keydown performance
39270         if(Roo.isIE){
39271             return function(e){
39272                 var k = e.getKey(), r;
39273                 if(k == e.TAB){
39274                     e.stopEvent();
39275                     r = this.doc.selection.createRange();
39276                     if(r){
39277                         r.collapse(true);
39278                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39279                         this.deferFocus();
39280                     }
39281                     return;
39282                 }
39283                 
39284                 if(k == e.ENTER){
39285                     r = this.doc.selection.createRange();
39286                     if(r){
39287                         var target = r.parentElement();
39288                         if(!target || target.tagName.toLowerCase() != 'li'){
39289                             e.stopEvent();
39290                             r.pasteHTML('<br />');
39291                             r.collapse(false);
39292                             r.select();
39293                         }
39294                     }
39295                 }
39296                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39297                     this.cleanUpPaste.defer(100, this);
39298                     return;
39299                 }
39300                 
39301                 
39302             };
39303         }else if(Roo.isOpera){
39304             return function(e){
39305                 var k = e.getKey();
39306                 if(k == e.TAB){
39307                     e.stopEvent();
39308                     this.win.focus();
39309                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39310                     this.deferFocus();
39311                 }
39312                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39313                     this.cleanUpPaste.defer(100, this);
39314                     return;
39315                 }
39316                 
39317             };
39318         }else if(Roo.isSafari){
39319             return function(e){
39320                 var k = e.getKey();
39321                 
39322                 if(k == e.TAB){
39323                     e.stopEvent();
39324                     this.execCmd('InsertText','\t');
39325                     this.deferFocus();
39326                     return;
39327                 }
39328                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39329                     this.cleanUpPaste.defer(100, this);
39330                     return;
39331                 }
39332                 
39333              };
39334         }
39335     }(),
39336     
39337     getAllAncestors: function()
39338     {
39339         var p = this.getSelectedNode();
39340         var a = [];
39341         if (!p) {
39342             a.push(p); // push blank onto stack..
39343             p = this.getParentElement();
39344         }
39345         
39346         
39347         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39348             a.push(p);
39349             p = p.parentNode;
39350         }
39351         a.push(this.doc.body);
39352         return a;
39353     },
39354     lastSel : false,
39355     lastSelNode : false,
39356     
39357     
39358     getSelection : function() 
39359     {
39360         this.assignDocWin();
39361         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39362     },
39363     
39364     getSelectedNode: function() 
39365     {
39366         // this may only work on Gecko!!!
39367         
39368         // should we cache this!!!!
39369         
39370         
39371         
39372          
39373         var range = this.createRange(this.getSelection()).cloneRange();
39374         
39375         if (Roo.isIE) {
39376             var parent = range.parentElement();
39377             while (true) {
39378                 var testRange = range.duplicate();
39379                 testRange.moveToElementText(parent);
39380                 if (testRange.inRange(range)) {
39381                     break;
39382                 }
39383                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39384                     break;
39385                 }
39386                 parent = parent.parentElement;
39387             }
39388             return parent;
39389         }
39390         
39391         // is ancestor a text element.
39392         var ac =  range.commonAncestorContainer;
39393         if (ac.nodeType == 3) {
39394             ac = ac.parentNode;
39395         }
39396         
39397         var ar = ac.childNodes;
39398          
39399         var nodes = [];
39400         var other_nodes = [];
39401         var has_other_nodes = false;
39402         for (var i=0;i<ar.length;i++) {
39403             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39404                 continue;
39405             }
39406             // fullly contained node.
39407             
39408             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39409                 nodes.push(ar[i]);
39410                 continue;
39411             }
39412             
39413             // probably selected..
39414             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39415                 other_nodes.push(ar[i]);
39416                 continue;
39417             }
39418             // outer..
39419             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39420                 continue;
39421             }
39422             
39423             
39424             has_other_nodes = true;
39425         }
39426         if (!nodes.length && other_nodes.length) {
39427             nodes= other_nodes;
39428         }
39429         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39430             return false;
39431         }
39432         
39433         return nodes[0];
39434     },
39435     createRange: function(sel)
39436     {
39437         // this has strange effects when using with 
39438         // top toolbar - not sure if it's a great idea.
39439         //this.editor.contentWindow.focus();
39440         if (typeof sel != "undefined") {
39441             try {
39442                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39443             } catch(e) {
39444                 return this.doc.createRange();
39445             }
39446         } else {
39447             return this.doc.createRange();
39448         }
39449     },
39450     getParentElement: function()
39451     {
39452         
39453         this.assignDocWin();
39454         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39455         
39456         var range = this.createRange(sel);
39457          
39458         try {
39459             var p = range.commonAncestorContainer;
39460             while (p.nodeType == 3) { // text node
39461                 p = p.parentNode;
39462             }
39463             return p;
39464         } catch (e) {
39465             return null;
39466         }
39467     
39468     },
39469     /***
39470      *
39471      * Range intersection.. the hard stuff...
39472      *  '-1' = before
39473      *  '0' = hits..
39474      *  '1' = after.
39475      *         [ -- selected range --- ]
39476      *   [fail]                        [fail]
39477      *
39478      *    basically..
39479      *      if end is before start or  hits it. fail.
39480      *      if start is after end or hits it fail.
39481      *
39482      *   if either hits (but other is outside. - then it's not 
39483      *   
39484      *    
39485      **/
39486     
39487     
39488     // @see http://www.thismuchiknow.co.uk/?p=64.
39489     rangeIntersectsNode : function(range, node)
39490     {
39491         var nodeRange = node.ownerDocument.createRange();
39492         try {
39493             nodeRange.selectNode(node);
39494         } catch (e) {
39495             nodeRange.selectNodeContents(node);
39496         }
39497     
39498         var rangeStartRange = range.cloneRange();
39499         rangeStartRange.collapse(true);
39500     
39501         var rangeEndRange = range.cloneRange();
39502         rangeEndRange.collapse(false);
39503     
39504         var nodeStartRange = nodeRange.cloneRange();
39505         nodeStartRange.collapse(true);
39506     
39507         var nodeEndRange = nodeRange.cloneRange();
39508         nodeEndRange.collapse(false);
39509     
39510         return rangeStartRange.compareBoundaryPoints(
39511                  Range.START_TO_START, nodeEndRange) == -1 &&
39512                rangeEndRange.compareBoundaryPoints(
39513                  Range.START_TO_START, nodeStartRange) == 1;
39514         
39515          
39516     },
39517     rangeCompareNode : function(range, node)
39518     {
39519         var nodeRange = node.ownerDocument.createRange();
39520         try {
39521             nodeRange.selectNode(node);
39522         } catch (e) {
39523             nodeRange.selectNodeContents(node);
39524         }
39525         
39526         
39527         range.collapse(true);
39528     
39529         nodeRange.collapse(true);
39530      
39531         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39532         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39533          
39534         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39535         
39536         var nodeIsBefore   =  ss == 1;
39537         var nodeIsAfter    = ee == -1;
39538         
39539         if (nodeIsBefore && nodeIsAfter)
39540             return 0; // outer
39541         if (!nodeIsBefore && nodeIsAfter)
39542             return 1; //right trailed.
39543         
39544         if (nodeIsBefore && !nodeIsAfter)
39545             return 2;  // left trailed.
39546         // fully contined.
39547         return 3;
39548     },
39549
39550     // private? - in a new class?
39551     cleanUpPaste :  function()
39552     {
39553         // cleans up the whole document..
39554       //  console.log('cleanuppaste');
39555         this.cleanUpChildren(this.doc.body);
39556         
39557         
39558     },
39559     cleanUpChildren : function (n)
39560     {
39561         if (!n.childNodes.length) {
39562             return;
39563         }
39564         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39565            this.cleanUpChild(n.childNodes[i]);
39566         }
39567     },
39568     
39569     
39570         
39571     
39572     cleanUpChild : function (node)
39573     {
39574         //console.log(node);
39575         if (node.nodeName == "#text") {
39576             // clean up silly Windows -- stuff?
39577             return; 
39578         }
39579         if (node.nodeName == "#comment") {
39580             node.parentNode.removeChild(node);
39581             // clean up silly Windows -- stuff?
39582             return; 
39583         }
39584         
39585         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39586             // remove node.
39587             node.parentNode.removeChild(node);
39588             return;
39589             
39590         }
39591         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39592             this.cleanUpChildren(node);
39593             // inserts everything just before this node...
39594             while (node.childNodes.length) {
39595                 var cn = node.childNodes[0];
39596                 node.removeChild(cn);
39597                 node.parentNode.insertBefore(cn, node);
39598             }
39599             node.parentNode.removeChild(node);
39600             return;
39601         }
39602         
39603         if (!node.attributes || !node.attributes.length) {
39604             this.cleanUpChildren(node);
39605             return;
39606         }
39607         
39608         function cleanAttr(n,v)
39609         {
39610             
39611             if (v.match(/^\./) || v.match(/^\//)) {
39612                 return;
39613             }
39614             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39615                 return;
39616             }
39617             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39618             node.removeAttribute(n);
39619             
39620         }
39621         
39622         function cleanStyle(n,v)
39623         {
39624             if (v.match(/expression/)) { //XSS?? should we even bother..
39625                 node.removeAttribute(n);
39626                 return;
39627             }
39628             
39629             
39630             var parts = v.split(/;/);
39631             Roo.each(parts, function(p) {
39632                 p = p.replace(/\s+/g,'');
39633                 if (!p.length) {
39634                     return true;
39635                 }
39636                 var l = p.split(':').shift().replace(/\s+/g,'');
39637                 
39638                 // only allow 'c whitelisted system attributes'
39639                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39640                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39641                     node.removeAttribute(n);
39642                     return false;
39643                 }
39644                 return true;
39645             });
39646             
39647             
39648         }
39649         
39650         
39651         for (var i = node.attributes.length-1; i > -1 ; i--) {
39652             var a = node.attributes[i];
39653             //console.log(a);
39654             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39655                 node.removeAttribute(a.name);
39656                 return;
39657             }
39658             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39659                 cleanAttr(a.name,a.value); // fixme..
39660                 return;
39661             }
39662             if (a.name == 'style') {
39663                 cleanStyle(a.name,a.value);
39664             }
39665             /// clean up MS crap..
39666             if (a.name == 'class') {
39667                 if (a.value.match(/^Mso/)) {
39668                     node.className = '';
39669                 }
39670             }
39671             
39672             // style cleanup!?
39673             // class cleanup?
39674             
39675         }
39676         
39677         
39678         this.cleanUpChildren(node);
39679         
39680         
39681     }
39682     
39683     
39684     // hide stuff that is not compatible
39685     /**
39686      * @event blur
39687      * @hide
39688      */
39689     /**
39690      * @event change
39691      * @hide
39692      */
39693     /**
39694      * @event focus
39695      * @hide
39696      */
39697     /**
39698      * @event specialkey
39699      * @hide
39700      */
39701     /**
39702      * @cfg {String} fieldClass @hide
39703      */
39704     /**
39705      * @cfg {String} focusClass @hide
39706      */
39707     /**
39708      * @cfg {String} autoCreate @hide
39709      */
39710     /**
39711      * @cfg {String} inputType @hide
39712      */
39713     /**
39714      * @cfg {String} invalidClass @hide
39715      */
39716     /**
39717      * @cfg {String} invalidText @hide
39718      */
39719     /**
39720      * @cfg {String} msgFx @hide
39721      */
39722     /**
39723      * @cfg {String} validateOnBlur @hide
39724      */
39725 });
39726
39727 Roo.form.HtmlEditor.white = [
39728         'area', 'br', 'img', 'input', 'hr', 'wbr',
39729         
39730        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39731        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39732        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39733        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39734        'table',   'ul',         'xmp', 
39735        
39736        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39737       'thead',   'tr', 
39738      
39739       'dir', 'menu', 'ol', 'ul', 'dl',
39740        
39741       'embed',  'object'
39742 ];
39743
39744
39745 Roo.form.HtmlEditor.black = [
39746     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39747         'applet', // 
39748         'base',   'basefont', 'bgsound', 'blink',  'body', 
39749         'frame',  'frameset', 'head',    'html',   'ilayer', 
39750         'iframe', 'layer',  'link',     'meta',    'object',   
39751         'script', 'style' ,'title',  'xml' // clean later..
39752 ];
39753 Roo.form.HtmlEditor.clean = [
39754     'script', 'style', 'title', 'xml'
39755 ];
39756 Roo.form.HtmlEditor.remove = [
39757     'font'
39758 ];
39759 // attributes..
39760
39761 Roo.form.HtmlEditor.ablack = [
39762     'on'
39763 ];
39764     
39765 Roo.form.HtmlEditor.aclean = [ 
39766     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39767 ];
39768
39769 // protocols..
39770 Roo.form.HtmlEditor.pwhite= [
39771         'http',  'https',  'mailto'
39772 ];
39773
39774 // white listed style attributes.
39775 Roo.form.HtmlEditor.cwhite= [
39776         'text-align',
39777         'font-size'
39778 ];
39779
39780 // <script type="text/javascript">
39781 /*
39782  * Based on
39783  * Ext JS Library 1.1.1
39784  * Copyright(c) 2006-2007, Ext JS, LLC.
39785  *  
39786  
39787  */
39788
39789 /**
39790  * @class Roo.form.HtmlEditorToolbar1
39791  * Basic Toolbar
39792  * 
39793  * Usage:
39794  *
39795  new Roo.form.HtmlEditor({
39796     ....
39797     toolbars : [
39798         new Roo.form.HtmlEditorToolbar1({
39799             disable : { fonts: 1 , format: 1, ..., ... , ...],
39800             btns : [ .... ]
39801         })
39802     }
39803      
39804  * 
39805  * @cfg {Object} disable List of elements to disable..
39806  * @cfg {Array} btns List of additional buttons.
39807  * 
39808  * 
39809  * NEEDS Extra CSS? 
39810  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39811  */
39812  
39813 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39814 {
39815     
39816     Roo.apply(this, config);
39817     
39818     // default disabled, based on 'good practice'..
39819     this.disable = this.disable || {};
39820     Roo.applyIf(this.disable, {
39821         fontSize : true,
39822         colors : true,
39823         specialElements : true
39824     });
39825     
39826     
39827     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39828     // dont call parent... till later.
39829 }
39830
39831 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39832     
39833     tb: false,
39834     
39835     rendered: false,
39836     
39837     editor : false,
39838     /**
39839      * @cfg {Object} disable  List of toolbar elements to disable
39840          
39841      */
39842     disable : false,
39843       /**
39844      * @cfg {Array} fontFamilies An array of available font families
39845      */
39846     fontFamilies : [
39847         'Arial',
39848         'Courier New',
39849         'Tahoma',
39850         'Times New Roman',
39851         'Verdana'
39852     ],
39853     
39854     specialChars : [
39855            "&#169;",
39856           "&#174;",     
39857           "&#8482;",    
39858           "&#163;" ,    
39859          // "&#8212;",    
39860           "&#8230;",    
39861           "&#247;" ,    
39862         //  "&#225;" ,     ?? a acute?
39863            "&#8364;"    , //Euro
39864        //   "&#8220;"    ,
39865         //  "&#8221;"    ,
39866         //  "&#8226;"    ,
39867           "&#176;"  //   , // degrees
39868
39869          // "&#233;"     , // e ecute
39870          // "&#250;"     , // u ecute?
39871     ],
39872     
39873     specialElements : [
39874         {
39875             text: "Insert Table",
39876             xtype: 'MenuItem',
39877             xns : Roo.Menu,
39878             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39879                 
39880         },
39881         {    
39882             text: "Insert Image",
39883             xtype: 'MenuItem',
39884             xns : Roo.Menu,
39885             ihtml : '<img src="about:blank"/>'
39886             
39887         }
39888         
39889          
39890     ],
39891     
39892     
39893     inputElements : [ 
39894             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39895             "input:submit", "input:button", "select", "textarea", "label" ],
39896     formats : [
39897         ["p"] ,  
39898         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39899         ["pre"],[ "code"], 
39900         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39901     ],
39902      /**
39903      * @cfg {String} defaultFont default font to use.
39904      */
39905     defaultFont: 'tahoma',
39906    
39907     fontSelect : false,
39908     
39909     
39910     formatCombo : false,
39911     
39912     init : function(editor)
39913     {
39914         this.editor = editor;
39915         
39916         
39917         var fid = editor.frameId;
39918         var etb = this;
39919         function btn(id, toggle, handler){
39920             var xid = fid + '-'+ id ;
39921             return {
39922                 id : xid,
39923                 cmd : id,
39924                 cls : 'x-btn-icon x-edit-'+id,
39925                 enableToggle:toggle !== false,
39926                 scope: editor, // was editor...
39927                 handler:handler||editor.relayBtnCmd,
39928                 clickEvent:'mousedown',
39929                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39930                 tabIndex:-1
39931             };
39932         }
39933         
39934         
39935         
39936         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39937         this.tb = tb;
39938          // stop form submits
39939         tb.el.on('click', function(e){
39940             e.preventDefault(); // what does this do?
39941         });
39942
39943         if(!this.disable.font && !Roo.isSafari){
39944             /* why no safari for fonts
39945             editor.fontSelect = tb.el.createChild({
39946                 tag:'select',
39947                 tabIndex: -1,
39948                 cls:'x-font-select',
39949                 html: editor.createFontOptions()
39950             });
39951             editor.fontSelect.on('change', function(){
39952                 var font = editor.fontSelect.dom.value;
39953                 editor.relayCmd('fontname', font);
39954                 editor.deferFocus();
39955             }, editor);
39956             tb.add(
39957                 editor.fontSelect.dom,
39958                 '-'
39959             );
39960             */
39961         };
39962         if(!this.disable.formats){
39963             this.formatCombo = new Roo.form.ComboBox({
39964                 store: new Roo.data.SimpleStore({
39965                     id : 'tag',
39966                     fields: ['tag'],
39967                     data : this.formats // from states.js
39968                 }),
39969                 blockFocus : true,
39970                 //autoCreate : {tag: "div",  size: "20"},
39971                 displayField:'tag',
39972                 typeAhead: false,
39973                 mode: 'local',
39974                 editable : false,
39975                 triggerAction: 'all',
39976                 emptyText:'Add tag',
39977                 selectOnFocus:true,
39978                 width:135,
39979                 listeners : {
39980                     'select': function(c, r, i) {
39981                         editor.insertTag(r.get('tag'));
39982                         editor.focus();
39983                     }
39984                 }
39985
39986             });
39987             tb.addField(this.formatCombo);
39988             
39989         }
39990         
39991         if(!this.disable.format){
39992             tb.add(
39993                 btn('bold'),
39994                 btn('italic'),
39995                 btn('underline')
39996             );
39997         };
39998         if(!this.disable.fontSize){
39999             tb.add(
40000                 '-',
40001                 
40002                 
40003                 btn('increasefontsize', false, editor.adjustFont),
40004                 btn('decreasefontsize', false, editor.adjustFont)
40005             );
40006         };
40007         
40008         
40009         if(!this.disable.colors){
40010             tb.add(
40011                 '-', {
40012                     id:editor.frameId +'-forecolor',
40013                     cls:'x-btn-icon x-edit-forecolor',
40014                     clickEvent:'mousedown',
40015                     tooltip: this.buttonTips['forecolor'] || undefined,
40016                     tabIndex:-1,
40017                     menu : new Roo.menu.ColorMenu({
40018                         allowReselect: true,
40019                         focus: Roo.emptyFn,
40020                         value:'000000',
40021                         plain:true,
40022                         selectHandler: function(cp, color){
40023                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40024                             editor.deferFocus();
40025                         },
40026                         scope: editor,
40027                         clickEvent:'mousedown'
40028                     })
40029                 }, {
40030                     id:editor.frameId +'backcolor',
40031                     cls:'x-btn-icon x-edit-backcolor',
40032                     clickEvent:'mousedown',
40033                     tooltip: this.buttonTips['backcolor'] || undefined,
40034                     tabIndex:-1,
40035                     menu : new Roo.menu.ColorMenu({
40036                         focus: Roo.emptyFn,
40037                         value:'FFFFFF',
40038                         plain:true,
40039                         allowReselect: true,
40040                         selectHandler: function(cp, color){
40041                             if(Roo.isGecko){
40042                                 editor.execCmd('useCSS', false);
40043                                 editor.execCmd('hilitecolor', color);
40044                                 editor.execCmd('useCSS', true);
40045                                 editor.deferFocus();
40046                             }else{
40047                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40048                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40049                                 editor.deferFocus();
40050                             }
40051                         },
40052                         scope:editor,
40053                         clickEvent:'mousedown'
40054                     })
40055                 }
40056             );
40057         };
40058         // now add all the items...
40059         
40060
40061         if(!this.disable.alignments){
40062             tb.add(
40063                 '-',
40064                 btn('justifyleft'),
40065                 btn('justifycenter'),
40066                 btn('justifyright')
40067             );
40068         };
40069
40070         //if(!Roo.isSafari){
40071             if(!this.disable.links){
40072                 tb.add(
40073                     '-',
40074                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40075                 );
40076             };
40077
40078             if(!this.disable.lists){
40079                 tb.add(
40080                     '-',
40081                     btn('insertorderedlist'),
40082                     btn('insertunorderedlist')
40083                 );
40084             }
40085             if(!this.disable.sourceEdit){
40086                 tb.add(
40087                     '-',
40088                     btn('sourceedit', true, function(btn){
40089                         this.toggleSourceEdit(btn.pressed);
40090                     })
40091                 );
40092             }
40093         //}
40094         
40095         var smenu = { };
40096         // special menu.. - needs to be tidied up..
40097         if (!this.disable.special) {
40098             smenu = {
40099                 text: "&#169;",
40100                 cls: 'x-edit-none',
40101                 
40102                 menu : {
40103                     items : []
40104                 }
40105             };
40106             for (var i =0; i < this.specialChars.length; i++) {
40107                 smenu.menu.items.push({
40108                     
40109                     html: this.specialChars[i],
40110                     handler: function(a,b) {
40111                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40112                         
40113                     },
40114                     tabIndex:-1
40115                 });
40116             }
40117             
40118             
40119             tb.add(smenu);
40120             
40121             
40122         }
40123          
40124         if (!this.disable.specialElements) {
40125             var semenu = {
40126                 text: "Other;",
40127                 cls: 'x-edit-none',
40128                 menu : {
40129                     items : []
40130                 }
40131             };
40132             for (var i =0; i < this.specialElements.length; i++) {
40133                 semenu.menu.items.push(
40134                     Roo.apply({ 
40135                         handler: function(a,b) {
40136                             editor.insertAtCursor(this.ihtml);
40137                         }
40138                     }, this.specialElements[i])
40139                 );
40140                     
40141             }
40142             
40143             tb.add(semenu);
40144             
40145             
40146         }
40147          
40148         
40149         if (this.btns) {
40150             for(var i =0; i< this.btns.length;i++) {
40151                 var b = this.btns[i];
40152                 b.cls =  'x-edit-none';
40153                 b.scope = editor;
40154                 tb.add(b);
40155             }
40156         
40157         }
40158         
40159         
40160         
40161         // disable everything...
40162         
40163         this.tb.items.each(function(item){
40164            if(item.id != editor.frameId+ '-sourceedit'){
40165                 item.disable();
40166             }
40167         });
40168         this.rendered = true;
40169         
40170         // the all the btns;
40171         editor.on('editorevent', this.updateToolbar, this);
40172         // other toolbars need to implement this..
40173         //editor.on('editmodechange', this.updateToolbar, this);
40174     },
40175     
40176     
40177     
40178     /**
40179      * Protected method that will not generally be called directly. It triggers
40180      * a toolbar update by reading the markup state of the current selection in the editor.
40181      */
40182     updateToolbar: function(){
40183
40184         if(!this.editor.activated){
40185             this.editor.onFirstFocus();
40186             return;
40187         }
40188
40189         var btns = this.tb.items.map, 
40190             doc = this.editor.doc,
40191             frameId = this.editor.frameId;
40192
40193         if(!this.disable.font && !Roo.isSafari){
40194             /*
40195             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40196             if(name != this.fontSelect.dom.value){
40197                 this.fontSelect.dom.value = name;
40198             }
40199             */
40200         }
40201         if(!this.disable.format){
40202             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40203             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40204             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40205         }
40206         if(!this.disable.alignments){
40207             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40208             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40209             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40210         }
40211         if(!Roo.isSafari && !this.disable.lists){
40212             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40213             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40214         }
40215         
40216         var ans = this.editor.getAllAncestors();
40217         if (this.formatCombo) {
40218             
40219             
40220             var store = this.formatCombo.store;
40221             this.formatCombo.setValue("");
40222             for (var i =0; i < ans.length;i++) {
40223                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40224                     // select it..
40225                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40226                     break;
40227                 }
40228             }
40229         }
40230         
40231         
40232         
40233         // hides menus... - so this cant be on a menu...
40234         Roo.menu.MenuMgr.hideAll();
40235
40236         //this.editorsyncValue();
40237     },
40238    
40239     
40240     createFontOptions : function(){
40241         var buf = [], fs = this.fontFamilies, ff, lc;
40242         for(var i = 0, len = fs.length; i< len; i++){
40243             ff = fs[i];
40244             lc = ff.toLowerCase();
40245             buf.push(
40246                 '<option value="',lc,'" style="font-family:',ff,';"',
40247                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40248                     ff,
40249                 '</option>'
40250             );
40251         }
40252         return buf.join('');
40253     },
40254     
40255     toggleSourceEdit : function(sourceEditMode){
40256         if(sourceEditMode === undefined){
40257             sourceEditMode = !this.sourceEditMode;
40258         }
40259         this.sourceEditMode = sourceEditMode === true;
40260         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40261         // just toggle the button?
40262         if(btn.pressed !== this.editor.sourceEditMode){
40263             btn.toggle(this.editor.sourceEditMode);
40264             return;
40265         }
40266         
40267         if(this.sourceEditMode){
40268             this.tb.items.each(function(item){
40269                 if(item.cmd != 'sourceedit'){
40270                     item.disable();
40271                 }
40272             });
40273           
40274         }else{
40275             if(this.initialized){
40276                 this.tb.items.each(function(item){
40277                     item.enable();
40278                 });
40279             }
40280             
40281         }
40282         // tell the editor that it's been pressed..
40283         this.editor.toggleSourceEdit(sourceEditMode);
40284        
40285     },
40286      /**
40287      * Object collection of toolbar tooltips for the buttons in the editor. The key
40288      * is the command id associated with that button and the value is a valid QuickTips object.
40289      * For example:
40290 <pre><code>
40291 {
40292     bold : {
40293         title: 'Bold (Ctrl+B)',
40294         text: 'Make the selected text bold.',
40295         cls: 'x-html-editor-tip'
40296     },
40297     italic : {
40298         title: 'Italic (Ctrl+I)',
40299         text: 'Make the selected text italic.',
40300         cls: 'x-html-editor-tip'
40301     },
40302     ...
40303 </code></pre>
40304     * @type Object
40305      */
40306     buttonTips : {
40307         bold : {
40308             title: 'Bold (Ctrl+B)',
40309             text: 'Make the selected text bold.',
40310             cls: 'x-html-editor-tip'
40311         },
40312         italic : {
40313             title: 'Italic (Ctrl+I)',
40314             text: 'Make the selected text italic.',
40315             cls: 'x-html-editor-tip'
40316         },
40317         underline : {
40318             title: 'Underline (Ctrl+U)',
40319             text: 'Underline the selected text.',
40320             cls: 'x-html-editor-tip'
40321         },
40322         increasefontsize : {
40323             title: 'Grow Text',
40324             text: 'Increase the font size.',
40325             cls: 'x-html-editor-tip'
40326         },
40327         decreasefontsize : {
40328             title: 'Shrink Text',
40329             text: 'Decrease the font size.',
40330             cls: 'x-html-editor-tip'
40331         },
40332         backcolor : {
40333             title: 'Text Highlight Color',
40334             text: 'Change the background color of the selected text.',
40335             cls: 'x-html-editor-tip'
40336         },
40337         forecolor : {
40338             title: 'Font Color',
40339             text: 'Change the color of the selected text.',
40340             cls: 'x-html-editor-tip'
40341         },
40342         justifyleft : {
40343             title: 'Align Text Left',
40344             text: 'Align text to the left.',
40345             cls: 'x-html-editor-tip'
40346         },
40347         justifycenter : {
40348             title: 'Center Text',
40349             text: 'Center text in the editor.',
40350             cls: 'x-html-editor-tip'
40351         },
40352         justifyright : {
40353             title: 'Align Text Right',
40354             text: 'Align text to the right.',
40355             cls: 'x-html-editor-tip'
40356         },
40357         insertunorderedlist : {
40358             title: 'Bullet List',
40359             text: 'Start a bulleted list.',
40360             cls: 'x-html-editor-tip'
40361         },
40362         insertorderedlist : {
40363             title: 'Numbered List',
40364             text: 'Start a numbered list.',
40365             cls: 'x-html-editor-tip'
40366         },
40367         createlink : {
40368             title: 'Hyperlink',
40369             text: 'Make the selected text a hyperlink.',
40370             cls: 'x-html-editor-tip'
40371         },
40372         sourceedit : {
40373             title: 'Source Edit',
40374             text: 'Switch to source editing mode.',
40375             cls: 'x-html-editor-tip'
40376         }
40377     },
40378     // private
40379     onDestroy : function(){
40380         if(this.rendered){
40381             
40382             this.tb.items.each(function(item){
40383                 if(item.menu){
40384                     item.menu.removeAll();
40385                     if(item.menu.el){
40386                         item.menu.el.destroy();
40387                     }
40388                 }
40389                 item.destroy();
40390             });
40391              
40392         }
40393     },
40394     onFirstFocus: function() {
40395         this.tb.items.each(function(item){
40396            item.enable();
40397         });
40398     }
40399 });
40400
40401
40402
40403
40404 // <script type="text/javascript">
40405 /*
40406  * Based on
40407  * Ext JS Library 1.1.1
40408  * Copyright(c) 2006-2007, Ext JS, LLC.
40409  *  
40410  
40411  */
40412
40413  
40414 /**
40415  * @class Roo.form.HtmlEditor.ToolbarContext
40416  * Context Toolbar
40417  * 
40418  * Usage:
40419  *
40420  new Roo.form.HtmlEditor({
40421     ....
40422     toolbars : [
40423         { xtype: 'ToolbarStandard', styles : {} }
40424         { xtype: 'ToolbarContext', disable : {} }
40425     ]
40426 })
40427
40428      
40429  * 
40430  * @config : {Object} disable List of elements to disable.. (not done yet.)
40431  * @config : {Object} styles  Map of styles available.
40432  * 
40433  */
40434
40435 Roo.form.HtmlEditor.ToolbarContext = function(config)
40436 {
40437     
40438     Roo.apply(this, config);
40439     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40440     // dont call parent... till later.
40441     this.styles = this.styles || {};
40442 }
40443 Roo.form.HtmlEditor.ToolbarContext.types = {
40444     'IMG' : {
40445         width : {
40446             title: "Width",
40447             width: 40
40448         },
40449         height:  {
40450             title: "Height",
40451             width: 40
40452         },
40453         align: {
40454             title: "Align",
40455             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40456             width : 80
40457             
40458         },
40459         border: {
40460             title: "Border",
40461             width: 40
40462         },
40463         alt: {
40464             title: "Alt",
40465             width: 120
40466         },
40467         src : {
40468             title: "Src",
40469             width: 220
40470         }
40471         
40472     },
40473     'A' : {
40474         name : {
40475             title: "Name",
40476             width: 50
40477         },
40478         href:  {
40479             title: "Href",
40480             width: 220
40481         } // border?
40482         
40483     },
40484     'TABLE' : {
40485         rows : {
40486             title: "Rows",
40487             width: 20
40488         },
40489         cols : {
40490             title: "Cols",
40491             width: 20
40492         },
40493         width : {
40494             title: "Width",
40495             width: 40
40496         },
40497         height : {
40498             title: "Height",
40499             width: 40
40500         },
40501         border : {
40502             title: "Border",
40503             width: 20
40504         }
40505     },
40506     'TD' : {
40507         width : {
40508             title: "Width",
40509             width: 40
40510         },
40511         height : {
40512             title: "Height",
40513             width: 40
40514         },   
40515         align: {
40516             title: "Align",
40517             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40518             width: 80
40519         },
40520         valign: {
40521             title: "Valign",
40522             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40523             width: 80
40524         },
40525         colspan: {
40526             title: "Colspan",
40527             width: 20
40528             
40529         }
40530     },
40531     'INPUT' : {
40532         name : {
40533             title: "name",
40534             width: 120
40535         },
40536         value : {
40537             title: "Value",
40538             width: 120
40539         },
40540         width : {
40541             title: "Width",
40542             width: 40
40543         }
40544     },
40545     'LABEL' : {
40546         'for' : {
40547             title: "For",
40548             width: 120
40549         }
40550     },
40551     'TEXTAREA' : {
40552           name : {
40553             title: "name",
40554             width: 120
40555         },
40556         rows : {
40557             title: "Rows",
40558             width: 20
40559         },
40560         cols : {
40561             title: "Cols",
40562             width: 20
40563         }
40564     },
40565     'SELECT' : {
40566         name : {
40567             title: "name",
40568             width: 120
40569         },
40570         selectoptions : {
40571             title: "Options",
40572             width: 200
40573         }
40574     },
40575     
40576     // should we really allow this??
40577     // should this just be 
40578     'BODY' : {
40579         title : {
40580             title: "title",
40581             width: 200,
40582             disabled : true
40583         }
40584     },
40585     '*' : {
40586         // empty..
40587     }
40588 };
40589
40590
40591
40592 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40593     
40594     tb: false,
40595     
40596     rendered: false,
40597     
40598     editor : false,
40599     /**
40600      * @cfg {Object} disable  List of toolbar elements to disable
40601          
40602      */
40603     disable : false,
40604     /**
40605      * @cfg {Object} styles List of styles 
40606      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40607      *
40608      * These must be defined in the page, so they get rendered correctly..
40609      * .headline { }
40610      * TD.underline { }
40611      * 
40612      */
40613     styles : false,
40614     
40615     
40616     
40617     toolbars : false,
40618     
40619     init : function(editor)
40620     {
40621         this.editor = editor;
40622         
40623         
40624         var fid = editor.frameId;
40625         var etb = this;
40626         function btn(id, toggle, handler){
40627             var xid = fid + '-'+ id ;
40628             return {
40629                 id : xid,
40630                 cmd : id,
40631                 cls : 'x-btn-icon x-edit-'+id,
40632                 enableToggle:toggle !== false,
40633                 scope: editor, // was editor...
40634                 handler:handler||editor.relayBtnCmd,
40635                 clickEvent:'mousedown',
40636                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40637                 tabIndex:-1
40638             };
40639         }
40640         // create a new element.
40641         var wdiv = editor.wrap.createChild({
40642                 tag: 'div'
40643             }, editor.wrap.dom.firstChild.nextSibling, true);
40644         
40645         // can we do this more than once??
40646         
40647          // stop form submits
40648       
40649  
40650         // disable everything...
40651         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40652         this.toolbars = {};
40653            
40654         for (var i in  ty) {
40655           
40656             this.toolbars[i] = this.buildToolbar(ty[i],i);
40657         }
40658         this.tb = this.toolbars.BODY;
40659         this.tb.el.show();
40660         this.buildFooter();
40661         this.footer.show();
40662          
40663         this.rendered = true;
40664         
40665         // the all the btns;
40666         editor.on('editorevent', this.updateToolbar, this);
40667         // other toolbars need to implement this..
40668         //editor.on('editmodechange', this.updateToolbar, this);
40669     },
40670     
40671     
40672     
40673     /**
40674      * Protected method that will not generally be called directly. It triggers
40675      * a toolbar update by reading the markup state of the current selection in the editor.
40676      */
40677     updateToolbar: function(ignore_a,ignore_b,sel){
40678
40679         
40680         if(!this.editor.activated){
40681              this.editor.onFirstFocus();
40682             return;
40683         }
40684         var updateFooter = sel ? false : true;
40685         
40686         
40687         var ans = this.editor.getAllAncestors();
40688         
40689         // pick
40690         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40691         
40692         if (!sel) { 
40693             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40694             sel = sel ? sel : this.editor.doc.body;
40695             sel = sel.tagName.length ? sel : this.editor.doc.body;
40696             
40697         }
40698         // pick a menu that exists..
40699         var tn = sel.tagName.toUpperCase();
40700         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40701         
40702         tn = sel.tagName.toUpperCase();
40703         
40704         var lastSel = this.tb.selectedNode
40705         
40706         this.tb.selectedNode = sel;
40707         
40708         // if current menu does not match..
40709         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40710                 
40711             this.tb.el.hide();
40712             ///console.log("show: " + tn);
40713             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40714             this.tb.el.show();
40715             // update name
40716             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40717             
40718             
40719             // update attributes
40720             if (this.tb.fields) {
40721                 this.tb.fields.each(function(e) {
40722                    e.setValue(sel.getAttribute(e.name));
40723                 });
40724             }
40725             
40726             // update styles
40727             var st = this.tb.fields.item(0);
40728             st.store.removeAll();
40729             var cn = sel.className.split(/\s+/);
40730             
40731             var avs = [];
40732             if (this.styles['*']) {
40733                 
40734                 Roo.each(this.styles['*'], function(v) {
40735                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40736                 });
40737             }
40738             if (this.styles[tn]) { 
40739                 Roo.each(this.styles[tn], function(v) {
40740                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40741                 });
40742             }
40743             
40744             st.store.loadData(avs);
40745             st.collapse();
40746             st.setValue(cn);
40747             
40748             // flag our selected Node.
40749             this.tb.selectedNode = sel;
40750            
40751            
40752             Roo.menu.MenuMgr.hideAll();
40753
40754         }
40755         
40756         if (!updateFooter) {
40757             return;
40758         }
40759         // update the footer
40760         //
40761         var html = '';
40762         
40763         this.footerEls = ans.reverse();
40764         Roo.each(this.footerEls, function(a,i) {
40765             if (!a) { return; }
40766             html += html.length ? ' &gt; '  :  '';
40767             
40768             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40769             
40770         });
40771        
40772         // 
40773         var sz = this.footDisp.up('td').getSize();
40774         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40775         this.footDisp.dom.style.marginLeft = '5px';
40776         
40777         this.footDisp.dom.style.overflow = 'hidden';
40778         
40779         this.footDisp.dom.innerHTML = html;
40780             
40781         //this.editorsyncValue();
40782     },
40783    
40784        
40785     // private
40786     onDestroy : function(){
40787         if(this.rendered){
40788             
40789             this.tb.items.each(function(item){
40790                 if(item.menu){
40791                     item.menu.removeAll();
40792                     if(item.menu.el){
40793                         item.menu.el.destroy();
40794                     }
40795                 }
40796                 item.destroy();
40797             });
40798              
40799         }
40800     },
40801     onFirstFocus: function() {
40802         // need to do this for all the toolbars..
40803         this.tb.items.each(function(item){
40804            item.enable();
40805         });
40806     },
40807     buildToolbar: function(tlist, nm)
40808     {
40809         var editor = this.editor;
40810          // create a new element.
40811         var wdiv = editor.wrap.createChild({
40812                 tag: 'div'
40813             }, editor.wrap.dom.firstChild.nextSibling, true);
40814         
40815        
40816         var tb = new Roo.Toolbar(wdiv);
40817         // add the name..
40818         
40819         tb.add(nm+ ":&nbsp;");
40820         
40821         // styles...
40822         if (this.styles) {
40823             
40824             // this needs a multi-select checkbox...
40825             tb.addField( new Roo.form.ComboBox({
40826                 store: new Roo.data.SimpleStore({
40827                     id : 'val',
40828                     fields: ['val', 'selected'],
40829                     data : [] 
40830                 }),
40831                 name : 'className',
40832                 displayField:'val',
40833                 typeAhead: false,
40834                 mode: 'local',
40835                 editable : false,
40836                 triggerAction: 'all',
40837                 emptyText:'Select Style',
40838                 selectOnFocus:true,
40839                 width: 130,
40840                 listeners : {
40841                     'select': function(c, r, i) {
40842                         // initial support only for on class per el..
40843                         tb.selectedNode.className =  r ? r.get('val') : '';
40844                     }
40845                 }
40846     
40847             }));
40848         }
40849             
40850         
40851         
40852         for (var i in tlist) {
40853             
40854             var item = tlist[i];
40855             tb.add(item.title + ":&nbsp;");
40856             
40857             
40858             
40859             
40860             if (item.opts) {
40861                 // opts == pulldown..
40862                 tb.addField( new Roo.form.ComboBox({
40863                     store: new Roo.data.SimpleStore({
40864                         id : 'val',
40865                         fields: ['val'],
40866                         data : item.opts  
40867                     }),
40868                     name : i,
40869                     displayField:'val',
40870                     typeAhead: false,
40871                     mode: 'local',
40872                     editable : false,
40873                     triggerAction: 'all',
40874                     emptyText:'Select',
40875                     selectOnFocus:true,
40876                     width: item.width ? item.width  : 130,
40877                     listeners : {
40878                         'select': function(c, r, i) {
40879                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40880                         }
40881                     }
40882
40883                 }));
40884                 continue;
40885                     
40886                  
40887                 
40888                 tb.addField( new Roo.form.TextField({
40889                     name: i,
40890                     width: 100,
40891                     //allowBlank:false,
40892                     value: ''
40893                 }));
40894                 continue;
40895             }
40896             tb.addField( new Roo.form.TextField({
40897                 name: i,
40898                 width: item.width,
40899                 //allowBlank:true,
40900                 value: '',
40901                 listeners: {
40902                     'change' : function(f, nv, ov) {
40903                         tb.selectedNode.setAttribute(f.name, nv);
40904                     }
40905                 }
40906             }));
40907              
40908         }
40909         tb.el.on('click', function(e){
40910             e.preventDefault(); // what does this do?
40911         });
40912         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40913         tb.el.hide();
40914         tb.name = nm;
40915         // dont need to disable them... as they will get hidden
40916         return tb;
40917          
40918         
40919     },
40920     buildFooter : function()
40921     {
40922         
40923         var fel = this.editor.wrap.createChild();
40924         this.footer = new Roo.Toolbar(fel);
40925         // toolbar has scrolly on left / right?
40926         var footDisp= new Roo.Toolbar.Fill();
40927         var _t = this;
40928         this.footer.add(
40929             {
40930                 text : '&lt;',
40931                 xtype: 'Button',
40932                 handler : function() {
40933                     _t.footDisp.scrollTo('left',0,true)
40934                 }
40935             }
40936         );
40937         this.footer.add( footDisp );
40938         this.footer.add( 
40939             {
40940                 text : '&gt;',
40941                 xtype: 'Button',
40942                 handler : function() {
40943                     // no animation..
40944                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40945                 }
40946             }
40947         );
40948         var fel = Roo.get(footDisp.el);
40949         fel.addClass('x-editor-context');
40950         this.footDispWrap = fel; 
40951         this.footDispWrap.overflow  = 'hidden';
40952         
40953         this.footDisp = fel.createChild();
40954         this.footDispWrap.on('click', this.onContextClick, this)
40955         
40956         
40957     },
40958     onContextClick : function (ev,dom)
40959     {
40960         ev.preventDefault();
40961         var  cn = dom.className;
40962         Roo.log(cn);
40963         if (!cn.match(/x-ed-loc-/)) {
40964             return;
40965         }
40966         var n = cn.split('-').pop();
40967         var ans = this.footerEls;
40968         var sel = ans[n];
40969         
40970          // pick
40971         var range = this.editor.createRange();
40972         
40973         range.selectNodeContents(sel);
40974         //range.selectNode(sel);
40975         
40976         
40977         var selection = this.editor.getSelection();
40978         selection.removeAllRanges();
40979         selection.addRange(range);
40980         
40981         
40982         
40983         this.updateToolbar(null, null, sel);
40984         
40985         
40986     }
40987     
40988     
40989     
40990     
40991     
40992 });
40993
40994
40995
40996
40997
40998 /*
40999  * Based on:
41000  * Ext JS Library 1.1.1
41001  * Copyright(c) 2006-2007, Ext JS, LLC.
41002  *
41003  * Originally Released Under LGPL - original licence link has changed is not relivant.
41004  *
41005  * Fork - LGPL
41006  * <script type="text/javascript">
41007  */
41008  
41009 /**
41010  * @class Roo.form.BasicForm
41011  * @extends Roo.util.Observable
41012  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41013  * @constructor
41014  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41015  * @param {Object} config Configuration options
41016  */
41017 Roo.form.BasicForm = function(el, config){
41018     this.allItems = [];
41019     this.childForms = [];
41020     Roo.apply(this, config);
41021     /*
41022      * The Roo.form.Field items in this form.
41023      * @type MixedCollection
41024      */
41025      
41026      
41027     this.items = new Roo.util.MixedCollection(false, function(o){
41028         return o.id || (o.id = Roo.id());
41029     });
41030     this.addEvents({
41031         /**
41032          * @event beforeaction
41033          * Fires before any action is performed. Return false to cancel the action.
41034          * @param {Form} this
41035          * @param {Action} action The action to be performed
41036          */
41037         beforeaction: true,
41038         /**
41039          * @event actionfailed
41040          * Fires when an action fails.
41041          * @param {Form} this
41042          * @param {Action} action The action that failed
41043          */
41044         actionfailed : true,
41045         /**
41046          * @event actioncomplete
41047          * Fires when an action is completed.
41048          * @param {Form} this
41049          * @param {Action} action The action that completed
41050          */
41051         actioncomplete : true
41052     });
41053     if(el){
41054         this.initEl(el);
41055     }
41056     Roo.form.BasicForm.superclass.constructor.call(this);
41057 };
41058
41059 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41060     /**
41061      * @cfg {String} method
41062      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41063      */
41064     /**
41065      * @cfg {DataReader} reader
41066      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41067      * This is optional as there is built-in support for processing JSON.
41068      */
41069     /**
41070      * @cfg {DataReader} errorReader
41071      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41072      * This is completely optional as there is built-in support for processing JSON.
41073      */
41074     /**
41075      * @cfg {String} url
41076      * The URL to use for form actions if one isn't supplied in the action options.
41077      */
41078     /**
41079      * @cfg {Boolean} fileUpload
41080      * Set to true if this form is a file upload.
41081      */
41082      
41083     /**
41084      * @cfg {Object} baseParams
41085      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41086      */
41087      /**
41088      
41089     /**
41090      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41091      */
41092     timeout: 30,
41093
41094     // private
41095     activeAction : null,
41096
41097     /**
41098      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41099      * or setValues() data instead of when the form was first created.
41100      */
41101     trackResetOnLoad : false,
41102     
41103     
41104     /**
41105      * childForms - used for multi-tab forms
41106      * @type {Array}
41107      */
41108     childForms : false,
41109     
41110     /**
41111      * allItems - full list of fields.
41112      * @type {Array}
41113      */
41114     allItems : false,
41115     
41116     /**
41117      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41118      * element by passing it or its id or mask the form itself by passing in true.
41119      * @type Mixed
41120      */
41121     waitMsgTarget : false,
41122
41123     // private
41124     initEl : function(el){
41125         this.el = Roo.get(el);
41126         this.id = this.el.id || Roo.id();
41127         this.el.on('submit', this.onSubmit, this);
41128         this.el.addClass('x-form');
41129     },
41130
41131     // private
41132     onSubmit : function(e){
41133         e.stopEvent();
41134     },
41135
41136     /**
41137      * Returns true if client-side validation on the form is successful.
41138      * @return Boolean
41139      */
41140     isValid : function(){
41141         var valid = true;
41142         this.items.each(function(f){
41143            if(!f.validate()){
41144                valid = false;
41145            }
41146         });
41147         return valid;
41148     },
41149
41150     /**
41151      * Returns true if any fields in this form have changed since their original load.
41152      * @return Boolean
41153      */
41154     isDirty : function(){
41155         var dirty = false;
41156         this.items.each(function(f){
41157            if(f.isDirty()){
41158                dirty = true;
41159                return false;
41160            }
41161         });
41162         return dirty;
41163     },
41164
41165     /**
41166      * Performs a predefined action (submit or load) or custom actions you define on this form.
41167      * @param {String} actionName The name of the action type
41168      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41169      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41170      * accept other config options):
41171      * <pre>
41172 Property          Type             Description
41173 ----------------  ---------------  ----------------------------------------------------------------------------------
41174 url               String           The url for the action (defaults to the form's url)
41175 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41176 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41177 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41178                                    validate the form on the client (defaults to false)
41179      * </pre>
41180      * @return {BasicForm} this
41181      */
41182     doAction : function(action, options){
41183         if(typeof action == 'string'){
41184             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41185         }
41186         if(this.fireEvent('beforeaction', this, action) !== false){
41187             this.beforeAction(action);
41188             action.run.defer(100, action);
41189         }
41190         return this;
41191     },
41192
41193     /**
41194      * Shortcut to do a submit action.
41195      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41196      * @return {BasicForm} this
41197      */
41198     submit : function(options){
41199         this.doAction('submit', options);
41200         return this;
41201     },
41202
41203     /**
41204      * Shortcut to do a load action.
41205      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41206      * @return {BasicForm} this
41207      */
41208     load : function(options){
41209         this.doAction('load', options);
41210         return this;
41211     },
41212
41213     /**
41214      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41215      * @param {Record} record The record to edit
41216      * @return {BasicForm} this
41217      */
41218     updateRecord : function(record){
41219         record.beginEdit();
41220         var fs = record.fields;
41221         fs.each(function(f){
41222             var field = this.findField(f.name);
41223             if(field){
41224                 record.set(f.name, field.getValue());
41225             }
41226         }, this);
41227         record.endEdit();
41228         return this;
41229     },
41230
41231     /**
41232      * Loads an Roo.data.Record into this form.
41233      * @param {Record} record The record to load
41234      * @return {BasicForm} this
41235      */
41236     loadRecord : function(record){
41237         this.setValues(record.data);
41238         return this;
41239     },
41240
41241     // private
41242     beforeAction : function(action){
41243         var o = action.options;
41244         
41245        
41246         if(this.waitMsgTarget === true){
41247             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41248         }else if(this.waitMsgTarget){
41249             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41250             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41251         }else {
41252             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41253         }
41254          
41255     },
41256
41257     // private
41258     afterAction : function(action, success){
41259         this.activeAction = null;
41260         var o = action.options;
41261         
41262         if(this.waitMsgTarget === true){
41263             this.el.unmask();
41264         }else if(this.waitMsgTarget){
41265             this.waitMsgTarget.unmask();
41266         }else{
41267             Roo.MessageBox.updateProgress(1);
41268             Roo.MessageBox.hide();
41269         }
41270          
41271         if(success){
41272             if(o.reset){
41273                 this.reset();
41274             }
41275             Roo.callback(o.success, o.scope, [this, action]);
41276             this.fireEvent('actioncomplete', this, action);
41277             
41278         }else{
41279             Roo.callback(o.failure, o.scope, [this, action]);
41280             // show an error message if no failed handler is set..
41281             if (!this.hasListener('actionfailed')) {
41282                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41283             }
41284             
41285             this.fireEvent('actionfailed', this, action);
41286         }
41287         
41288     },
41289
41290     /**
41291      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41292      * @param {String} id The value to search for
41293      * @return Field
41294      */
41295     findField : function(id){
41296         var field = this.items.get(id);
41297         if(!field){
41298             this.items.each(function(f){
41299                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41300                     field = f;
41301                     return false;
41302                 }
41303             });
41304         }
41305         return field || null;
41306     },
41307
41308     /**
41309      * Add a secondary form to this one, 
41310      * Used to provide tabbed forms. One form is primary, with hidden values 
41311      * which mirror the elements from the other forms.
41312      * 
41313      * @param {Roo.form.Form} form to add.
41314      * 
41315      */
41316     addForm : function(form)
41317     {
41318        
41319         if (this.childForms.indexOf(form) > -1) {
41320             // already added..
41321             return;
41322         }
41323         this.childForms.push(form);
41324         var n = '';
41325         Roo.each(form.allItems, function (fe) {
41326             
41327             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41328             if (this.findField(n)) { // already added..
41329                 return;
41330             }
41331             var add = new Roo.form.Hidden({
41332                 name : n
41333             });
41334             add.render(this.el);
41335             
41336             this.add( add );
41337         }, this);
41338         
41339     },
41340     /**
41341      * Mark fields in this form invalid in bulk.
41342      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41343      * @return {BasicForm} this
41344      */
41345     markInvalid : function(errors){
41346         if(errors instanceof Array){
41347             for(var i = 0, len = errors.length; i < len; i++){
41348                 var fieldError = errors[i];
41349                 var f = this.findField(fieldError.id);
41350                 if(f){
41351                     f.markInvalid(fieldError.msg);
41352                 }
41353             }
41354         }else{
41355             var field, id;
41356             for(id in errors){
41357                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41358                     field.markInvalid(errors[id]);
41359                 }
41360             }
41361         }
41362         Roo.each(this.childForms || [], function (f) {
41363             f.markInvalid(errors);
41364         });
41365         
41366         return this;
41367     },
41368
41369     /**
41370      * Set values for fields in this form in bulk.
41371      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41372      * @return {BasicForm} this
41373      */
41374     setValues : function(values){
41375         if(values instanceof Array){ // array of objects
41376             for(var i = 0, len = values.length; i < len; i++){
41377                 var v = values[i];
41378                 var f = this.findField(v.id);
41379                 if(f){
41380                     f.setValue(v.value);
41381                     if(this.trackResetOnLoad){
41382                         f.originalValue = f.getValue();
41383                     }
41384                 }
41385             }
41386         }else{ // object hash
41387             var field, id;
41388             for(id in values){
41389                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41390                     
41391                     if (field.setFromData && 
41392                         field.valueField && 
41393                         field.displayField &&
41394                         // combos' with local stores can 
41395                         // be queried via setValue()
41396                         // to set their value..
41397                         (field.store && !field.store.isLocal)
41398                         ) {
41399                         // it's a combo
41400                         var sd = { };
41401                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41402                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41403                         field.setFromData(sd);
41404                         
41405                     } else {
41406                         field.setValue(values[id]);
41407                     }
41408                     
41409                     
41410                     if(this.trackResetOnLoad){
41411                         field.originalValue = field.getValue();
41412                     }
41413                 }
41414             }
41415         }
41416          
41417         Roo.each(this.childForms || [], function (f) {
41418             f.setValues(values);
41419         });
41420                 
41421         return this;
41422     },
41423
41424     /**
41425      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41426      * they are returned as an array.
41427      * @param {Boolean} asString
41428      * @return {Object}
41429      */
41430     getValues : function(asString){
41431         if (this.childForms) {
41432             // copy values from the child forms
41433             Roo.each(this.childForms, function (f) {
41434                 this.setValues(f.getValues());
41435             }, this);
41436         }
41437         
41438         
41439         
41440         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41441         if(asString === true){
41442             return fs;
41443         }
41444         return Roo.urlDecode(fs);
41445     },
41446     
41447     /**
41448      * Returns the fields in this form as an object with key/value pairs. 
41449      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41450      * @return {Object}
41451      */
41452     getFieldValues : function()
41453     {
41454         if (this.childForms) {
41455             // copy values from the child forms
41456             Roo.each(this.childForms, function (f) {
41457                 this.setValues(f.getValues());
41458             }, this);
41459         }
41460         
41461         var ret = {};
41462         this.items.each(function(f){
41463             if (!f.getName()) {
41464                 return;
41465             }
41466             var v = f.getValue();
41467             if ((typeof(v) == 'object') && f.getRawValue) {
41468                 v = f.getRawValue() ; // dates..
41469             }
41470             ret[f.getName()] = v;
41471         });
41472         
41473         return ret;
41474     },
41475
41476     /**
41477      * Clears all invalid messages in this form.
41478      * @return {BasicForm} this
41479      */
41480     clearInvalid : function(){
41481         this.items.each(function(f){
41482            f.clearInvalid();
41483         });
41484         
41485         Roo.each(this.childForms || [], function (f) {
41486             f.clearInvalid();
41487         });
41488         
41489         
41490         return this;
41491     },
41492
41493     /**
41494      * Resets this form.
41495      * @return {BasicForm} this
41496      */
41497     reset : function(){
41498         this.items.each(function(f){
41499             f.reset();
41500         });
41501         
41502         Roo.each(this.childForms || [], function (f) {
41503             f.reset();
41504         });
41505        
41506         
41507         return this;
41508     },
41509
41510     /**
41511      * Add Roo.form components to this form.
41512      * @param {Field} field1
41513      * @param {Field} field2 (optional)
41514      * @param {Field} etc (optional)
41515      * @return {BasicForm} this
41516      */
41517     add : function(){
41518         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41519         return this;
41520     },
41521
41522
41523     /**
41524      * Removes a field from the items collection (does NOT remove its markup).
41525      * @param {Field} field
41526      * @return {BasicForm} this
41527      */
41528     remove : function(field){
41529         this.items.remove(field);
41530         return this;
41531     },
41532
41533     /**
41534      * Looks at the fields in this form, checks them for an id attribute,
41535      * and calls applyTo on the existing dom element with that id.
41536      * @return {BasicForm} this
41537      */
41538     render : function(){
41539         this.items.each(function(f){
41540             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41541                 f.applyTo(f.id);
41542             }
41543         });
41544         return this;
41545     },
41546
41547     /**
41548      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41549      * @param {Object} values
41550      * @return {BasicForm} this
41551      */
41552     applyToFields : function(o){
41553         this.items.each(function(f){
41554            Roo.apply(f, o);
41555         });
41556         return this;
41557     },
41558
41559     /**
41560      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41561      * @param {Object} values
41562      * @return {BasicForm} this
41563      */
41564     applyIfToFields : function(o){
41565         this.items.each(function(f){
41566            Roo.applyIf(f, o);
41567         });
41568         return this;
41569     }
41570 });
41571
41572 // back compat
41573 Roo.BasicForm = Roo.form.BasicForm;/*
41574  * Based on:
41575  * Ext JS Library 1.1.1
41576  * Copyright(c) 2006-2007, Ext JS, LLC.
41577  *
41578  * Originally Released Under LGPL - original licence link has changed is not relivant.
41579  *
41580  * Fork - LGPL
41581  * <script type="text/javascript">
41582  */
41583
41584 /**
41585  * @class Roo.form.Form
41586  * @extends Roo.form.BasicForm
41587  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41588  * @constructor
41589  * @param {Object} config Configuration options
41590  */
41591 Roo.form.Form = function(config){
41592     var xitems =  [];
41593     if (config.items) {
41594         xitems = config.items;
41595         delete config.items;
41596     }
41597    
41598     
41599     Roo.form.Form.superclass.constructor.call(this, null, config);
41600     this.url = this.url || this.action;
41601     if(!this.root){
41602         this.root = new Roo.form.Layout(Roo.applyIf({
41603             id: Roo.id()
41604         }, config));
41605     }
41606     this.active = this.root;
41607     /**
41608      * Array of all the buttons that have been added to this form via {@link addButton}
41609      * @type Array
41610      */
41611     this.buttons = [];
41612     this.allItems = [];
41613     this.addEvents({
41614         /**
41615          * @event clientvalidation
41616          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41617          * @param {Form} this
41618          * @param {Boolean} valid true if the form has passed client-side validation
41619          */
41620         clientvalidation: true,
41621         /**
41622          * @event rendered
41623          * Fires when the form is rendered
41624          * @param {Roo.form.Form} form
41625          */
41626         rendered : true
41627     });
41628     
41629     if (this.progressUrl) {
41630             // push a hidden field onto the list of fields..
41631             this.addxtype( {
41632                     xns: Roo.form, 
41633                     xtype : 'Hidden', 
41634                     name : 'UPLOAD_IDENTIFIER' 
41635             });
41636         }
41637         
41638     
41639     Roo.each(xitems, this.addxtype, this);
41640     
41641     
41642     
41643 };
41644
41645 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41646     /**
41647      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41648      */
41649     /**
41650      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41651      */
41652     /**
41653      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41654      */
41655     buttonAlign:'center',
41656
41657     /**
41658      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41659      */
41660     minButtonWidth:75,
41661
41662     /**
41663      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41664      * This property cascades to child containers if not set.
41665      */
41666     labelAlign:'left',
41667
41668     /**
41669      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41670      * fires a looping event with that state. This is required to bind buttons to the valid
41671      * state using the config value formBind:true on the button.
41672      */
41673     monitorValid : false,
41674
41675     /**
41676      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41677      */
41678     monitorPoll : 200,
41679     
41680     /**
41681      * @cfg {String} progressUrl - Url to return progress data 
41682      */
41683     
41684     progressUrl : false,
41685   
41686     /**
41687      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41688      * fields are added and the column is closed. If no fields are passed the column remains open
41689      * until end() is called.
41690      * @param {Object} config The config to pass to the column
41691      * @param {Field} field1 (optional)
41692      * @param {Field} field2 (optional)
41693      * @param {Field} etc (optional)
41694      * @return Column The column container object
41695      */
41696     column : function(c){
41697         var col = new Roo.form.Column(c);
41698         this.start(col);
41699         if(arguments.length > 1){ // duplicate code required because of Opera
41700             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41701             this.end();
41702         }
41703         return col;
41704     },
41705
41706     /**
41707      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41708      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41709      * until end() is called.
41710      * @param {Object} config The config to pass to the fieldset
41711      * @param {Field} field1 (optional)
41712      * @param {Field} field2 (optional)
41713      * @param {Field} etc (optional)
41714      * @return FieldSet The fieldset container object
41715      */
41716     fieldset : function(c){
41717         var fs = new Roo.form.FieldSet(c);
41718         this.start(fs);
41719         if(arguments.length > 1){ // duplicate code required because of Opera
41720             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41721             this.end();
41722         }
41723         return fs;
41724     },
41725
41726     /**
41727      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41728      * fields are added and the container is closed. If no fields are passed the container remains open
41729      * until end() is called.
41730      * @param {Object} config The config to pass to the Layout
41731      * @param {Field} field1 (optional)
41732      * @param {Field} field2 (optional)
41733      * @param {Field} etc (optional)
41734      * @return Layout The container object
41735      */
41736     container : function(c){
41737         var l = new Roo.form.Layout(c);
41738         this.start(l);
41739         if(arguments.length > 1){ // duplicate code required because of Opera
41740             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41741             this.end();
41742         }
41743         return l;
41744     },
41745
41746     /**
41747      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41748      * @param {Object} container A Roo.form.Layout or subclass of Layout
41749      * @return {Form} this
41750      */
41751     start : function(c){
41752         // cascade label info
41753         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41754         this.active.stack.push(c);
41755         c.ownerCt = this.active;
41756         this.active = c;
41757         return this;
41758     },
41759
41760     /**
41761      * Closes the current open container
41762      * @return {Form} this
41763      */
41764     end : function(){
41765         if(this.active == this.root){
41766             return this;
41767         }
41768         this.active = this.active.ownerCt;
41769         return this;
41770     },
41771
41772     /**
41773      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41774      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41775      * as the label of the field.
41776      * @param {Field} field1
41777      * @param {Field} field2 (optional)
41778      * @param {Field} etc. (optional)
41779      * @return {Form} this
41780      */
41781     add : function(){
41782         this.active.stack.push.apply(this.active.stack, arguments);
41783         this.allItems.push.apply(this.allItems,arguments);
41784         var r = [];
41785         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41786             if(a[i].isFormField){
41787                 r.push(a[i]);
41788             }
41789         }
41790         if(r.length > 0){
41791             Roo.form.Form.superclass.add.apply(this, r);
41792         }
41793         return this;
41794     },
41795     
41796
41797     
41798     
41799     
41800      /**
41801      * Find any element that has been added to a form, using it's ID or name
41802      * This can include framesets, columns etc. along with regular fields..
41803      * @param {String} id - id or name to find.
41804      
41805      * @return {Element} e - or false if nothing found.
41806      */
41807     findbyId : function(id)
41808     {
41809         var ret = false;
41810         if (!id) {
41811             return ret;
41812         }
41813         Roo.each(this.allItems, function(f){
41814             if (f.id == id || f.name == id ){
41815                 ret = f;
41816                 return false;
41817             }
41818         });
41819         return ret;
41820     },
41821
41822     
41823     
41824     /**
41825      * Render this form into the passed container. This should only be called once!
41826      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41827      * @return {Form} this
41828      */
41829     render : function(ct)
41830     {
41831         
41832         
41833         
41834         ct = Roo.get(ct);
41835         var o = this.autoCreate || {
41836             tag: 'form',
41837             method : this.method || 'POST',
41838             id : this.id || Roo.id()
41839         };
41840         this.initEl(ct.createChild(o));
41841
41842         this.root.render(this.el);
41843         
41844        
41845              
41846         this.items.each(function(f){
41847             f.render('x-form-el-'+f.id);
41848         });
41849
41850         if(this.buttons.length > 0){
41851             // tables are required to maintain order and for correct IE layout
41852             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41853                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41854                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41855             }}, null, true);
41856             var tr = tb.getElementsByTagName('tr')[0];
41857             for(var i = 0, len = this.buttons.length; i < len; i++) {
41858                 var b = this.buttons[i];
41859                 var td = document.createElement('td');
41860                 td.className = 'x-form-btn-td';
41861                 b.render(tr.appendChild(td));
41862             }
41863         }
41864         if(this.monitorValid){ // initialize after render
41865             this.startMonitoring();
41866         }
41867         this.fireEvent('rendered', this);
41868         return this;
41869     },
41870
41871     /**
41872      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41873      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41874      * object or a valid Roo.DomHelper element config
41875      * @param {Function} handler The function called when the button is clicked
41876      * @param {Object} scope (optional) The scope of the handler function
41877      * @return {Roo.Button}
41878      */
41879     addButton : function(config, handler, scope){
41880         var bc = {
41881             handler: handler,
41882             scope: scope,
41883             minWidth: this.minButtonWidth,
41884             hideParent:true
41885         };
41886         if(typeof config == "string"){
41887             bc.text = config;
41888         }else{
41889             Roo.apply(bc, config);
41890         }
41891         var btn = new Roo.Button(null, bc);
41892         this.buttons.push(btn);
41893         return btn;
41894     },
41895
41896      /**
41897      * Adds a series of form elements (using the xtype property as the factory method.
41898      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41899      * @param {Object} config 
41900      */
41901     
41902     addxtype : function()
41903     {
41904         var ar = Array.prototype.slice.call(arguments, 0);
41905         var ret = false;
41906         for(var i = 0; i < ar.length; i++) {
41907             if (!ar[i]) {
41908                 continue; // skip -- if this happends something invalid got sent, we 
41909                 // should ignore it, as basically that interface element will not show up
41910                 // and that should be pretty obvious!!
41911             }
41912             
41913             if (Roo.form[ar[i].xtype]) {
41914                 ar[i].form = this;
41915                 var fe = Roo.factory(ar[i], Roo.form);
41916                 if (!ret) {
41917                     ret = fe;
41918                 }
41919                 fe.form = this;
41920                 if (fe.store) {
41921                     fe.store.form = this;
41922                 }
41923                 if (fe.isLayout) {  
41924                          
41925                     this.start(fe);
41926                     this.allItems.push(fe);
41927                     if (fe.items && fe.addxtype) {
41928                         fe.addxtype.apply(fe, fe.items);
41929                         delete fe.items;
41930                     }
41931                      this.end();
41932                     continue;
41933                 }
41934                 
41935                 
41936                  
41937                 this.add(fe);
41938               //  console.log('adding ' + ar[i].xtype);
41939             }
41940             if (ar[i].xtype == 'Button') {  
41941                 //console.log('adding button');
41942                 //console.log(ar[i]);
41943                 this.addButton(ar[i]);
41944                 this.allItems.push(fe);
41945                 continue;
41946             }
41947             
41948             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41949                 alert('end is not supported on xtype any more, use items');
41950             //    this.end();
41951             //    //console.log('adding end');
41952             }
41953             
41954         }
41955         return ret;
41956     },
41957     
41958     /**
41959      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41960      * option "monitorValid"
41961      */
41962     startMonitoring : function(){
41963         if(!this.bound){
41964             this.bound = true;
41965             Roo.TaskMgr.start({
41966                 run : this.bindHandler,
41967                 interval : this.monitorPoll || 200,
41968                 scope: this
41969             });
41970         }
41971     },
41972
41973     /**
41974      * Stops monitoring of the valid state of this form
41975      */
41976     stopMonitoring : function(){
41977         this.bound = false;
41978     },
41979
41980     // private
41981     bindHandler : function(){
41982         if(!this.bound){
41983             return false; // stops binding
41984         }
41985         var valid = true;
41986         this.items.each(function(f){
41987             if(!f.isValid(true)){
41988                 valid = false;
41989                 return false;
41990             }
41991         });
41992         for(var i = 0, len = this.buttons.length; i < len; i++){
41993             var btn = this.buttons[i];
41994             if(btn.formBind === true && btn.disabled === valid){
41995                 btn.setDisabled(!valid);
41996             }
41997         }
41998         this.fireEvent('clientvalidation', this, valid);
41999     }
42000     
42001     
42002     
42003     
42004     
42005     
42006     
42007     
42008 });
42009
42010
42011 // back compat
42012 Roo.Form = Roo.form.Form;
42013 /*
42014  * Based on:
42015  * Ext JS Library 1.1.1
42016  * Copyright(c) 2006-2007, Ext JS, LLC.
42017  *
42018  * Originally Released Under LGPL - original licence link has changed is not relivant.
42019  *
42020  * Fork - LGPL
42021  * <script type="text/javascript">
42022  */
42023  
42024  /**
42025  * @class Roo.form.Action
42026  * Internal Class used to handle form actions
42027  * @constructor
42028  * @param {Roo.form.BasicForm} el The form element or its id
42029  * @param {Object} config Configuration options
42030  */
42031  
42032  
42033 // define the action interface
42034 Roo.form.Action = function(form, options){
42035     this.form = form;
42036     this.options = options || {};
42037 };
42038 /**
42039  * Client Validation Failed
42040  * @const 
42041  */
42042 Roo.form.Action.CLIENT_INVALID = 'client';
42043 /**
42044  * Server Validation Failed
42045  * @const 
42046  */
42047  Roo.form.Action.SERVER_INVALID = 'server';
42048  /**
42049  * Connect to Server Failed
42050  * @const 
42051  */
42052 Roo.form.Action.CONNECT_FAILURE = 'connect';
42053 /**
42054  * Reading Data from Server Failed
42055  * @const 
42056  */
42057 Roo.form.Action.LOAD_FAILURE = 'load';
42058
42059 Roo.form.Action.prototype = {
42060     type : 'default',
42061     failureType : undefined,
42062     response : undefined,
42063     result : undefined,
42064
42065     // interface method
42066     run : function(options){
42067
42068     },
42069
42070     // interface method
42071     success : function(response){
42072
42073     },
42074
42075     // interface method
42076     handleResponse : function(response){
42077
42078     },
42079
42080     // default connection failure
42081     failure : function(response){
42082         
42083         this.response = response;
42084         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42085         this.form.afterAction(this, false);
42086     },
42087
42088     processResponse : function(response){
42089         this.response = response;
42090         if(!response.responseText){
42091             return true;
42092         }
42093         this.result = this.handleResponse(response);
42094         return this.result;
42095     },
42096
42097     // utility functions used internally
42098     getUrl : function(appendParams){
42099         var url = this.options.url || this.form.url || this.form.el.dom.action;
42100         if(appendParams){
42101             var p = this.getParams();
42102             if(p){
42103                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42104             }
42105         }
42106         return url;
42107     },
42108
42109     getMethod : function(){
42110         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42111     },
42112
42113     getParams : function(){
42114         var bp = this.form.baseParams;
42115         var p = this.options.params;
42116         if(p){
42117             if(typeof p == "object"){
42118                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42119             }else if(typeof p == 'string' && bp){
42120                 p += '&' + Roo.urlEncode(bp);
42121             }
42122         }else if(bp){
42123             p = Roo.urlEncode(bp);
42124         }
42125         return p;
42126     },
42127
42128     createCallback : function(){
42129         return {
42130             success: this.success,
42131             failure: this.failure,
42132             scope: this,
42133             timeout: (this.form.timeout*1000),
42134             upload: this.form.fileUpload ? this.success : undefined
42135         };
42136     }
42137 };
42138
42139 Roo.form.Action.Submit = function(form, options){
42140     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42141 };
42142
42143 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42144     type : 'submit',
42145
42146     haveProgress : false,
42147     uploadComplete : false,
42148     
42149     // uploadProgress indicator.
42150     uploadProgress : function()
42151     {
42152         if (!this.form.progressUrl) {
42153             return;
42154         }
42155         
42156         if (!this.haveProgress) {
42157             Roo.MessageBox.progress("Uploading", "Uploading");
42158         }
42159         if (this.uploadComplete) {
42160            Roo.MessageBox.hide();
42161            return;
42162         }
42163         
42164         this.haveProgress = true;
42165    
42166         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42167         
42168         var c = new Roo.data.Connection();
42169         c.request({
42170             url : this.form.progressUrl,
42171             params: {
42172                 id : uid
42173             },
42174             method: 'GET',
42175             success : function(req){
42176                //console.log(data);
42177                 var rdata = false;
42178                 var edata;
42179                 try  {
42180                    rdata = Roo.decode(req.responseText)
42181                 } catch (e) {
42182                     Roo.log("Invalid data from server..");
42183                     Roo.log(edata);
42184                     return;
42185                 }
42186                 if (!rdata || !rdata.success) {
42187                     Roo.log(rdata);
42188                     return;
42189                 }
42190                 var data = rdata.data;
42191                 
42192                 if (this.uploadComplete) {
42193                    Roo.MessageBox.hide();
42194                    return;
42195                 }
42196                    
42197                 if (data){
42198                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42199                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42200                     );
42201                 }
42202                 this.uploadProgress.defer(2000,this);
42203             },
42204        
42205             failure: function(data) {
42206                 Roo.log('progress url failed ');
42207                 Roo.log(data);
42208             },
42209             scope : this
42210         });
42211            
42212     },
42213     
42214     
42215     run : function()
42216     {
42217         // run get Values on the form, so it syncs any secondary forms.
42218         this.form.getValues();
42219         
42220         var o = this.options;
42221         var method = this.getMethod();
42222         var isPost = method == 'POST';
42223         if(o.clientValidation === false || this.form.isValid()){
42224             
42225             if (this.form.progressUrl) {
42226                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42227                     (new Date() * 1) + '' + Math.random());
42228                     
42229             } 
42230             
42231             
42232             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42233                 form:this.form.el.dom,
42234                 url:this.getUrl(!isPost),
42235                 method: method,
42236                 params:isPost ? this.getParams() : null,
42237                 isUpload: this.form.fileUpload
42238             }));
42239             
42240             this.uploadProgress();
42241
42242         }else if (o.clientValidation !== false){ // client validation failed
42243             this.failureType = Roo.form.Action.CLIENT_INVALID;
42244             this.form.afterAction(this, false);
42245         }
42246     },
42247
42248     success : function(response)
42249     {
42250         this.uploadComplete= true;
42251         if (this.haveProgress) {
42252             Roo.MessageBox.hide();
42253         }
42254         
42255         
42256         var result = this.processResponse(response);
42257         if(result === true || result.success){
42258             this.form.afterAction(this, true);
42259             return;
42260         }
42261         if(result.errors){
42262             this.form.markInvalid(result.errors);
42263             this.failureType = Roo.form.Action.SERVER_INVALID;
42264         }
42265         this.form.afterAction(this, false);
42266     },
42267     failure : function(response)
42268     {
42269         this.uploadComplete= true;
42270         if (this.haveProgress) {
42271             Roo.MessageBox.hide();
42272         }
42273         
42274         
42275         this.response = response;
42276         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42277         this.form.afterAction(this, false);
42278     },
42279     
42280     handleResponse : function(response){
42281         if(this.form.errorReader){
42282             var rs = this.form.errorReader.read(response);
42283             var errors = [];
42284             if(rs.records){
42285                 for(var i = 0, len = rs.records.length; i < len; i++) {
42286                     var r = rs.records[i];
42287                     errors[i] = r.data;
42288                 }
42289             }
42290             if(errors.length < 1){
42291                 errors = null;
42292             }
42293             return {
42294                 success : rs.success,
42295                 errors : errors
42296             };
42297         }
42298         var ret = false;
42299         try {
42300             ret = Roo.decode(response.responseText);
42301         } catch (e) {
42302             ret = {
42303                 success: false,
42304                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42305                 errors : []
42306             };
42307         }
42308         return ret;
42309         
42310     }
42311 });
42312
42313
42314 Roo.form.Action.Load = function(form, options){
42315     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42316     this.reader = this.form.reader;
42317 };
42318
42319 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42320     type : 'load',
42321
42322     run : function(){
42323         
42324         Roo.Ajax.request(Roo.apply(
42325                 this.createCallback(), {
42326                     method:this.getMethod(),
42327                     url:this.getUrl(false),
42328                     params:this.getParams()
42329         }));
42330     },
42331
42332     success : function(response){
42333         
42334         var result = this.processResponse(response);
42335         if(result === true || !result.success || !result.data){
42336             this.failureType = Roo.form.Action.LOAD_FAILURE;
42337             this.form.afterAction(this, false);
42338             return;
42339         }
42340         this.form.clearInvalid();
42341         this.form.setValues(result.data);
42342         this.form.afterAction(this, true);
42343     },
42344
42345     handleResponse : function(response){
42346         if(this.form.reader){
42347             var rs = this.form.reader.read(response);
42348             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42349             return {
42350                 success : rs.success,
42351                 data : data
42352             };
42353         }
42354         return Roo.decode(response.responseText);
42355     }
42356 });
42357
42358 Roo.form.Action.ACTION_TYPES = {
42359     'load' : Roo.form.Action.Load,
42360     'submit' : Roo.form.Action.Submit
42361 };/*
42362  * Based on:
42363  * Ext JS Library 1.1.1
42364  * Copyright(c) 2006-2007, Ext JS, LLC.
42365  *
42366  * Originally Released Under LGPL - original licence link has changed is not relivant.
42367  *
42368  * Fork - LGPL
42369  * <script type="text/javascript">
42370  */
42371  
42372 /**
42373  * @class Roo.form.Layout
42374  * @extends Roo.Component
42375  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42376  * @constructor
42377  * @param {Object} config Configuration options
42378  */
42379 Roo.form.Layout = function(config){
42380     var xitems = [];
42381     if (config.items) {
42382         xitems = config.items;
42383         delete config.items;
42384     }
42385     Roo.form.Layout.superclass.constructor.call(this, config);
42386     this.stack = [];
42387     Roo.each(xitems, this.addxtype, this);
42388      
42389 };
42390
42391 Roo.extend(Roo.form.Layout, Roo.Component, {
42392     /**
42393      * @cfg {String/Object} autoCreate
42394      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42395      */
42396     /**
42397      * @cfg {String/Object/Function} style
42398      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42399      * a function which returns such a specification.
42400      */
42401     /**
42402      * @cfg {String} labelAlign
42403      * Valid values are "left," "top" and "right" (defaults to "left")
42404      */
42405     /**
42406      * @cfg {Number} labelWidth
42407      * Fixed width in pixels of all field labels (defaults to undefined)
42408      */
42409     /**
42410      * @cfg {Boolean} clear
42411      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42412      */
42413     clear : true,
42414     /**
42415      * @cfg {String} labelSeparator
42416      * The separator to use after field labels (defaults to ':')
42417      */
42418     labelSeparator : ':',
42419     /**
42420      * @cfg {Boolean} hideLabels
42421      * True to suppress the display of field labels in this layout (defaults to false)
42422      */
42423     hideLabels : false,
42424
42425     // private
42426     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42427     
42428     isLayout : true,
42429     
42430     // private
42431     onRender : function(ct, position){
42432         if(this.el){ // from markup
42433             this.el = Roo.get(this.el);
42434         }else {  // generate
42435             var cfg = this.getAutoCreate();
42436             this.el = ct.createChild(cfg, position);
42437         }
42438         if(this.style){
42439             this.el.applyStyles(this.style);
42440         }
42441         if(this.labelAlign){
42442             this.el.addClass('x-form-label-'+this.labelAlign);
42443         }
42444         if(this.hideLabels){
42445             this.labelStyle = "display:none";
42446             this.elementStyle = "padding-left:0;";
42447         }else{
42448             if(typeof this.labelWidth == 'number'){
42449                 this.labelStyle = "width:"+this.labelWidth+"px;";
42450                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42451             }
42452             if(this.labelAlign == 'top'){
42453                 this.labelStyle = "width:auto;";
42454                 this.elementStyle = "padding-left:0;";
42455             }
42456         }
42457         var stack = this.stack;
42458         var slen = stack.length;
42459         if(slen > 0){
42460             if(!this.fieldTpl){
42461                 var t = new Roo.Template(
42462                     '<div class="x-form-item {5}">',
42463                         '<label for="{0}" style="{2}">{1}{4}</label>',
42464                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42465                         '</div>',
42466                     '</div><div class="x-form-clear-left"></div>'
42467                 );
42468                 t.disableFormats = true;
42469                 t.compile();
42470                 Roo.form.Layout.prototype.fieldTpl = t;
42471             }
42472             for(var i = 0; i < slen; i++) {
42473                 if(stack[i].isFormField){
42474                     this.renderField(stack[i]);
42475                 }else{
42476                     this.renderComponent(stack[i]);
42477                 }
42478             }
42479         }
42480         if(this.clear){
42481             this.el.createChild({cls:'x-form-clear'});
42482         }
42483     },
42484
42485     // private
42486     renderField : function(f){
42487         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42488                f.id, //0
42489                f.fieldLabel, //1
42490                f.labelStyle||this.labelStyle||'', //2
42491                this.elementStyle||'', //3
42492                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42493                f.itemCls||this.itemCls||''  //5
42494        ], true).getPrevSibling());
42495     },
42496
42497     // private
42498     renderComponent : function(c){
42499         c.render(c.isLayout ? this.el : this.el.createChild());    
42500     },
42501     /**
42502      * Adds a object form elements (using the xtype property as the factory method.)
42503      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42504      * @param {Object} config 
42505      */
42506     addxtype : function(o)
42507     {
42508         // create the lement.
42509         o.form = this.form;
42510         var fe = Roo.factory(o, Roo.form);
42511         this.form.allItems.push(fe);
42512         this.stack.push(fe);
42513         
42514         if (fe.isFormField) {
42515             this.form.items.add(fe);
42516         }
42517          
42518         return fe;
42519     }
42520 });
42521
42522 /**
42523  * @class Roo.form.Column
42524  * @extends Roo.form.Layout
42525  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42526  * @constructor
42527  * @param {Object} config Configuration options
42528  */
42529 Roo.form.Column = function(config){
42530     Roo.form.Column.superclass.constructor.call(this, config);
42531 };
42532
42533 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42534     /**
42535      * @cfg {Number/String} width
42536      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42537      */
42538     /**
42539      * @cfg {String/Object} autoCreate
42540      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42541      */
42542
42543     // private
42544     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42545
42546     // private
42547     onRender : function(ct, position){
42548         Roo.form.Column.superclass.onRender.call(this, ct, position);
42549         if(this.width){
42550             this.el.setWidth(this.width);
42551         }
42552     }
42553 });
42554
42555
42556 /**
42557  * @class Roo.form.Row
42558  * @extends Roo.form.Layout
42559  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42560  * @constructor
42561  * @param {Object} config Configuration options
42562  */
42563
42564  
42565 Roo.form.Row = function(config){
42566     Roo.form.Row.superclass.constructor.call(this, config);
42567 };
42568  
42569 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42570       /**
42571      * @cfg {Number/String} width
42572      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42573      */
42574     /**
42575      * @cfg {Number/String} height
42576      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42577      */
42578     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42579     
42580     padWidth : 20,
42581     // private
42582     onRender : function(ct, position){
42583         //console.log('row render');
42584         if(!this.rowTpl){
42585             var t = new Roo.Template(
42586                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42587                     '<label for="{0}" style="{2}">{1}{4}</label>',
42588                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42589                     '</div>',
42590                 '</div>'
42591             );
42592             t.disableFormats = true;
42593             t.compile();
42594             Roo.form.Layout.prototype.rowTpl = t;
42595         }
42596         this.fieldTpl = this.rowTpl;
42597         
42598         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42599         var labelWidth = 100;
42600         
42601         if ((this.labelAlign != 'top')) {
42602             if (typeof this.labelWidth == 'number') {
42603                 labelWidth = this.labelWidth
42604             }
42605             this.padWidth =  20 + labelWidth;
42606             
42607         }
42608         
42609         Roo.form.Column.superclass.onRender.call(this, ct, position);
42610         if(this.width){
42611             this.el.setWidth(this.width);
42612         }
42613         if(this.height){
42614             this.el.setHeight(this.height);
42615         }
42616     },
42617     
42618     // private
42619     renderField : function(f){
42620         f.fieldEl = this.fieldTpl.append(this.el, [
42621                f.id, f.fieldLabel,
42622                f.labelStyle||this.labelStyle||'',
42623                this.elementStyle||'',
42624                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42625                f.itemCls||this.itemCls||'',
42626                f.width ? f.width + this.padWidth : 160 + this.padWidth
42627        ],true);
42628     }
42629 });
42630  
42631
42632 /**
42633  * @class Roo.form.FieldSet
42634  * @extends Roo.form.Layout
42635  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42636  * @constructor
42637  * @param {Object} config Configuration options
42638  */
42639 Roo.form.FieldSet = function(config){
42640     Roo.form.FieldSet.superclass.constructor.call(this, config);
42641 };
42642
42643 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42644     /**
42645      * @cfg {String} legend
42646      * The text to display as the legend for the FieldSet (defaults to '')
42647      */
42648     /**
42649      * @cfg {String/Object} autoCreate
42650      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42651      */
42652
42653     // private
42654     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42655
42656     // private
42657     onRender : function(ct, position){
42658         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42659         if(this.legend){
42660             this.setLegend(this.legend);
42661         }
42662     },
42663
42664     // private
42665     setLegend : function(text){
42666         if(this.rendered){
42667             this.el.child('legend').update(text);
42668         }
42669     }
42670 });/*
42671  * Based on:
42672  * Ext JS Library 1.1.1
42673  * Copyright(c) 2006-2007, Ext JS, LLC.
42674  *
42675  * Originally Released Under LGPL - original licence link has changed is not relivant.
42676  *
42677  * Fork - LGPL
42678  * <script type="text/javascript">
42679  */
42680 /**
42681  * @class Roo.form.VTypes
42682  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42683  * @singleton
42684  */
42685 Roo.form.VTypes = function(){
42686     // closure these in so they are only created once.
42687     var alpha = /^[a-zA-Z_]+$/;
42688     var alphanum = /^[a-zA-Z0-9_]+$/;
42689     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42690     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42691
42692     // All these messages and functions are configurable
42693     return {
42694         /**
42695          * The function used to validate email addresses
42696          * @param {String} value The email address
42697          */
42698         'email' : function(v){
42699             return email.test(v);
42700         },
42701         /**
42702          * The error text to display when the email validation function returns false
42703          * @type String
42704          */
42705         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42706         /**
42707          * The keystroke filter mask to be applied on email input
42708          * @type RegExp
42709          */
42710         'emailMask' : /[a-z0-9_\.\-@]/i,
42711
42712         /**
42713          * The function used to validate URLs
42714          * @param {String} value The URL
42715          */
42716         'url' : function(v){
42717             return url.test(v);
42718         },
42719         /**
42720          * The error text to display when the url validation function returns false
42721          * @type String
42722          */
42723         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42724         
42725         /**
42726          * The function used to validate alpha values
42727          * @param {String} value The value
42728          */
42729         'alpha' : function(v){
42730             return alpha.test(v);
42731         },
42732         /**
42733          * The error text to display when the alpha validation function returns false
42734          * @type String
42735          */
42736         'alphaText' : 'This field should only contain letters and _',
42737         /**
42738          * The keystroke filter mask to be applied on alpha input
42739          * @type RegExp
42740          */
42741         'alphaMask' : /[a-z_]/i,
42742
42743         /**
42744          * The function used to validate alphanumeric values
42745          * @param {String} value The value
42746          */
42747         'alphanum' : function(v){
42748             return alphanum.test(v);
42749         },
42750         /**
42751          * The error text to display when the alphanumeric validation function returns false
42752          * @type String
42753          */
42754         'alphanumText' : 'This field should only contain letters, numbers and _',
42755         /**
42756          * The keystroke filter mask to be applied on alphanumeric input
42757          * @type RegExp
42758          */
42759         'alphanumMask' : /[a-z0-9_]/i
42760     };
42761 }();//<script type="text/javascript">
42762
42763 /**
42764  * @class Roo.form.FCKeditor
42765  * @extends Roo.form.TextArea
42766  * Wrapper around the FCKEditor http://www.fckeditor.net
42767  * @constructor
42768  * Creates a new FCKeditor
42769  * @param {Object} config Configuration options
42770  */
42771 Roo.form.FCKeditor = function(config){
42772     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42773     this.addEvents({
42774          /**
42775          * @event editorinit
42776          * Fired when the editor is initialized - you can add extra handlers here..
42777          * @param {FCKeditor} this
42778          * @param {Object} the FCK object.
42779          */
42780         editorinit : true
42781     });
42782     
42783     
42784 };
42785 Roo.form.FCKeditor.editors = { };
42786 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42787 {
42788     //defaultAutoCreate : {
42789     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42790     //},
42791     // private
42792     /**
42793      * @cfg {Object} fck options - see fck manual for details.
42794      */
42795     fckconfig : false,
42796     
42797     /**
42798      * @cfg {Object} fck toolbar set (Basic or Default)
42799      */
42800     toolbarSet : 'Basic',
42801     /**
42802      * @cfg {Object} fck BasePath
42803      */ 
42804     basePath : '/fckeditor/',
42805     
42806     
42807     frame : false,
42808     
42809     value : '',
42810     
42811    
42812     onRender : function(ct, position)
42813     {
42814         if(!this.el){
42815             this.defaultAutoCreate = {
42816                 tag: "textarea",
42817                 style:"width:300px;height:60px;",
42818                 autocomplete: "off"
42819             };
42820         }
42821         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42822         /*
42823         if(this.grow){
42824             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42825             if(this.preventScrollbars){
42826                 this.el.setStyle("overflow", "hidden");
42827             }
42828             this.el.setHeight(this.growMin);
42829         }
42830         */
42831         //console.log('onrender' + this.getId() );
42832         Roo.form.FCKeditor.editors[this.getId()] = this;
42833          
42834
42835         this.replaceTextarea() ;
42836         
42837     },
42838     
42839     getEditor : function() {
42840         return this.fckEditor;
42841     },
42842     /**
42843      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42844      * @param {Mixed} value The value to set
42845      */
42846     
42847     
42848     setValue : function(value)
42849     {
42850         //console.log('setValue: ' + value);
42851         
42852         if(typeof(value) == 'undefined') { // not sure why this is happending...
42853             return;
42854         }
42855         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42856         
42857         //if(!this.el || !this.getEditor()) {
42858         //    this.value = value;
42859             //this.setValue.defer(100,this,[value]);    
42860         //    return;
42861         //} 
42862         
42863         if(!this.getEditor()) {
42864             return;
42865         }
42866         
42867         this.getEditor().SetData(value);
42868         
42869         //
42870
42871     },
42872
42873     /**
42874      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42875      * @return {Mixed} value The field value
42876      */
42877     getValue : function()
42878     {
42879         
42880         if (this.frame && this.frame.dom.style.display == 'none') {
42881             return Roo.form.FCKeditor.superclass.getValue.call(this);
42882         }
42883         
42884         if(!this.el || !this.getEditor()) {
42885            
42886            // this.getValue.defer(100,this); 
42887             return this.value;
42888         }
42889        
42890         
42891         var value=this.getEditor().GetData();
42892         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42893         return Roo.form.FCKeditor.superclass.getValue.call(this);
42894         
42895
42896     },
42897
42898     /**
42899      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42900      * @return {Mixed} value The field value
42901      */
42902     getRawValue : function()
42903     {
42904         if (this.frame && this.frame.dom.style.display == 'none') {
42905             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42906         }
42907         
42908         if(!this.el || !this.getEditor()) {
42909             //this.getRawValue.defer(100,this); 
42910             return this.value;
42911             return;
42912         }
42913         
42914         
42915         
42916         var value=this.getEditor().GetData();
42917         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42918         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42919          
42920     },
42921     
42922     setSize : function(w,h) {
42923         
42924         
42925         
42926         //if (this.frame && this.frame.dom.style.display == 'none') {
42927         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42928         //    return;
42929         //}
42930         //if(!this.el || !this.getEditor()) {
42931         //    this.setSize.defer(100,this, [w,h]); 
42932         //    return;
42933         //}
42934         
42935         
42936         
42937         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42938         
42939         this.frame.dom.setAttribute('width', w);
42940         this.frame.dom.setAttribute('height', h);
42941         this.frame.setSize(w,h);
42942         
42943     },
42944     
42945     toggleSourceEdit : function(value) {
42946         
42947       
42948          
42949         this.el.dom.style.display = value ? '' : 'none';
42950         this.frame.dom.style.display = value ?  'none' : '';
42951         
42952     },
42953     
42954     
42955     focus: function(tag)
42956     {
42957         if (this.frame.dom.style.display == 'none') {
42958             return Roo.form.FCKeditor.superclass.focus.call(this);
42959         }
42960         if(!this.el || !this.getEditor()) {
42961             this.focus.defer(100,this, [tag]); 
42962             return;
42963         }
42964         
42965         
42966         
42967         
42968         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42969         this.getEditor().Focus();
42970         if (tgs.length) {
42971             if (!this.getEditor().Selection.GetSelection()) {
42972                 this.focus.defer(100,this, [tag]); 
42973                 return;
42974             }
42975             
42976             
42977             var r = this.getEditor().EditorDocument.createRange();
42978             r.setStart(tgs[0],0);
42979             r.setEnd(tgs[0],0);
42980             this.getEditor().Selection.GetSelection().removeAllRanges();
42981             this.getEditor().Selection.GetSelection().addRange(r);
42982             this.getEditor().Focus();
42983         }
42984         
42985     },
42986     
42987     
42988     
42989     replaceTextarea : function()
42990     {
42991         if ( document.getElementById( this.getId() + '___Frame' ) )
42992             return ;
42993         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42994         //{
42995             // We must check the elements firstly using the Id and then the name.
42996         var oTextarea = document.getElementById( this.getId() );
42997         
42998         var colElementsByName = document.getElementsByName( this.getId() ) ;
42999          
43000         oTextarea.style.display = 'none' ;
43001
43002         if ( oTextarea.tabIndex ) {            
43003             this.TabIndex = oTextarea.tabIndex ;
43004         }
43005         
43006         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43007         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43008         this.frame = Roo.get(this.getId() + '___Frame')
43009     },
43010     
43011     _getConfigHtml : function()
43012     {
43013         var sConfig = '' ;
43014
43015         for ( var o in this.fckconfig ) {
43016             sConfig += sConfig.length > 0  ? '&amp;' : '';
43017             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43018         }
43019
43020         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43021     },
43022     
43023     
43024     _getIFrameHtml : function()
43025     {
43026         var sFile = 'fckeditor.html' ;
43027         /* no idea what this is about..
43028         try
43029         {
43030             if ( (/fcksource=true/i).test( window.top.location.search ) )
43031                 sFile = 'fckeditor.original.html' ;
43032         }
43033         catch (e) { 
43034         */
43035
43036         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43037         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43038         
43039         
43040         var html = '<iframe id="' + this.getId() +
43041             '___Frame" src="' + sLink +
43042             '" width="' + this.width +
43043             '" height="' + this.height + '"' +
43044             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43045             ' frameborder="0" scrolling="no"></iframe>' ;
43046
43047         return html ;
43048     },
43049     
43050     _insertHtmlBefore : function( html, element )
43051     {
43052         if ( element.insertAdjacentHTML )       {
43053             // IE
43054             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43055         } else { // Gecko
43056             var oRange = document.createRange() ;
43057             oRange.setStartBefore( element ) ;
43058             var oFragment = oRange.createContextualFragment( html );
43059             element.parentNode.insertBefore( oFragment, element ) ;
43060         }
43061     }
43062     
43063     
43064   
43065     
43066     
43067     
43068     
43069
43070 });
43071
43072 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43073
43074 function FCKeditor_OnComplete(editorInstance){
43075     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43076     f.fckEditor = editorInstance;
43077     //console.log("loaded");
43078     f.fireEvent('editorinit', f, editorInstance);
43079
43080   
43081
43082  
43083
43084
43085
43086
43087
43088
43089
43090
43091
43092
43093
43094
43095
43096
43097
43098 //<script type="text/javascript">
43099 /**
43100  * @class Roo.form.GridField
43101  * @extends Roo.form.Field
43102  * Embed a grid (or editable grid into a form)
43103  * STATUS ALPHA
43104  * 
43105  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43106  * it needs 
43107  * xgrid.store = Roo.data.Store
43108  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43109  * xgrid.store.reader = Roo.data.JsonReader 
43110  * 
43111  * 
43112  * @constructor
43113  * Creates a new GridField
43114  * @param {Object} config Configuration options
43115  */
43116 Roo.form.GridField = function(config){
43117     Roo.form.GridField.superclass.constructor.call(this, config);
43118      
43119 };
43120
43121 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43122     /**
43123      * @cfg {Number} width  - used to restrict width of grid..
43124      */
43125     width : 100,
43126     /**
43127      * @cfg {Number} height - used to restrict height of grid..
43128      */
43129     height : 50,
43130      /**
43131      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43132          * 
43133          *}
43134      */
43135     xgrid : false, 
43136     /**
43137      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43138      * {tag: "input", type: "checkbox", autocomplete: "off"})
43139      */
43140    // defaultAutoCreate : { tag: 'div' },
43141     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43142     /**
43143      * @cfg {String} addTitle Text to include for adding a title.
43144      */
43145     addTitle : false,
43146     //
43147     onResize : function(){
43148         Roo.form.Field.superclass.onResize.apply(this, arguments);
43149     },
43150
43151     initEvents : function(){
43152         // Roo.form.Checkbox.superclass.initEvents.call(this);
43153         // has no events...
43154        
43155     },
43156
43157
43158     getResizeEl : function(){
43159         return this.wrap;
43160     },
43161
43162     getPositionEl : function(){
43163         return this.wrap;
43164     },
43165
43166     // private
43167     onRender : function(ct, position){
43168         
43169         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43170         var style = this.style;
43171         delete this.style;
43172         
43173         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43174         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43175         this.viewEl = this.wrap.createChild({ tag: 'div' });
43176         if (style) {
43177             this.viewEl.applyStyles(style);
43178         }
43179         if (this.width) {
43180             this.viewEl.setWidth(this.width);
43181         }
43182         if (this.height) {
43183             this.viewEl.setHeight(this.height);
43184         }
43185         //if(this.inputValue !== undefined){
43186         //this.setValue(this.value);
43187         
43188         
43189         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43190         
43191         
43192         this.grid.render();
43193         this.grid.getDataSource().on('remove', this.refreshValue, this);
43194         this.grid.getDataSource().on('update', this.refreshValue, this);
43195         this.grid.on('afteredit', this.refreshValue, this);
43196  
43197     },
43198      
43199     
43200     /**
43201      * Sets the value of the item. 
43202      * @param {String} either an object  or a string..
43203      */
43204     setValue : function(v){
43205         //this.value = v;
43206         v = v || []; // empty set..
43207         // this does not seem smart - it really only affects memoryproxy grids..
43208         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43209             var ds = this.grid.getDataSource();
43210             // assumes a json reader..
43211             var data = {}
43212             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43213             ds.loadData( data);
43214         }
43215         // clear selection so it does not get stale.
43216         if (this.grid.sm) { 
43217             this.grid.sm.clearSelections();
43218         }
43219         
43220         Roo.form.GridField.superclass.setValue.call(this, v);
43221         this.refreshValue();
43222         // should load data in the grid really....
43223     },
43224     
43225     // private
43226     refreshValue: function() {
43227          var val = [];
43228         this.grid.getDataSource().each(function(r) {
43229             val.push(r.data);
43230         });
43231         this.el.dom.value = Roo.encode(val);
43232     }
43233     
43234      
43235     
43236     
43237 });/*
43238  * Based on:
43239  * Ext JS Library 1.1.1
43240  * Copyright(c) 2006-2007, Ext JS, LLC.
43241  *
43242  * Originally Released Under LGPL - original licence link has changed is not relivant.
43243  *
43244  * Fork - LGPL
43245  * <script type="text/javascript">
43246  */
43247 /**
43248  * @class Roo.form.DisplayField
43249  * @extends Roo.form.Field
43250  * A generic Field to display non-editable data.
43251  * @constructor
43252  * Creates a new Display Field item.
43253  * @param {Object} config Configuration options
43254  */
43255 Roo.form.DisplayField = function(config){
43256     Roo.form.DisplayField.superclass.constructor.call(this, config);
43257     
43258 };
43259
43260 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43261     inputType:      'hidden',
43262     allowBlank:     true,
43263     readOnly:         true,
43264     
43265  
43266     /**
43267      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43268      */
43269     focusClass : undefined,
43270     /**
43271      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43272      */
43273     fieldClass: 'x-form-field',
43274     
43275      /**
43276      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43277      */
43278     valueRenderer: undefined,
43279     
43280     width: 100,
43281     /**
43282      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43283      * {tag: "input", type: "checkbox", autocomplete: "off"})
43284      */
43285      
43286  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43287
43288     onResize : function(){
43289         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43290         
43291     },
43292
43293     initEvents : function(){
43294         // Roo.form.Checkbox.superclass.initEvents.call(this);
43295         // has no events...
43296        
43297     },
43298
43299
43300     getResizeEl : function(){
43301         return this.wrap;
43302     },
43303
43304     getPositionEl : function(){
43305         return this.wrap;
43306     },
43307
43308     // private
43309     onRender : function(ct, position){
43310         
43311         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43312         //if(this.inputValue !== undefined){
43313         this.wrap = this.el.wrap();
43314         
43315         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43316         
43317         if (this.bodyStyle) {
43318             this.viewEl.applyStyles(this.bodyStyle);
43319         }
43320         //this.viewEl.setStyle('padding', '2px');
43321         
43322         this.setValue(this.value);
43323         
43324     },
43325 /*
43326     // private
43327     initValue : Roo.emptyFn,
43328
43329   */
43330
43331         // private
43332     onClick : function(){
43333         
43334     },
43335
43336     /**
43337      * Sets the checked state of the checkbox.
43338      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43339      */
43340     setValue : function(v){
43341         this.value = v;
43342         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43343         // this might be called before we have a dom element..
43344         if (!this.viewEl) {
43345             return;
43346         }
43347         this.viewEl.dom.innerHTML = html;
43348         Roo.form.DisplayField.superclass.setValue.call(this, v);
43349
43350     }
43351 });/*
43352  * 
43353  * Licence- LGPL
43354  * 
43355  */
43356
43357 /**
43358  * @class Roo.form.DayPicker
43359  * @extends Roo.form.Field
43360  * A Day picker show [M] [T] [W] ....
43361  * @constructor
43362  * Creates a new Day Picker
43363  * @param {Object} config Configuration options
43364  */
43365 Roo.form.DayPicker= function(config){
43366     Roo.form.DayPicker.superclass.constructor.call(this, config);
43367      
43368 };
43369
43370 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43371     /**
43372      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43373      */
43374     focusClass : undefined,
43375     /**
43376      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43377      */
43378     fieldClass: "x-form-field",
43379    
43380     /**
43381      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43382      * {tag: "input", type: "checkbox", autocomplete: "off"})
43383      */
43384     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43385     
43386    
43387     actionMode : 'viewEl', 
43388     //
43389     // private
43390  
43391     inputType : 'hidden',
43392     
43393      
43394     inputElement: false, // real input element?
43395     basedOn: false, // ????
43396     
43397     isFormField: true, // not sure where this is needed!!!!
43398
43399     onResize : function(){
43400         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43401         if(!this.boxLabel){
43402             this.el.alignTo(this.wrap, 'c-c');
43403         }
43404     },
43405
43406     initEvents : function(){
43407         Roo.form.Checkbox.superclass.initEvents.call(this);
43408         this.el.on("click", this.onClick,  this);
43409         this.el.on("change", this.onClick,  this);
43410     },
43411
43412
43413     getResizeEl : function(){
43414         return this.wrap;
43415     },
43416
43417     getPositionEl : function(){
43418         return this.wrap;
43419     },
43420
43421     
43422     // private
43423     onRender : function(ct, position){
43424         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43425        
43426         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43427         
43428         var r1 = '<table><tr>';
43429         var r2 = '<tr class="x-form-daypick-icons">';
43430         for (var i=0; i < 7; i++) {
43431             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43432             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43433         }
43434         
43435         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43436         viewEl.select('img').on('click', this.onClick, this);
43437         this.viewEl = viewEl;   
43438         
43439         
43440         // this will not work on Chrome!!!
43441         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43442         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43443         
43444         
43445           
43446
43447     },
43448
43449     // private
43450     initValue : Roo.emptyFn,
43451
43452     /**
43453      * Returns the checked state of the checkbox.
43454      * @return {Boolean} True if checked, else false
43455      */
43456     getValue : function(){
43457         return this.el.dom.value;
43458         
43459     },
43460
43461         // private
43462     onClick : function(e){ 
43463         //this.setChecked(!this.checked);
43464         Roo.get(e.target).toggleClass('x-menu-item-checked');
43465         this.refreshValue();
43466         //if(this.el.dom.checked != this.checked){
43467         //    this.setValue(this.el.dom.checked);
43468        // }
43469     },
43470     
43471     // private
43472     refreshValue : function()
43473     {
43474         var val = '';
43475         this.viewEl.select('img',true).each(function(e,i,n)  {
43476             val += e.is(".x-menu-item-checked") ? String(n) : '';
43477         });
43478         this.setValue(val, true);
43479     },
43480
43481     /**
43482      * Sets the checked state of the checkbox.
43483      * On is always based on a string comparison between inputValue and the param.
43484      * @param {Boolean/String} value - the value to set 
43485      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43486      */
43487     setValue : function(v,suppressEvent){
43488         if (!this.el.dom) {
43489             return;
43490         }
43491         var old = this.el.dom.value ;
43492         this.el.dom.value = v;
43493         if (suppressEvent) {
43494             return ;
43495         }
43496          
43497         // update display..
43498         this.viewEl.select('img',true).each(function(e,i,n)  {
43499             
43500             var on = e.is(".x-menu-item-checked");
43501             var newv = v.indexOf(String(n)) > -1;
43502             if (on != newv) {
43503                 e.toggleClass('x-menu-item-checked');
43504             }
43505             
43506         });
43507         
43508         
43509         this.fireEvent('change', this, v, old);
43510         
43511         
43512     },
43513    
43514     // handle setting of hidden value by some other method!!?!?
43515     setFromHidden: function()
43516     {
43517         if(!this.el){
43518             return;
43519         }
43520         //console.log("SET FROM HIDDEN");
43521         //alert('setFrom hidden');
43522         this.setValue(this.el.dom.value);
43523     },
43524     
43525     onDestroy : function()
43526     {
43527         if(this.viewEl){
43528             Roo.get(this.viewEl).remove();
43529         }
43530          
43531         Roo.form.DayPicker.superclass.onDestroy.call(this);
43532     }
43533
43534 });/*
43535  * RooJS Library 1.1.1
43536  * Copyright(c) 2008-2011  Alan Knowles
43537  *
43538  * License - LGPL
43539  */
43540  
43541
43542 /**
43543  * @class Roo.form.ComboCheck
43544  * @extends Roo.form.ComboBox
43545  * A combobox for multiple select items.
43546  *
43547  * FIXME - could do with a reset button..
43548  * 
43549  * @constructor
43550  * Create a new ComboCheck
43551  * @param {Object} config Configuration options
43552  */
43553 Roo.form.ComboCheck = function(config){
43554     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43555     // should verify some data...
43556     // like
43557     // hiddenName = required..
43558     // displayField = required
43559     // valudField == required
43560     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43561     var _t = this;
43562     Roo.each(req, function(e) {
43563         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43564             throw "Roo.form.ComboCheck : missing value for: " + e;
43565         }
43566     });
43567     
43568     
43569 };
43570
43571 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43572      
43573      
43574     editable : false,
43575      
43576     selectedClass: 'x-menu-item-checked', 
43577     
43578     // private
43579     onRender : function(ct, position){
43580         var _t = this;
43581         
43582         
43583         
43584         if(!this.tpl){
43585             var cls = 'x-combo-list';
43586
43587             
43588             this.tpl =  new Roo.Template({
43589                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43590                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43591                    '<span>{' + this.displayField + '}</span>' +
43592                     '</div>' 
43593                 
43594             });
43595         }
43596  
43597         
43598         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43599         this.view.singleSelect = false;
43600         this.view.multiSelect = true;
43601         this.view.toggleSelect = true;
43602         this.pageTb.add(new Roo.Toolbar.Fill(), {
43603             
43604             text: 'Done',
43605             handler: function()
43606             {
43607                 _t.collapse();
43608             }
43609         });
43610     },
43611     
43612     onViewOver : function(e, t){
43613         // do nothing...
43614         return;
43615         
43616     },
43617     
43618     onViewClick : function(doFocus,index){
43619         return;
43620         
43621     },
43622     select: function () {
43623         //Roo.log("SELECT CALLED");
43624     },
43625      
43626     selectByValue : function(xv, scrollIntoView){
43627         var ar = this.getValueArray();
43628         var sels = [];
43629         
43630         Roo.each(ar, function(v) {
43631             if(v === undefined || v === null){
43632                 return;
43633             }
43634             var r = this.findRecord(this.valueField, v);
43635             if(r){
43636                 sels.push(this.store.indexOf(r))
43637                 
43638             }
43639         },this);
43640         this.view.select(sels);
43641         return false;
43642     },
43643     
43644     
43645     
43646     onSelect : function(record, index){
43647        // Roo.log("onselect Called");
43648        // this is only called by the clear button now..
43649         this.view.clearSelections();
43650         this.setValue('[]');
43651         if (this.value != this.valueBefore) {
43652             this.fireEvent('change', this, this.value, this.valueBefore);
43653         }
43654     },
43655     getValueArray : function()
43656     {
43657         var ar = [] ;
43658         
43659         try {
43660             Roo.log(this.value);
43661             var ar = Roo.decode(this.value);
43662             return  ar instanceof Array ? ar : []; //?? valid?
43663             
43664         } catch(e) {
43665             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43666             return [];
43667         }
43668          
43669     },
43670     expand : function ()
43671     {
43672         Roo.form.ComboCheck.superclass.expand.call(this);
43673         this.valueBefore = this.value;
43674         
43675
43676     },
43677     
43678     collapse : function(){
43679         Roo.form.ComboCheck.superclass.collapse.call(this);
43680         var sl = this.view.getSelectedIndexes();
43681         var st = this.store;
43682         var nv = [];
43683         var tv = [];
43684         var r;
43685         Roo.each(sl, function(i) {
43686             r = st.getAt(i);
43687             nv.push(r.get(this.valueField));
43688         },this);
43689         this.setValue(Roo.encode(nv));
43690         if (this.value != this.valueBefore) {
43691
43692             this.fireEvent('change', this, this.value, this.valueBefore);
43693         }
43694         
43695     },
43696     
43697     setValue : function(v){
43698         // Roo.log(v);
43699         this.value = v;
43700         
43701         var vals = this.getValueArray();
43702         var tv = [];
43703         Roo.each(vals, function(k) {
43704             var r = this.findRecord(this.valueField, k);
43705             if(r){
43706                 tv.push(r.data[this.displayField]);
43707             }else if(this.valueNotFoundText !== undefined){
43708                 tv.push( this.valueNotFoundText );
43709             }
43710         },this);
43711        // Roo.log(tv);
43712         
43713         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43714         this.hiddenField.value = v;
43715         this.value = v;
43716     }
43717     
43718 });//<script type="text/javasscript">
43719  
43720
43721 /**
43722  * @class Roo.DDView
43723  * A DnD enabled version of Roo.View.
43724  * @param {Element/String} container The Element in which to create the View.
43725  * @param {String} tpl The template string used to create the markup for each element of the View
43726  * @param {Object} config The configuration properties. These include all the config options of
43727  * {@link Roo.View} plus some specific to this class.<br>
43728  * <p>
43729  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43730  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43731  * <p>
43732  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43733 .x-view-drag-insert-above {
43734         border-top:1px dotted #3366cc;
43735 }
43736 .x-view-drag-insert-below {
43737         border-bottom:1px dotted #3366cc;
43738 }
43739 </code></pre>
43740  * 
43741  */
43742  
43743 Roo.DDView = function(container, tpl, config) {
43744     Roo.DDView.superclass.constructor.apply(this, arguments);
43745     this.getEl().setStyle("outline", "0px none");
43746     this.getEl().unselectable();
43747     if (this.dragGroup) {
43748                 this.setDraggable(this.dragGroup.split(","));
43749     }
43750     if (this.dropGroup) {
43751                 this.setDroppable(this.dropGroup.split(","));
43752     }
43753     if (this.deletable) {
43754         this.setDeletable();
43755     }
43756     this.isDirtyFlag = false;
43757         this.addEvents({
43758                 "drop" : true
43759         });
43760 };
43761
43762 Roo.extend(Roo.DDView, Roo.View, {
43763 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43764 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43765 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43766 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43767
43768         isFormField: true,
43769
43770         reset: Roo.emptyFn,
43771         
43772         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43773
43774         validate: function() {
43775                 return true;
43776         },
43777         
43778         destroy: function() {
43779                 this.purgeListeners();
43780                 this.getEl.removeAllListeners();
43781                 this.getEl().remove();
43782                 if (this.dragZone) {
43783                         if (this.dragZone.destroy) {
43784                                 this.dragZone.destroy();
43785                         }
43786                 }
43787                 if (this.dropZone) {
43788                         if (this.dropZone.destroy) {
43789                                 this.dropZone.destroy();
43790                         }
43791                 }
43792         },
43793
43794 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43795         getName: function() {
43796                 return this.name;
43797         },
43798
43799 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43800         setValue: function(v) {
43801                 if (!this.store) {
43802                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43803                 }
43804                 var data = {};
43805                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43806                 this.store.proxy = new Roo.data.MemoryProxy(data);
43807                 this.store.load();
43808         },
43809
43810 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43811         getValue: function() {
43812                 var result = '(';
43813                 this.store.each(function(rec) {
43814                         result += rec.id + ',';
43815                 });
43816                 return result.substr(0, result.length - 1) + ')';
43817         },
43818         
43819         getIds: function() {
43820                 var i = 0, result = new Array(this.store.getCount());
43821                 this.store.each(function(rec) {
43822                         result[i++] = rec.id;
43823                 });
43824                 return result;
43825         },
43826         
43827         isDirty: function() {
43828                 return this.isDirtyFlag;
43829         },
43830
43831 /**
43832  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43833  *      whole Element becomes the target, and this causes the drop gesture to append.
43834  */
43835     getTargetFromEvent : function(e) {
43836                 var target = e.getTarget();
43837                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43838                 target = target.parentNode;
43839                 }
43840                 if (!target) {
43841                         target = this.el.dom.lastChild || this.el.dom;
43842                 }
43843                 return target;
43844     },
43845
43846 /**
43847  *      Create the drag data which consists of an object which has the property "ddel" as
43848  *      the drag proxy element. 
43849  */
43850     getDragData : function(e) {
43851         var target = this.findItemFromChild(e.getTarget());
43852                 if(target) {
43853                         this.handleSelection(e);
43854                         var selNodes = this.getSelectedNodes();
43855             var dragData = {
43856                 source: this,
43857                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43858                 nodes: selNodes,
43859                 records: []
43860                         };
43861                         var selectedIndices = this.getSelectedIndexes();
43862                         for (var i = 0; i < selectedIndices.length; i++) {
43863                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43864                         }
43865                         if (selNodes.length == 1) {
43866                                 dragData.ddel = target.cloneNode(true); // the div element
43867                         } else {
43868                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43869                                 div.className = 'multi-proxy';
43870                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43871                                         div.appendChild(selNodes[i].cloneNode(true));
43872                                 }
43873                                 dragData.ddel = div;
43874                         }
43875             //console.log(dragData)
43876             //console.log(dragData.ddel.innerHTML)
43877                         return dragData;
43878                 }
43879         //console.log('nodragData')
43880                 return false;
43881     },
43882     
43883 /**     Specify to which ddGroup items in this DDView may be dragged. */
43884     setDraggable: function(ddGroup) {
43885         if (ddGroup instanceof Array) {
43886                 Roo.each(ddGroup, this.setDraggable, this);
43887                 return;
43888         }
43889         if (this.dragZone) {
43890                 this.dragZone.addToGroup(ddGroup);
43891         } else {
43892                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43893                                 containerScroll: true,
43894                                 ddGroup: ddGroup 
43895
43896                         });
43897 //                      Draggability implies selection. DragZone's mousedown selects the element.
43898                         if (!this.multiSelect) { this.singleSelect = true; }
43899
43900 //                      Wire the DragZone's handlers up to methods in *this*
43901                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43902                 }
43903     },
43904
43905 /**     Specify from which ddGroup this DDView accepts drops. */
43906     setDroppable: function(ddGroup) {
43907         if (ddGroup instanceof Array) {
43908                 Roo.each(ddGroup, this.setDroppable, this);
43909                 return;
43910         }
43911         if (this.dropZone) {
43912                 this.dropZone.addToGroup(ddGroup);
43913         } else {
43914                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43915                                 containerScroll: true,
43916                                 ddGroup: ddGroup
43917                         });
43918
43919 //                      Wire the DropZone's handlers up to methods in *this*
43920                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43921                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43922                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43923                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43924                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43925                 }
43926     },
43927
43928 /**     Decide whether to drop above or below a View node. */
43929     getDropPoint : function(e, n, dd){
43930         if (n == this.el.dom) { return "above"; }
43931                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43932                 var c = t + (b - t) / 2;
43933                 var y = Roo.lib.Event.getPageY(e);
43934                 if(y <= c) {
43935                         return "above";
43936                 }else{
43937                         return "below";
43938                 }
43939     },
43940
43941     onNodeEnter : function(n, dd, e, data){
43942                 return false;
43943     },
43944     
43945     onNodeOver : function(n, dd, e, data){
43946                 var pt = this.getDropPoint(e, n, dd);
43947                 // set the insert point style on the target node
43948                 var dragElClass = this.dropNotAllowed;
43949                 if (pt) {
43950                         var targetElClass;
43951                         if (pt == "above"){
43952                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43953                                 targetElClass = "x-view-drag-insert-above";
43954                         } else {
43955                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43956                                 targetElClass = "x-view-drag-insert-below";
43957                         }
43958                         if (this.lastInsertClass != targetElClass){
43959                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43960                                 this.lastInsertClass = targetElClass;
43961                         }
43962                 }
43963                 return dragElClass;
43964         },
43965
43966     onNodeOut : function(n, dd, e, data){
43967                 this.removeDropIndicators(n);
43968     },
43969
43970     onNodeDrop : function(n, dd, e, data){
43971         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43972                 return false;
43973         }
43974         var pt = this.getDropPoint(e, n, dd);
43975                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43976                 if (pt == "below") { insertAt++; }
43977                 for (var i = 0; i < data.records.length; i++) {
43978                         var r = data.records[i];
43979                         var dup = this.store.getById(r.id);
43980                         if (dup && (dd != this.dragZone)) {
43981                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43982                         } else {
43983                                 if (data.copy) {
43984                                         this.store.insert(insertAt++, r.copy());
43985                                 } else {
43986                                         data.source.isDirtyFlag = true;
43987                                         r.store.remove(r);
43988                                         this.store.insert(insertAt++, r);
43989                                 }
43990                                 this.isDirtyFlag = true;
43991                         }
43992                 }
43993                 this.dragZone.cachedTarget = null;
43994                 return true;
43995     },
43996
43997     removeDropIndicators : function(n){
43998                 if(n){
43999                         Roo.fly(n).removeClass([
44000                                 "x-view-drag-insert-above",
44001                                 "x-view-drag-insert-below"]);
44002                         this.lastInsertClass = "_noclass";
44003                 }
44004     },
44005
44006 /**
44007  *      Utility method. Add a delete option to the DDView's context menu.
44008  *      @param {String} imageUrl The URL of the "delete" icon image.
44009  */
44010         setDeletable: function(imageUrl) {
44011                 if (!this.singleSelect && !this.multiSelect) {
44012                         this.singleSelect = true;
44013                 }
44014                 var c = this.getContextMenu();
44015                 this.contextMenu.on("itemclick", function(item) {
44016                         switch (item.id) {
44017                                 case "delete":
44018                                         this.remove(this.getSelectedIndexes());
44019                                         break;
44020                         }
44021                 }, this);
44022                 this.contextMenu.add({
44023                         icon: imageUrl,
44024                         id: "delete",
44025                         text: 'Delete'
44026                 });
44027         },
44028         
44029 /**     Return the context menu for this DDView. */
44030         getContextMenu: function() {
44031                 if (!this.contextMenu) {
44032 //                      Create the View's context menu
44033                         this.contextMenu = new Roo.menu.Menu({
44034                                 id: this.id + "-contextmenu"
44035                         });
44036                         this.el.on("contextmenu", this.showContextMenu, this);
44037                 }
44038                 return this.contextMenu;
44039         },
44040         
44041         disableContextMenu: function() {
44042                 if (this.contextMenu) {
44043                         this.el.un("contextmenu", this.showContextMenu, this);
44044                 }
44045         },
44046
44047         showContextMenu: function(e, item) {
44048         item = this.findItemFromChild(e.getTarget());
44049                 if (item) {
44050                         e.stopEvent();
44051                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44052                         this.contextMenu.showAt(e.getXY());
44053             }
44054     },
44055
44056 /**
44057  *      Remove {@link Roo.data.Record}s at the specified indices.
44058  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44059  */
44060     remove: function(selectedIndices) {
44061                 selectedIndices = [].concat(selectedIndices);
44062                 for (var i = 0; i < selectedIndices.length; i++) {
44063                         var rec = this.store.getAt(selectedIndices[i]);
44064                         this.store.remove(rec);
44065                 }
44066     },
44067
44068 /**
44069  *      Double click fires the event, but also, if this is draggable, and there is only one other
44070  *      related DropZone, it transfers the selected node.
44071  */
44072     onDblClick : function(e){
44073         var item = this.findItemFromChild(e.getTarget());
44074         if(item){
44075             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44076                 return false;
44077             }
44078             if (this.dragGroup) {
44079                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44080                     while (targets.indexOf(this.dropZone) > -1) {
44081                             targets.remove(this.dropZone);
44082                                 }
44083                     if (targets.length == 1) {
44084                                         this.dragZone.cachedTarget = null;
44085                         var el = Roo.get(targets[0].getEl());
44086                         var box = el.getBox(true);
44087                         targets[0].onNodeDrop(el.dom, {
44088                                 target: el.dom,
44089                                 xy: [box.x, box.y + box.height - 1]
44090                         }, null, this.getDragData(e));
44091                     }
44092                 }
44093         }
44094     },
44095     
44096     handleSelection: function(e) {
44097                 this.dragZone.cachedTarget = null;
44098         var item = this.findItemFromChild(e.getTarget());
44099         if (!item) {
44100                 this.clearSelections(true);
44101                 return;
44102         }
44103                 if (item && (this.multiSelect || this.singleSelect)){
44104                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44105                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44106                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44107                                 this.unselect(item);
44108                         } else {
44109                                 this.select(item, this.multiSelect && e.ctrlKey);
44110                                 this.lastSelection = item;
44111                         }
44112                 }
44113     },
44114
44115     onItemClick : function(item, index, e){
44116                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44117                         return false;
44118                 }
44119                 return true;
44120     },
44121
44122     unselect : function(nodeInfo, suppressEvent){
44123                 var node = this.getNode(nodeInfo);
44124                 if(node && this.isSelected(node)){
44125                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44126                                 Roo.fly(node).removeClass(this.selectedClass);
44127                                 this.selections.remove(node);
44128                                 if(!suppressEvent){
44129                                         this.fireEvent("selectionchange", this, this.selections);
44130                                 }
44131                         }
44132                 }
44133     }
44134 });
44135 /*
44136  * Based on:
44137  * Ext JS Library 1.1.1
44138  * Copyright(c) 2006-2007, Ext JS, LLC.
44139  *
44140  * Originally Released Under LGPL - original licence link has changed is not relivant.
44141  *
44142  * Fork - LGPL
44143  * <script type="text/javascript">
44144  */
44145  
44146 /**
44147  * @class Roo.LayoutManager
44148  * @extends Roo.util.Observable
44149  * Base class for layout managers.
44150  */
44151 Roo.LayoutManager = function(container, config){
44152     Roo.LayoutManager.superclass.constructor.call(this);
44153     this.el = Roo.get(container);
44154     // ie scrollbar fix
44155     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44156         document.body.scroll = "no";
44157     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44158         this.el.position('relative');
44159     }
44160     this.id = this.el.id;
44161     this.el.addClass("x-layout-container");
44162     /** false to disable window resize monitoring @type Boolean */
44163     this.monitorWindowResize = true;
44164     this.regions = {};
44165     this.addEvents({
44166         /**
44167          * @event layout
44168          * Fires when a layout is performed. 
44169          * @param {Roo.LayoutManager} this
44170          */
44171         "layout" : true,
44172         /**
44173          * @event regionresized
44174          * Fires when the user resizes a region. 
44175          * @param {Roo.LayoutRegion} region The resized region
44176          * @param {Number} newSize The new size (width for east/west, height for north/south)
44177          */
44178         "regionresized" : true,
44179         /**
44180          * @event regioncollapsed
44181          * Fires when a region is collapsed. 
44182          * @param {Roo.LayoutRegion} region The collapsed region
44183          */
44184         "regioncollapsed" : true,
44185         /**
44186          * @event regionexpanded
44187          * Fires when a region is expanded.  
44188          * @param {Roo.LayoutRegion} region The expanded region
44189          */
44190         "regionexpanded" : true
44191     });
44192     this.updating = false;
44193     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44194 };
44195
44196 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44197     /**
44198      * Returns true if this layout is currently being updated
44199      * @return {Boolean}
44200      */
44201     isUpdating : function(){
44202         return this.updating; 
44203     },
44204     
44205     /**
44206      * Suspend the LayoutManager from doing auto-layouts while
44207      * making multiple add or remove calls
44208      */
44209     beginUpdate : function(){
44210         this.updating = true;    
44211     },
44212     
44213     /**
44214      * Restore auto-layouts and optionally disable the manager from performing a layout
44215      * @param {Boolean} noLayout true to disable a layout update 
44216      */
44217     endUpdate : function(noLayout){
44218         this.updating = false;
44219         if(!noLayout){
44220             this.layout();
44221         }    
44222     },
44223     
44224     layout: function(){
44225         
44226     },
44227     
44228     onRegionResized : function(region, newSize){
44229         this.fireEvent("regionresized", region, newSize);
44230         this.layout();
44231     },
44232     
44233     onRegionCollapsed : function(region){
44234         this.fireEvent("regioncollapsed", region);
44235     },
44236     
44237     onRegionExpanded : function(region){
44238         this.fireEvent("regionexpanded", region);
44239     },
44240         
44241     /**
44242      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44243      * performs box-model adjustments.
44244      * @return {Object} The size as an object {width: (the width), height: (the height)}
44245      */
44246     getViewSize : function(){
44247         var size;
44248         if(this.el.dom != document.body){
44249             size = this.el.getSize();
44250         }else{
44251             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44252         }
44253         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44254         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44255         return size;
44256     },
44257     
44258     /**
44259      * Returns the Element this layout is bound to.
44260      * @return {Roo.Element}
44261      */
44262     getEl : function(){
44263         return this.el;
44264     },
44265     
44266     /**
44267      * Returns the specified region.
44268      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44269      * @return {Roo.LayoutRegion}
44270      */
44271     getRegion : function(target){
44272         return this.regions[target.toLowerCase()];
44273     },
44274     
44275     onWindowResize : function(){
44276         if(this.monitorWindowResize){
44277             this.layout();
44278         }
44279     }
44280 });/*
44281  * Based on:
44282  * Ext JS Library 1.1.1
44283  * Copyright(c) 2006-2007, Ext JS, LLC.
44284  *
44285  * Originally Released Under LGPL - original licence link has changed is not relivant.
44286  *
44287  * Fork - LGPL
44288  * <script type="text/javascript">
44289  */
44290 /**
44291  * @class Roo.BorderLayout
44292  * @extends Roo.LayoutManager
44293  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44294  * please see: <br><br>
44295  * <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>
44296  * <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>
44297  * Example:
44298  <pre><code>
44299  var layout = new Roo.BorderLayout(document.body, {
44300     north: {
44301         initialSize: 25,
44302         titlebar: false
44303     },
44304     west: {
44305         split:true,
44306         initialSize: 200,
44307         minSize: 175,
44308         maxSize: 400,
44309         titlebar: true,
44310         collapsible: true
44311     },
44312     east: {
44313         split:true,
44314         initialSize: 202,
44315         minSize: 175,
44316         maxSize: 400,
44317         titlebar: true,
44318         collapsible: true
44319     },
44320     south: {
44321         split:true,
44322         initialSize: 100,
44323         minSize: 100,
44324         maxSize: 200,
44325         titlebar: true,
44326         collapsible: true
44327     },
44328     center: {
44329         titlebar: true,
44330         autoScroll:true,
44331         resizeTabs: true,
44332         minTabWidth: 50,
44333         preferredTabWidth: 150
44334     }
44335 });
44336
44337 // shorthand
44338 var CP = Roo.ContentPanel;
44339
44340 layout.beginUpdate();
44341 layout.add("north", new CP("north", "North"));
44342 layout.add("south", new CP("south", {title: "South", closable: true}));
44343 layout.add("west", new CP("west", {title: "West"}));
44344 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44345 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44346 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44347 layout.getRegion("center").showPanel("center1");
44348 layout.endUpdate();
44349 </code></pre>
44350
44351 <b>The container the layout is rendered into can be either the body element or any other element.
44352 If it is not the body element, the container needs to either be an absolute positioned element,
44353 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44354 the container size if it is not the body element.</b>
44355
44356 * @constructor
44357 * Create a new BorderLayout
44358 * @param {String/HTMLElement/Element} container The container this layout is bound to
44359 * @param {Object} config Configuration options
44360  */
44361 Roo.BorderLayout = function(container, config){
44362     config = config || {};
44363     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44364     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44365     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44366         var target = this.factory.validRegions[i];
44367         if(config[target]){
44368             this.addRegion(target, config[target]);
44369         }
44370     }
44371 };
44372
44373 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44374     /**
44375      * Creates and adds a new region if it doesn't already exist.
44376      * @param {String} target The target region key (north, south, east, west or center).
44377      * @param {Object} config The regions config object
44378      * @return {BorderLayoutRegion} The new region
44379      */
44380     addRegion : function(target, config){
44381         if(!this.regions[target]){
44382             var r = this.factory.create(target, this, config);
44383             this.bindRegion(target, r);
44384         }
44385         return this.regions[target];
44386     },
44387
44388     // private (kinda)
44389     bindRegion : function(name, r){
44390         this.regions[name] = r;
44391         r.on("visibilitychange", this.layout, this);
44392         r.on("paneladded", this.layout, this);
44393         r.on("panelremoved", this.layout, this);
44394         r.on("invalidated", this.layout, this);
44395         r.on("resized", this.onRegionResized, this);
44396         r.on("collapsed", this.onRegionCollapsed, this);
44397         r.on("expanded", this.onRegionExpanded, this);
44398     },
44399
44400     /**
44401      * Performs a layout update.
44402      */
44403     layout : function(){
44404         if(this.updating) return;
44405         var size = this.getViewSize();
44406         var w = size.width;
44407         var h = size.height;
44408         var centerW = w;
44409         var centerH = h;
44410         var centerY = 0;
44411         var centerX = 0;
44412         //var x = 0, y = 0;
44413
44414         var rs = this.regions;
44415         var north = rs["north"];
44416         var south = rs["south"]; 
44417         var west = rs["west"];
44418         var east = rs["east"];
44419         var center = rs["center"];
44420         //if(this.hideOnLayout){ // not supported anymore
44421             //c.el.setStyle("display", "none");
44422         //}
44423         if(north && north.isVisible()){
44424             var b = north.getBox();
44425             var m = north.getMargins();
44426             b.width = w - (m.left+m.right);
44427             b.x = m.left;
44428             b.y = m.top;
44429             centerY = b.height + b.y + m.bottom;
44430             centerH -= centerY;
44431             north.updateBox(this.safeBox(b));
44432         }
44433         if(south && south.isVisible()){
44434             var b = south.getBox();
44435             var m = south.getMargins();
44436             b.width = w - (m.left+m.right);
44437             b.x = m.left;
44438             var totalHeight = (b.height + m.top + m.bottom);
44439             b.y = h - totalHeight + m.top;
44440             centerH -= totalHeight;
44441             south.updateBox(this.safeBox(b));
44442         }
44443         if(west && west.isVisible()){
44444             var b = west.getBox();
44445             var m = west.getMargins();
44446             b.height = centerH - (m.top+m.bottom);
44447             b.x = m.left;
44448             b.y = centerY + m.top;
44449             var totalWidth = (b.width + m.left + m.right);
44450             centerX += totalWidth;
44451             centerW -= totalWidth;
44452             west.updateBox(this.safeBox(b));
44453         }
44454         if(east && east.isVisible()){
44455             var b = east.getBox();
44456             var m = east.getMargins();
44457             b.height = centerH - (m.top+m.bottom);
44458             var totalWidth = (b.width + m.left + m.right);
44459             b.x = w - totalWidth + m.left;
44460             b.y = centerY + m.top;
44461             centerW -= totalWidth;
44462             east.updateBox(this.safeBox(b));
44463         }
44464         if(center){
44465             var m = center.getMargins();
44466             var centerBox = {
44467                 x: centerX + m.left,
44468                 y: centerY + m.top,
44469                 width: centerW - (m.left+m.right),
44470                 height: centerH - (m.top+m.bottom)
44471             };
44472             //if(this.hideOnLayout){
44473                 //center.el.setStyle("display", "block");
44474             //}
44475             center.updateBox(this.safeBox(centerBox));
44476         }
44477         this.el.repaint();
44478         this.fireEvent("layout", this);
44479     },
44480
44481     // private
44482     safeBox : function(box){
44483         box.width = Math.max(0, box.width);
44484         box.height = Math.max(0, box.height);
44485         return box;
44486     },
44487
44488     /**
44489      * Adds a ContentPanel (or subclass) to this layout.
44490      * @param {String} target The target region key (north, south, east, west or center).
44491      * @param {Roo.ContentPanel} panel The panel to add
44492      * @return {Roo.ContentPanel} The added panel
44493      */
44494     add : function(target, panel){
44495          
44496         target = target.toLowerCase();
44497         return this.regions[target].add(panel);
44498     },
44499
44500     /**
44501      * Remove a ContentPanel (or subclass) to this layout.
44502      * @param {String} target The target region key (north, south, east, west or center).
44503      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44504      * @return {Roo.ContentPanel} The removed panel
44505      */
44506     remove : function(target, panel){
44507         target = target.toLowerCase();
44508         return this.regions[target].remove(panel);
44509     },
44510
44511     /**
44512      * Searches all regions for a panel with the specified id
44513      * @param {String} panelId
44514      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44515      */
44516     findPanel : function(panelId){
44517         var rs = this.regions;
44518         for(var target in rs){
44519             if(typeof rs[target] != "function"){
44520                 var p = rs[target].getPanel(panelId);
44521                 if(p){
44522                     return p;
44523                 }
44524             }
44525         }
44526         return null;
44527     },
44528
44529     /**
44530      * Searches all regions for a panel with the specified id and activates (shows) it.
44531      * @param {String/ContentPanel} panelId The panels id or the panel itself
44532      * @return {Roo.ContentPanel} The shown panel or null
44533      */
44534     showPanel : function(panelId) {
44535       var rs = this.regions;
44536       for(var target in rs){
44537          var r = rs[target];
44538          if(typeof r != "function"){
44539             if(r.hasPanel(panelId)){
44540                return r.showPanel(panelId);
44541             }
44542          }
44543       }
44544       return null;
44545    },
44546
44547    /**
44548      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44549      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44550      */
44551     restoreState : function(provider){
44552         if(!provider){
44553             provider = Roo.state.Manager;
44554         }
44555         var sm = new Roo.LayoutStateManager();
44556         sm.init(this, provider);
44557     },
44558
44559     /**
44560      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44561      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44562      * a valid ContentPanel config object.  Example:
44563      * <pre><code>
44564 // Create the main layout
44565 var layout = new Roo.BorderLayout('main-ct', {
44566     west: {
44567         split:true,
44568         minSize: 175,
44569         titlebar: true
44570     },
44571     center: {
44572         title:'Components'
44573     }
44574 }, 'main-ct');
44575
44576 // Create and add multiple ContentPanels at once via configs
44577 layout.batchAdd({
44578    west: {
44579        id: 'source-files',
44580        autoCreate:true,
44581        title:'Ext Source Files',
44582        autoScroll:true,
44583        fitToFrame:true
44584    },
44585    center : {
44586        el: cview,
44587        autoScroll:true,
44588        fitToFrame:true,
44589        toolbar: tb,
44590        resizeEl:'cbody'
44591    }
44592 });
44593 </code></pre>
44594      * @param {Object} regions An object containing ContentPanel configs by region name
44595      */
44596     batchAdd : function(regions){
44597         this.beginUpdate();
44598         for(var rname in regions){
44599             var lr = this.regions[rname];
44600             if(lr){
44601                 this.addTypedPanels(lr, regions[rname]);
44602             }
44603         }
44604         this.endUpdate();
44605     },
44606
44607     // private
44608     addTypedPanels : function(lr, ps){
44609         if(typeof ps == 'string'){
44610             lr.add(new Roo.ContentPanel(ps));
44611         }
44612         else if(ps instanceof Array){
44613             for(var i =0, len = ps.length; i < len; i++){
44614                 this.addTypedPanels(lr, ps[i]);
44615             }
44616         }
44617         else if(!ps.events){ // raw config?
44618             var el = ps.el;
44619             delete ps.el; // prevent conflict
44620             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44621         }
44622         else {  // panel object assumed!
44623             lr.add(ps);
44624         }
44625     },
44626     /**
44627      * Adds a xtype elements to the layout.
44628      * <pre><code>
44629
44630 layout.addxtype({
44631        xtype : 'ContentPanel',
44632        region: 'west',
44633        items: [ .... ]
44634    }
44635 );
44636
44637 layout.addxtype({
44638         xtype : 'NestedLayoutPanel',
44639         region: 'west',
44640         layout: {
44641            center: { },
44642            west: { }   
44643         },
44644         items : [ ... list of content panels or nested layout panels.. ]
44645    }
44646 );
44647 </code></pre>
44648      * @param {Object} cfg Xtype definition of item to add.
44649      */
44650     addxtype : function(cfg)
44651     {
44652         // basically accepts a pannel...
44653         // can accept a layout region..!?!?
44654         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44655         
44656         if (!cfg.xtype.match(/Panel$/)) {
44657             return false;
44658         }
44659         var ret = false;
44660         var region = cfg.region;
44661         delete cfg.region;
44662         
44663           
44664         var xitems = [];
44665         if (cfg.items) {
44666             xitems = cfg.items;
44667             delete cfg.items;
44668         }
44669         
44670         
44671         switch(cfg.xtype) 
44672         {
44673             case 'ContentPanel':  // ContentPanel (el, cfg)
44674             case 'ScrollPanel':  // ContentPanel (el, cfg)
44675                 if(cfg.autoCreate) {
44676                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44677                 } else {
44678                     var el = this.el.createChild();
44679                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44680                 }
44681                 
44682                 this.add(region, ret);
44683                 break;
44684             
44685             
44686             case 'TreePanel': // our new panel!
44687                 cfg.el = this.el.createChild();
44688                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44689                 this.add(region, ret);
44690                 break;
44691             
44692             case 'NestedLayoutPanel': 
44693                 // create a new Layout (which is  a Border Layout...
44694                 var el = this.el.createChild();
44695                 var clayout = cfg.layout;
44696                 delete cfg.layout;
44697                 clayout.items   = clayout.items  || [];
44698                 // replace this exitems with the clayout ones..
44699                 xitems = clayout.items;
44700                  
44701                 
44702                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44703                     cfg.background = false;
44704                 }
44705                 var layout = new Roo.BorderLayout(el, clayout);
44706                 
44707                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44708                 //console.log('adding nested layout panel '  + cfg.toSource());
44709                 this.add(region, ret);
44710                 
44711                 break;
44712                 
44713             case 'GridPanel': 
44714             
44715                 // needs grid and region
44716                 
44717                 //var el = this.getRegion(region).el.createChild();
44718                 var el = this.el.createChild();
44719                 // create the grid first...
44720                 
44721                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44722                 delete cfg.grid;
44723                 if (region == 'center' && this.active ) {
44724                     cfg.background = false;
44725                 }
44726                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44727                 
44728                 this.add(region, ret);
44729                 if (cfg.background) {
44730                     ret.on('activate', function(gp) {
44731                         if (!gp.grid.rendered) {
44732                             gp.grid.render();
44733                         }
44734                     });
44735                 } else {
44736                     grid.render();
44737                 }
44738                 break;
44739            
44740                
44741                 
44742                 
44743             default: 
44744                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44745                 return null;
44746              // GridPanel (grid, cfg)
44747             
44748         }
44749         this.beginUpdate();
44750         // add children..
44751         Roo.each(xitems, function(i)  {
44752             ret.addxtype(i);
44753         });
44754         this.endUpdate();
44755         return ret;
44756         
44757     }
44758 });
44759
44760 /**
44761  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44762  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44763  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44764  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44765  * <pre><code>
44766 // shorthand
44767 var CP = Roo.ContentPanel;
44768
44769 var layout = Roo.BorderLayout.create({
44770     north: {
44771         initialSize: 25,
44772         titlebar: false,
44773         panels: [new CP("north", "North")]
44774     },
44775     west: {
44776         split:true,
44777         initialSize: 200,
44778         minSize: 175,
44779         maxSize: 400,
44780         titlebar: true,
44781         collapsible: true,
44782         panels: [new CP("west", {title: "West"})]
44783     },
44784     east: {
44785         split:true,
44786         initialSize: 202,
44787         minSize: 175,
44788         maxSize: 400,
44789         titlebar: true,
44790         collapsible: true,
44791         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44792     },
44793     south: {
44794         split:true,
44795         initialSize: 100,
44796         minSize: 100,
44797         maxSize: 200,
44798         titlebar: true,
44799         collapsible: true,
44800         panels: [new CP("south", {title: "South", closable: true})]
44801     },
44802     center: {
44803         titlebar: true,
44804         autoScroll:true,
44805         resizeTabs: true,
44806         minTabWidth: 50,
44807         preferredTabWidth: 150,
44808         panels: [
44809             new CP("center1", {title: "Close Me", closable: true}),
44810             new CP("center2", {title: "Center Panel", closable: false})
44811         ]
44812     }
44813 }, document.body);
44814
44815 layout.getRegion("center").showPanel("center1");
44816 </code></pre>
44817  * @param config
44818  * @param targetEl
44819  */
44820 Roo.BorderLayout.create = function(config, targetEl){
44821     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44822     layout.beginUpdate();
44823     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44824     for(var j = 0, jlen = regions.length; j < jlen; j++){
44825         var lr = regions[j];
44826         if(layout.regions[lr] && config[lr].panels){
44827             var r = layout.regions[lr];
44828             var ps = config[lr].panels;
44829             layout.addTypedPanels(r, ps);
44830         }
44831     }
44832     layout.endUpdate();
44833     return layout;
44834 };
44835
44836 // private
44837 Roo.BorderLayout.RegionFactory = {
44838     // private
44839     validRegions : ["north","south","east","west","center"],
44840
44841     // private
44842     create : function(target, mgr, config){
44843         target = target.toLowerCase();
44844         if(config.lightweight || config.basic){
44845             return new Roo.BasicLayoutRegion(mgr, config, target);
44846         }
44847         switch(target){
44848             case "north":
44849                 return new Roo.NorthLayoutRegion(mgr, config);
44850             case "south":
44851                 return new Roo.SouthLayoutRegion(mgr, config);
44852             case "east":
44853                 return new Roo.EastLayoutRegion(mgr, config);
44854             case "west":
44855                 return new Roo.WestLayoutRegion(mgr, config);
44856             case "center":
44857                 return new Roo.CenterLayoutRegion(mgr, config);
44858         }
44859         throw 'Layout region "'+target+'" not supported.';
44860     }
44861 };/*
44862  * Based on:
44863  * Ext JS Library 1.1.1
44864  * Copyright(c) 2006-2007, Ext JS, LLC.
44865  *
44866  * Originally Released Under LGPL - original licence link has changed is not relivant.
44867  *
44868  * Fork - LGPL
44869  * <script type="text/javascript">
44870  */
44871  
44872 /**
44873  * @class Roo.BasicLayoutRegion
44874  * @extends Roo.util.Observable
44875  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44876  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44877  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44878  */
44879 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44880     this.mgr = mgr;
44881     this.position  = pos;
44882     this.events = {
44883         /**
44884          * @scope Roo.BasicLayoutRegion
44885          */
44886         
44887         /**
44888          * @event beforeremove
44889          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44890          * @param {Roo.LayoutRegion} this
44891          * @param {Roo.ContentPanel} panel The panel
44892          * @param {Object} e The cancel event object
44893          */
44894         "beforeremove" : true,
44895         /**
44896          * @event invalidated
44897          * Fires when the layout for this region is changed.
44898          * @param {Roo.LayoutRegion} this
44899          */
44900         "invalidated" : true,
44901         /**
44902          * @event visibilitychange
44903          * Fires when this region is shown or hidden 
44904          * @param {Roo.LayoutRegion} this
44905          * @param {Boolean} visibility true or false
44906          */
44907         "visibilitychange" : true,
44908         /**
44909          * @event paneladded
44910          * Fires when a panel is added. 
44911          * @param {Roo.LayoutRegion} this
44912          * @param {Roo.ContentPanel} panel The panel
44913          */
44914         "paneladded" : true,
44915         /**
44916          * @event panelremoved
44917          * Fires when a panel is removed. 
44918          * @param {Roo.LayoutRegion} this
44919          * @param {Roo.ContentPanel} panel The panel
44920          */
44921         "panelremoved" : true,
44922         /**
44923          * @event collapsed
44924          * Fires when this region is collapsed.
44925          * @param {Roo.LayoutRegion} this
44926          */
44927         "collapsed" : true,
44928         /**
44929          * @event expanded
44930          * Fires when this region is expanded.
44931          * @param {Roo.LayoutRegion} this
44932          */
44933         "expanded" : true,
44934         /**
44935          * @event slideshow
44936          * Fires when this region is slid into view.
44937          * @param {Roo.LayoutRegion} this
44938          */
44939         "slideshow" : true,
44940         /**
44941          * @event slidehide
44942          * Fires when this region slides out of view. 
44943          * @param {Roo.LayoutRegion} this
44944          */
44945         "slidehide" : true,
44946         /**
44947          * @event panelactivated
44948          * Fires when a panel is activated. 
44949          * @param {Roo.LayoutRegion} this
44950          * @param {Roo.ContentPanel} panel The activated panel
44951          */
44952         "panelactivated" : true,
44953         /**
44954          * @event resized
44955          * Fires when the user resizes this region. 
44956          * @param {Roo.LayoutRegion} this
44957          * @param {Number} newSize The new size (width for east/west, height for north/south)
44958          */
44959         "resized" : true
44960     };
44961     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44962     this.panels = new Roo.util.MixedCollection();
44963     this.panels.getKey = this.getPanelId.createDelegate(this);
44964     this.box = null;
44965     this.activePanel = null;
44966     // ensure listeners are added...
44967     
44968     if (config.listeners || config.events) {
44969         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44970             listeners : config.listeners || {},
44971             events : config.events || {}
44972         });
44973     }
44974     
44975     if(skipConfig !== true){
44976         this.applyConfig(config);
44977     }
44978 };
44979
44980 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44981     getPanelId : function(p){
44982         return p.getId();
44983     },
44984     
44985     applyConfig : function(config){
44986         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44987         this.config = config;
44988         
44989     },
44990     
44991     /**
44992      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44993      * the width, for horizontal (north, south) the height.
44994      * @param {Number} newSize The new width or height
44995      */
44996     resizeTo : function(newSize){
44997         var el = this.el ? this.el :
44998                  (this.activePanel ? this.activePanel.getEl() : null);
44999         if(el){
45000             switch(this.position){
45001                 case "east":
45002                 case "west":
45003                     el.setWidth(newSize);
45004                     this.fireEvent("resized", this, newSize);
45005                 break;
45006                 case "north":
45007                 case "south":
45008                     el.setHeight(newSize);
45009                     this.fireEvent("resized", this, newSize);
45010                 break;                
45011             }
45012         }
45013     },
45014     
45015     getBox : function(){
45016         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45017     },
45018     
45019     getMargins : function(){
45020         return this.margins;
45021     },
45022     
45023     updateBox : function(box){
45024         this.box = box;
45025         var el = this.activePanel.getEl();
45026         el.dom.style.left = box.x + "px";
45027         el.dom.style.top = box.y + "px";
45028         this.activePanel.setSize(box.width, box.height);
45029     },
45030     
45031     /**
45032      * Returns the container element for this region.
45033      * @return {Roo.Element}
45034      */
45035     getEl : function(){
45036         return this.activePanel;
45037     },
45038     
45039     /**
45040      * Returns true if this region is currently visible.
45041      * @return {Boolean}
45042      */
45043     isVisible : function(){
45044         return this.activePanel ? true : false;
45045     },
45046     
45047     setActivePanel : function(panel){
45048         panel = this.getPanel(panel);
45049         if(this.activePanel && this.activePanel != panel){
45050             this.activePanel.setActiveState(false);
45051             this.activePanel.getEl().setLeftTop(-10000,-10000);
45052         }
45053         this.activePanel = panel;
45054         panel.setActiveState(true);
45055         if(this.box){
45056             panel.setSize(this.box.width, this.box.height);
45057         }
45058         this.fireEvent("panelactivated", this, panel);
45059         this.fireEvent("invalidated");
45060     },
45061     
45062     /**
45063      * Show the specified panel.
45064      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45065      * @return {Roo.ContentPanel} The shown panel or null
45066      */
45067     showPanel : function(panel){
45068         if(panel = this.getPanel(panel)){
45069             this.setActivePanel(panel);
45070         }
45071         return panel;
45072     },
45073     
45074     /**
45075      * Get the active panel for this region.
45076      * @return {Roo.ContentPanel} The active panel or null
45077      */
45078     getActivePanel : function(){
45079         return this.activePanel;
45080     },
45081     
45082     /**
45083      * Add the passed ContentPanel(s)
45084      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45085      * @return {Roo.ContentPanel} The panel added (if only one was added)
45086      */
45087     add : function(panel){
45088         if(arguments.length > 1){
45089             for(var i = 0, len = arguments.length; i < len; i++) {
45090                 this.add(arguments[i]);
45091             }
45092             return null;
45093         }
45094         if(this.hasPanel(panel)){
45095             this.showPanel(panel);
45096             return panel;
45097         }
45098         var el = panel.getEl();
45099         if(el.dom.parentNode != this.mgr.el.dom){
45100             this.mgr.el.dom.appendChild(el.dom);
45101         }
45102         if(panel.setRegion){
45103             panel.setRegion(this);
45104         }
45105         this.panels.add(panel);
45106         el.setStyle("position", "absolute");
45107         if(!panel.background){
45108             this.setActivePanel(panel);
45109             if(this.config.initialSize && this.panels.getCount()==1){
45110                 this.resizeTo(this.config.initialSize);
45111             }
45112         }
45113         this.fireEvent("paneladded", this, panel);
45114         return panel;
45115     },
45116     
45117     /**
45118      * Returns true if the panel is in this region.
45119      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45120      * @return {Boolean}
45121      */
45122     hasPanel : function(panel){
45123         if(typeof panel == "object"){ // must be panel obj
45124             panel = panel.getId();
45125         }
45126         return this.getPanel(panel) ? true : false;
45127     },
45128     
45129     /**
45130      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45132      * @param {Boolean} preservePanel Overrides the config preservePanel option
45133      * @return {Roo.ContentPanel} The panel that was removed
45134      */
45135     remove : function(panel, preservePanel){
45136         panel = this.getPanel(panel);
45137         if(!panel){
45138             return null;
45139         }
45140         var e = {};
45141         this.fireEvent("beforeremove", this, panel, e);
45142         if(e.cancel === true){
45143             return null;
45144         }
45145         var panelId = panel.getId();
45146         this.panels.removeKey(panelId);
45147         return panel;
45148     },
45149     
45150     /**
45151      * Returns the panel specified or null if it's not in this region.
45152      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45153      * @return {Roo.ContentPanel}
45154      */
45155     getPanel : function(id){
45156         if(typeof id == "object"){ // must be panel obj
45157             return id;
45158         }
45159         return this.panels.get(id);
45160     },
45161     
45162     /**
45163      * Returns this regions position (north/south/east/west/center).
45164      * @return {String} 
45165      */
45166     getPosition: function(){
45167         return this.position;    
45168     }
45169 });/*
45170  * Based on:
45171  * Ext JS Library 1.1.1
45172  * Copyright(c) 2006-2007, Ext JS, LLC.
45173  *
45174  * Originally Released Under LGPL - original licence link has changed is not relivant.
45175  *
45176  * Fork - LGPL
45177  * <script type="text/javascript">
45178  */
45179  
45180 /**
45181  * @class Roo.LayoutRegion
45182  * @extends Roo.BasicLayoutRegion
45183  * This class represents a region in a layout manager.
45184  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45185  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45186  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45187  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45188  * @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})
45189  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45190  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45191  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45192  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45193  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45194  * @cfg {String}    title           The title for the region (overrides panel titles)
45195  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45196  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45197  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45198  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45199  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45200  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45201  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45202  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45203  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45204  * @cfg {Boolean}   showPin         True to show a pin button
45205  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45206  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45207  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45208  * @cfg {Number}    width           For East/West panels
45209  * @cfg {Number}    height          For North/South panels
45210  * @cfg {Boolean}   split           To show the splitter
45211  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45212  */
45213 Roo.LayoutRegion = function(mgr, config, pos){
45214     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45215     var dh = Roo.DomHelper;
45216     /** This region's container element 
45217     * @type Roo.Element */
45218     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45219     /** This region's title element 
45220     * @type Roo.Element */
45221
45222     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45223         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45224         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45225     ]}, true);
45226     this.titleEl.enableDisplayMode();
45227     /** This region's title text element 
45228     * @type HTMLElement */
45229     this.titleTextEl = this.titleEl.dom.firstChild;
45230     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45231     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45232     this.closeBtn.enableDisplayMode();
45233     this.closeBtn.on("click", this.closeClicked, this);
45234     this.closeBtn.hide();
45235
45236     this.createBody(config);
45237     this.visible = true;
45238     this.collapsed = false;
45239
45240     if(config.hideWhenEmpty){
45241         this.hide();
45242         this.on("paneladded", this.validateVisibility, this);
45243         this.on("panelremoved", this.validateVisibility, this);
45244     }
45245     this.applyConfig(config);
45246 };
45247
45248 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45249
45250     createBody : function(){
45251         /** This region's body element 
45252         * @type Roo.Element */
45253         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45254     },
45255
45256     applyConfig : function(c){
45257         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45258             var dh = Roo.DomHelper;
45259             if(c.titlebar !== false){
45260                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45261                 this.collapseBtn.on("click", this.collapse, this);
45262                 this.collapseBtn.enableDisplayMode();
45263
45264                 if(c.showPin === true || this.showPin){
45265                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45266                     this.stickBtn.enableDisplayMode();
45267                     this.stickBtn.on("click", this.expand, this);
45268                     this.stickBtn.hide();
45269                 }
45270             }
45271             /** This region's collapsed element
45272             * @type Roo.Element */
45273             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45274                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45275             ]}, true);
45276             if(c.floatable !== false){
45277                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45278                this.collapsedEl.on("click", this.collapseClick, this);
45279             }
45280
45281             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45282                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45283                    id: "message", unselectable: "on", style:{"float":"left"}});
45284                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45285              }
45286             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45287             this.expandBtn.on("click", this.expand, this);
45288         }
45289         if(this.collapseBtn){
45290             this.collapseBtn.setVisible(c.collapsible == true);
45291         }
45292         this.cmargins = c.cmargins || this.cmargins ||
45293                          (this.position == "west" || this.position == "east" ?
45294                              {top: 0, left: 2, right:2, bottom: 0} :
45295                              {top: 2, left: 0, right:0, bottom: 2});
45296         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45297         this.bottomTabs = c.tabPosition != "top";
45298         this.autoScroll = c.autoScroll || false;
45299         if(this.autoScroll){
45300             this.bodyEl.setStyle("overflow", "auto");
45301         }else{
45302             this.bodyEl.setStyle("overflow", "hidden");
45303         }
45304         //if(c.titlebar !== false){
45305             if((!c.titlebar && !c.title) || c.titlebar === false){
45306                 this.titleEl.hide();
45307             }else{
45308                 this.titleEl.show();
45309                 if(c.title){
45310                     this.titleTextEl.innerHTML = c.title;
45311                 }
45312             }
45313         //}
45314         this.duration = c.duration || .30;
45315         this.slideDuration = c.slideDuration || .45;
45316         this.config = c;
45317         if(c.collapsed){
45318             this.collapse(true);
45319         }
45320         if(c.hidden){
45321             this.hide();
45322         }
45323     },
45324     /**
45325      * Returns true if this region is currently visible.
45326      * @return {Boolean}
45327      */
45328     isVisible : function(){
45329         return this.visible;
45330     },
45331
45332     /**
45333      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45334      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45335      */
45336     setCollapsedTitle : function(title){
45337         title = title || "&#160;";
45338         if(this.collapsedTitleTextEl){
45339             this.collapsedTitleTextEl.innerHTML = title;
45340         }
45341     },
45342
45343     getBox : function(){
45344         var b;
45345         if(!this.collapsed){
45346             b = this.el.getBox(false, true);
45347         }else{
45348             b = this.collapsedEl.getBox(false, true);
45349         }
45350         return b;
45351     },
45352
45353     getMargins : function(){
45354         return this.collapsed ? this.cmargins : this.margins;
45355     },
45356
45357     highlight : function(){
45358         this.el.addClass("x-layout-panel-dragover");
45359     },
45360
45361     unhighlight : function(){
45362         this.el.removeClass("x-layout-panel-dragover");
45363     },
45364
45365     updateBox : function(box){
45366         this.box = box;
45367         if(!this.collapsed){
45368             this.el.dom.style.left = box.x + "px";
45369             this.el.dom.style.top = box.y + "px";
45370             this.updateBody(box.width, box.height);
45371         }else{
45372             this.collapsedEl.dom.style.left = box.x + "px";
45373             this.collapsedEl.dom.style.top = box.y + "px";
45374             this.collapsedEl.setSize(box.width, box.height);
45375         }
45376         if(this.tabs){
45377             this.tabs.autoSizeTabs();
45378         }
45379     },
45380
45381     updateBody : function(w, h){
45382         if(w !== null){
45383             this.el.setWidth(w);
45384             w -= this.el.getBorderWidth("rl");
45385             if(this.config.adjustments){
45386                 w += this.config.adjustments[0];
45387             }
45388         }
45389         if(h !== null){
45390             this.el.setHeight(h);
45391             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45392             h -= this.el.getBorderWidth("tb");
45393             if(this.config.adjustments){
45394                 h += this.config.adjustments[1];
45395             }
45396             this.bodyEl.setHeight(h);
45397             if(this.tabs){
45398                 h = this.tabs.syncHeight(h);
45399             }
45400         }
45401         if(this.panelSize){
45402             w = w !== null ? w : this.panelSize.width;
45403             h = h !== null ? h : this.panelSize.height;
45404         }
45405         if(this.activePanel){
45406             var el = this.activePanel.getEl();
45407             w = w !== null ? w : el.getWidth();
45408             h = h !== null ? h : el.getHeight();
45409             this.panelSize = {width: w, height: h};
45410             this.activePanel.setSize(w, h);
45411         }
45412         if(Roo.isIE && this.tabs){
45413             this.tabs.el.repaint();
45414         }
45415     },
45416
45417     /**
45418      * Returns the container element for this region.
45419      * @return {Roo.Element}
45420      */
45421     getEl : function(){
45422         return this.el;
45423     },
45424
45425     /**
45426      * Hides this region.
45427      */
45428     hide : function(){
45429         if(!this.collapsed){
45430             this.el.dom.style.left = "-2000px";
45431             this.el.hide();
45432         }else{
45433             this.collapsedEl.dom.style.left = "-2000px";
45434             this.collapsedEl.hide();
45435         }
45436         this.visible = false;
45437         this.fireEvent("visibilitychange", this, false);
45438     },
45439
45440     /**
45441      * Shows this region if it was previously hidden.
45442      */
45443     show : function(){
45444         if(!this.collapsed){
45445             this.el.show();
45446         }else{
45447             this.collapsedEl.show();
45448         }
45449         this.visible = true;
45450         this.fireEvent("visibilitychange", this, true);
45451     },
45452
45453     closeClicked : function(){
45454         if(this.activePanel){
45455             this.remove(this.activePanel);
45456         }
45457     },
45458
45459     collapseClick : function(e){
45460         if(this.isSlid){
45461            e.stopPropagation();
45462            this.slideIn();
45463         }else{
45464            e.stopPropagation();
45465            this.slideOut();
45466         }
45467     },
45468
45469     /**
45470      * Collapses this region.
45471      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45472      */
45473     collapse : function(skipAnim){
45474         if(this.collapsed) return;
45475         this.collapsed = true;
45476         if(this.split){
45477             this.split.el.hide();
45478         }
45479         if(this.config.animate && skipAnim !== true){
45480             this.fireEvent("invalidated", this);
45481             this.animateCollapse();
45482         }else{
45483             this.el.setLocation(-20000,-20000);
45484             this.el.hide();
45485             this.collapsedEl.show();
45486             this.fireEvent("collapsed", this);
45487             this.fireEvent("invalidated", this);
45488         }
45489     },
45490
45491     animateCollapse : function(){
45492         // overridden
45493     },
45494
45495     /**
45496      * Expands this region if it was previously collapsed.
45497      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45498      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45499      */
45500     expand : function(e, skipAnim){
45501         if(e) e.stopPropagation();
45502         if(!this.collapsed || this.el.hasActiveFx()) return;
45503         if(this.isSlid){
45504             this.afterSlideIn();
45505             skipAnim = true;
45506         }
45507         this.collapsed = false;
45508         if(this.config.animate && skipAnim !== true){
45509             this.animateExpand();
45510         }else{
45511             this.el.show();
45512             if(this.split){
45513                 this.split.el.show();
45514             }
45515             this.collapsedEl.setLocation(-2000,-2000);
45516             this.collapsedEl.hide();
45517             this.fireEvent("invalidated", this);
45518             this.fireEvent("expanded", this);
45519         }
45520     },
45521
45522     animateExpand : function(){
45523         // overridden
45524     },
45525
45526     initTabs : function()
45527     {
45528         this.bodyEl.setStyle("overflow", "hidden");
45529         var ts = new Roo.TabPanel(
45530                 this.bodyEl.dom,
45531                 {
45532                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45533                     disableTooltips: this.config.disableTabTips,
45534                     toolbar : this.config.toolbar
45535                 }
45536         );
45537         if(this.config.hideTabs){
45538             ts.stripWrap.setDisplayed(false);
45539         }
45540         this.tabs = ts;
45541         ts.resizeTabs = this.config.resizeTabs === true;
45542         ts.minTabWidth = this.config.minTabWidth || 40;
45543         ts.maxTabWidth = this.config.maxTabWidth || 250;
45544         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45545         ts.monitorResize = false;
45546         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45547         ts.bodyEl.addClass('x-layout-tabs-body');
45548         this.panels.each(this.initPanelAsTab, this);
45549     },
45550
45551     initPanelAsTab : function(panel){
45552         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45553                     this.config.closeOnTab && panel.isClosable());
45554         if(panel.tabTip !== undefined){
45555             ti.setTooltip(panel.tabTip);
45556         }
45557         ti.on("activate", function(){
45558               this.setActivePanel(panel);
45559         }, this);
45560         if(this.config.closeOnTab){
45561             ti.on("beforeclose", function(t, e){
45562                 e.cancel = true;
45563                 this.remove(panel);
45564             }, this);
45565         }
45566         return ti;
45567     },
45568
45569     updatePanelTitle : function(panel, title){
45570         if(this.activePanel == panel){
45571             this.updateTitle(title);
45572         }
45573         if(this.tabs){
45574             var ti = this.tabs.getTab(panel.getEl().id);
45575             ti.setText(title);
45576             if(panel.tabTip !== undefined){
45577                 ti.setTooltip(panel.tabTip);
45578             }
45579         }
45580     },
45581
45582     updateTitle : function(title){
45583         if(this.titleTextEl && !this.config.title){
45584             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45585         }
45586     },
45587
45588     setActivePanel : function(panel){
45589         panel = this.getPanel(panel);
45590         if(this.activePanel && this.activePanel != panel){
45591             this.activePanel.setActiveState(false);
45592         }
45593         this.activePanel = panel;
45594         panel.setActiveState(true);
45595         if(this.panelSize){
45596             panel.setSize(this.panelSize.width, this.panelSize.height);
45597         }
45598         if(this.closeBtn){
45599             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45600         }
45601         this.updateTitle(panel.getTitle());
45602         if(this.tabs){
45603             this.fireEvent("invalidated", this);
45604         }
45605         this.fireEvent("panelactivated", this, panel);
45606     },
45607
45608     /**
45609      * Shows the specified panel.
45610      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45611      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45612      */
45613     showPanel : function(panel){
45614         if(panel = this.getPanel(panel)){
45615             if(this.tabs){
45616                 var tab = this.tabs.getTab(panel.getEl().id);
45617                 if(tab.isHidden()){
45618                     this.tabs.unhideTab(tab.id);
45619                 }
45620                 tab.activate();
45621             }else{
45622                 this.setActivePanel(panel);
45623             }
45624         }
45625         return panel;
45626     },
45627
45628     /**
45629      * Get the active panel for this region.
45630      * @return {Roo.ContentPanel} The active panel or null
45631      */
45632     getActivePanel : function(){
45633         return this.activePanel;
45634     },
45635
45636     validateVisibility : function(){
45637         if(this.panels.getCount() < 1){
45638             this.updateTitle("&#160;");
45639             this.closeBtn.hide();
45640             this.hide();
45641         }else{
45642             if(!this.isVisible()){
45643                 this.show();
45644             }
45645         }
45646     },
45647
45648     /**
45649      * Adds the passed ContentPanel(s) to this region.
45650      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45651      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45652      */
45653     add : function(panel){
45654         if(arguments.length > 1){
45655             for(var i = 0, len = arguments.length; i < len; i++) {
45656                 this.add(arguments[i]);
45657             }
45658             return null;
45659         }
45660         if(this.hasPanel(panel)){
45661             this.showPanel(panel);
45662             return panel;
45663         }
45664         panel.setRegion(this);
45665         this.panels.add(panel);
45666         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45667             this.bodyEl.dom.appendChild(panel.getEl().dom);
45668             if(panel.background !== true){
45669                 this.setActivePanel(panel);
45670             }
45671             this.fireEvent("paneladded", this, panel);
45672             return panel;
45673         }
45674         if(!this.tabs){
45675             this.initTabs();
45676         }else{
45677             this.initPanelAsTab(panel);
45678         }
45679         if(panel.background !== true){
45680             this.tabs.activate(panel.getEl().id);
45681         }
45682         this.fireEvent("paneladded", this, panel);
45683         return panel;
45684     },
45685
45686     /**
45687      * Hides the tab for the specified panel.
45688      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45689      */
45690     hidePanel : function(panel){
45691         if(this.tabs && (panel = this.getPanel(panel))){
45692             this.tabs.hideTab(panel.getEl().id);
45693         }
45694     },
45695
45696     /**
45697      * Unhides the tab for a previously hidden panel.
45698      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45699      */
45700     unhidePanel : function(panel){
45701         if(this.tabs && (panel = this.getPanel(panel))){
45702             this.tabs.unhideTab(panel.getEl().id);
45703         }
45704     },
45705
45706     clearPanels : function(){
45707         while(this.panels.getCount() > 0){
45708              this.remove(this.panels.first());
45709         }
45710     },
45711
45712     /**
45713      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45714      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45715      * @param {Boolean} preservePanel Overrides the config preservePanel option
45716      * @return {Roo.ContentPanel} The panel that was removed
45717      */
45718     remove : function(panel, preservePanel){
45719         panel = this.getPanel(panel);
45720         if(!panel){
45721             return null;
45722         }
45723         var e = {};
45724         this.fireEvent("beforeremove", this, panel, e);
45725         if(e.cancel === true){
45726             return null;
45727         }
45728         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45729         var panelId = panel.getId();
45730         this.panels.removeKey(panelId);
45731         if(preservePanel){
45732             document.body.appendChild(panel.getEl().dom);
45733         }
45734         if(this.tabs){
45735             this.tabs.removeTab(panel.getEl().id);
45736         }else if (!preservePanel){
45737             this.bodyEl.dom.removeChild(panel.getEl().dom);
45738         }
45739         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45740             var p = this.panels.first();
45741             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45742             tempEl.appendChild(p.getEl().dom);
45743             this.bodyEl.update("");
45744             this.bodyEl.dom.appendChild(p.getEl().dom);
45745             tempEl = null;
45746             this.updateTitle(p.getTitle());
45747             this.tabs = null;
45748             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45749             this.setActivePanel(p);
45750         }
45751         panel.setRegion(null);
45752         if(this.activePanel == panel){
45753             this.activePanel = null;
45754         }
45755         if(this.config.autoDestroy !== false && preservePanel !== true){
45756             try{panel.destroy();}catch(e){}
45757         }
45758         this.fireEvent("panelremoved", this, panel);
45759         return panel;
45760     },
45761
45762     /**
45763      * Returns the TabPanel component used by this region
45764      * @return {Roo.TabPanel}
45765      */
45766     getTabs : function(){
45767         return this.tabs;
45768     },
45769
45770     createTool : function(parentEl, className){
45771         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45772             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45773         btn.addClassOnOver("x-layout-tools-button-over");
45774         return btn;
45775     }
45776 });/*
45777  * Based on:
45778  * Ext JS Library 1.1.1
45779  * Copyright(c) 2006-2007, Ext JS, LLC.
45780  *
45781  * Originally Released Under LGPL - original licence link has changed is not relivant.
45782  *
45783  * Fork - LGPL
45784  * <script type="text/javascript">
45785  */
45786  
45787
45788
45789 /**
45790  * @class Roo.SplitLayoutRegion
45791  * @extends Roo.LayoutRegion
45792  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45793  */
45794 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45795     this.cursor = cursor;
45796     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45797 };
45798
45799 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45800     splitTip : "Drag to resize.",
45801     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45802     useSplitTips : false,
45803
45804     applyConfig : function(config){
45805         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45806         if(config.split){
45807             if(!this.split){
45808                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45809                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45810                 /** The SplitBar for this region 
45811                 * @type Roo.SplitBar */
45812                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45813                 this.split.on("moved", this.onSplitMove, this);
45814                 this.split.useShim = config.useShim === true;
45815                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45816                 if(this.useSplitTips){
45817                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45818                 }
45819                 if(config.collapsible){
45820                     this.split.el.on("dblclick", this.collapse,  this);
45821                 }
45822             }
45823             if(typeof config.minSize != "undefined"){
45824                 this.split.minSize = config.minSize;
45825             }
45826             if(typeof config.maxSize != "undefined"){
45827                 this.split.maxSize = config.maxSize;
45828             }
45829             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45830                 this.hideSplitter();
45831             }
45832         }
45833     },
45834
45835     getHMaxSize : function(){
45836          var cmax = this.config.maxSize || 10000;
45837          var center = this.mgr.getRegion("center");
45838          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45839     },
45840
45841     getVMaxSize : function(){
45842          var cmax = this.config.maxSize || 10000;
45843          var center = this.mgr.getRegion("center");
45844          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45845     },
45846
45847     onSplitMove : function(split, newSize){
45848         this.fireEvent("resized", this, newSize);
45849     },
45850     
45851     /** 
45852      * Returns the {@link Roo.SplitBar} for this region.
45853      * @return {Roo.SplitBar}
45854      */
45855     getSplitBar : function(){
45856         return this.split;
45857     },
45858     
45859     hide : function(){
45860         this.hideSplitter();
45861         Roo.SplitLayoutRegion.superclass.hide.call(this);
45862     },
45863
45864     hideSplitter : function(){
45865         if(this.split){
45866             this.split.el.setLocation(-2000,-2000);
45867             this.split.el.hide();
45868         }
45869     },
45870
45871     show : function(){
45872         if(this.split){
45873             this.split.el.show();
45874         }
45875         Roo.SplitLayoutRegion.superclass.show.call(this);
45876     },
45877     
45878     beforeSlide: function(){
45879         if(Roo.isGecko){// firefox overflow auto bug workaround
45880             this.bodyEl.clip();
45881             if(this.tabs) this.tabs.bodyEl.clip();
45882             if(this.activePanel){
45883                 this.activePanel.getEl().clip();
45884                 
45885                 if(this.activePanel.beforeSlide){
45886                     this.activePanel.beforeSlide();
45887                 }
45888             }
45889         }
45890     },
45891     
45892     afterSlide : function(){
45893         if(Roo.isGecko){// firefox overflow auto bug workaround
45894             this.bodyEl.unclip();
45895             if(this.tabs) this.tabs.bodyEl.unclip();
45896             if(this.activePanel){
45897                 this.activePanel.getEl().unclip();
45898                 if(this.activePanel.afterSlide){
45899                     this.activePanel.afterSlide();
45900                 }
45901             }
45902         }
45903     },
45904
45905     initAutoHide : function(){
45906         if(this.autoHide !== false){
45907             if(!this.autoHideHd){
45908                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45909                 this.autoHideHd = {
45910                     "mouseout": function(e){
45911                         if(!e.within(this.el, true)){
45912                             st.delay(500);
45913                         }
45914                     },
45915                     "mouseover" : function(e){
45916                         st.cancel();
45917                     },
45918                     scope : this
45919                 };
45920             }
45921             this.el.on(this.autoHideHd);
45922         }
45923     },
45924
45925     clearAutoHide : function(){
45926         if(this.autoHide !== false){
45927             this.el.un("mouseout", this.autoHideHd.mouseout);
45928             this.el.un("mouseover", this.autoHideHd.mouseover);
45929         }
45930     },
45931
45932     clearMonitor : function(){
45933         Roo.get(document).un("click", this.slideInIf, this);
45934     },
45935
45936     // these names are backwards but not changed for compat
45937     slideOut : function(){
45938         if(this.isSlid || this.el.hasActiveFx()){
45939             return;
45940         }
45941         this.isSlid = true;
45942         if(this.collapseBtn){
45943             this.collapseBtn.hide();
45944         }
45945         this.closeBtnState = this.closeBtn.getStyle('display');
45946         this.closeBtn.hide();
45947         if(this.stickBtn){
45948             this.stickBtn.show();
45949         }
45950         this.el.show();
45951         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45952         this.beforeSlide();
45953         this.el.setStyle("z-index", 10001);
45954         this.el.slideIn(this.getSlideAnchor(), {
45955             callback: function(){
45956                 this.afterSlide();
45957                 this.initAutoHide();
45958                 Roo.get(document).on("click", this.slideInIf, this);
45959                 this.fireEvent("slideshow", this);
45960             },
45961             scope: this,
45962             block: true
45963         });
45964     },
45965
45966     afterSlideIn : function(){
45967         this.clearAutoHide();
45968         this.isSlid = false;
45969         this.clearMonitor();
45970         this.el.setStyle("z-index", "");
45971         if(this.collapseBtn){
45972             this.collapseBtn.show();
45973         }
45974         this.closeBtn.setStyle('display', this.closeBtnState);
45975         if(this.stickBtn){
45976             this.stickBtn.hide();
45977         }
45978         this.fireEvent("slidehide", this);
45979     },
45980
45981     slideIn : function(cb){
45982         if(!this.isSlid || this.el.hasActiveFx()){
45983             Roo.callback(cb);
45984             return;
45985         }
45986         this.isSlid = false;
45987         this.beforeSlide();
45988         this.el.slideOut(this.getSlideAnchor(), {
45989             callback: function(){
45990                 this.el.setLeftTop(-10000, -10000);
45991                 this.afterSlide();
45992                 this.afterSlideIn();
45993                 Roo.callback(cb);
45994             },
45995             scope: this,
45996             block: true
45997         });
45998     },
45999     
46000     slideInIf : function(e){
46001         if(!e.within(this.el)){
46002             this.slideIn();
46003         }
46004     },
46005
46006     animateCollapse : function(){
46007         this.beforeSlide();
46008         this.el.setStyle("z-index", 20000);
46009         var anchor = this.getSlideAnchor();
46010         this.el.slideOut(anchor, {
46011             callback : function(){
46012                 this.el.setStyle("z-index", "");
46013                 this.collapsedEl.slideIn(anchor, {duration:.3});
46014                 this.afterSlide();
46015                 this.el.setLocation(-10000,-10000);
46016                 this.el.hide();
46017                 this.fireEvent("collapsed", this);
46018             },
46019             scope: this,
46020             block: true
46021         });
46022     },
46023
46024     animateExpand : function(){
46025         this.beforeSlide();
46026         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46027         this.el.setStyle("z-index", 20000);
46028         this.collapsedEl.hide({
46029             duration:.1
46030         });
46031         this.el.slideIn(this.getSlideAnchor(), {
46032             callback : function(){
46033                 this.el.setStyle("z-index", "");
46034                 this.afterSlide();
46035                 if(this.split){
46036                     this.split.el.show();
46037                 }
46038                 this.fireEvent("invalidated", this);
46039                 this.fireEvent("expanded", this);
46040             },
46041             scope: this,
46042             block: true
46043         });
46044     },
46045
46046     anchors : {
46047         "west" : "left",
46048         "east" : "right",
46049         "north" : "top",
46050         "south" : "bottom"
46051     },
46052
46053     sanchors : {
46054         "west" : "l",
46055         "east" : "r",
46056         "north" : "t",
46057         "south" : "b"
46058     },
46059
46060     canchors : {
46061         "west" : "tl-tr",
46062         "east" : "tr-tl",
46063         "north" : "tl-bl",
46064         "south" : "bl-tl"
46065     },
46066
46067     getAnchor : function(){
46068         return this.anchors[this.position];
46069     },
46070
46071     getCollapseAnchor : function(){
46072         return this.canchors[this.position];
46073     },
46074
46075     getSlideAnchor : function(){
46076         return this.sanchors[this.position];
46077     },
46078
46079     getAlignAdj : function(){
46080         var cm = this.cmargins;
46081         switch(this.position){
46082             case "west":
46083                 return [0, 0];
46084             break;
46085             case "east":
46086                 return [0, 0];
46087             break;
46088             case "north":
46089                 return [0, 0];
46090             break;
46091             case "south":
46092                 return [0, 0];
46093             break;
46094         }
46095     },
46096
46097     getExpandAdj : function(){
46098         var c = this.collapsedEl, cm = this.cmargins;
46099         switch(this.position){
46100             case "west":
46101                 return [-(cm.right+c.getWidth()+cm.left), 0];
46102             break;
46103             case "east":
46104                 return [cm.right+c.getWidth()+cm.left, 0];
46105             break;
46106             case "north":
46107                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46108             break;
46109             case "south":
46110                 return [0, cm.top+cm.bottom+c.getHeight()];
46111             break;
46112         }
46113     }
46114 });/*
46115  * Based on:
46116  * Ext JS Library 1.1.1
46117  * Copyright(c) 2006-2007, Ext JS, LLC.
46118  *
46119  * Originally Released Under LGPL - original licence link has changed is not relivant.
46120  *
46121  * Fork - LGPL
46122  * <script type="text/javascript">
46123  */
46124 /*
46125  * These classes are private internal classes
46126  */
46127 Roo.CenterLayoutRegion = function(mgr, config){
46128     Roo.LayoutRegion.call(this, mgr, config, "center");
46129     this.visible = true;
46130     this.minWidth = config.minWidth || 20;
46131     this.minHeight = config.minHeight || 20;
46132 };
46133
46134 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46135     hide : function(){
46136         // center panel can't be hidden
46137     },
46138     
46139     show : function(){
46140         // center panel can't be hidden
46141     },
46142     
46143     getMinWidth: function(){
46144         return this.minWidth;
46145     },
46146     
46147     getMinHeight: function(){
46148         return this.minHeight;
46149     }
46150 });
46151
46152
46153 Roo.NorthLayoutRegion = function(mgr, config){
46154     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46155     if(this.split){
46156         this.split.placement = Roo.SplitBar.TOP;
46157         this.split.orientation = Roo.SplitBar.VERTICAL;
46158         this.split.el.addClass("x-layout-split-v");
46159     }
46160     var size = config.initialSize || config.height;
46161     if(typeof size != "undefined"){
46162         this.el.setHeight(size);
46163     }
46164 };
46165 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46166     orientation: Roo.SplitBar.VERTICAL,
46167     getBox : function(){
46168         if(this.collapsed){
46169             return this.collapsedEl.getBox();
46170         }
46171         var box = this.el.getBox();
46172         if(this.split){
46173             box.height += this.split.el.getHeight();
46174         }
46175         return box;
46176     },
46177     
46178     updateBox : function(box){
46179         if(this.split && !this.collapsed){
46180             box.height -= this.split.el.getHeight();
46181             this.split.el.setLeft(box.x);
46182             this.split.el.setTop(box.y+box.height);
46183             this.split.el.setWidth(box.width);
46184         }
46185         if(this.collapsed){
46186             this.updateBody(box.width, null);
46187         }
46188         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46189     }
46190 });
46191
46192 Roo.SouthLayoutRegion = function(mgr, config){
46193     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46194     if(this.split){
46195         this.split.placement = Roo.SplitBar.BOTTOM;
46196         this.split.orientation = Roo.SplitBar.VERTICAL;
46197         this.split.el.addClass("x-layout-split-v");
46198     }
46199     var size = config.initialSize || config.height;
46200     if(typeof size != "undefined"){
46201         this.el.setHeight(size);
46202     }
46203 };
46204 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46205     orientation: Roo.SplitBar.VERTICAL,
46206     getBox : function(){
46207         if(this.collapsed){
46208             return this.collapsedEl.getBox();
46209         }
46210         var box = this.el.getBox();
46211         if(this.split){
46212             var sh = this.split.el.getHeight();
46213             box.height += sh;
46214             box.y -= sh;
46215         }
46216         return box;
46217     },
46218     
46219     updateBox : function(box){
46220         if(this.split && !this.collapsed){
46221             var sh = this.split.el.getHeight();
46222             box.height -= sh;
46223             box.y += sh;
46224             this.split.el.setLeft(box.x);
46225             this.split.el.setTop(box.y-sh);
46226             this.split.el.setWidth(box.width);
46227         }
46228         if(this.collapsed){
46229             this.updateBody(box.width, null);
46230         }
46231         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46232     }
46233 });
46234
46235 Roo.EastLayoutRegion = function(mgr, config){
46236     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46237     if(this.split){
46238         this.split.placement = Roo.SplitBar.RIGHT;
46239         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46240         this.split.el.addClass("x-layout-split-h");
46241     }
46242     var size = config.initialSize || config.width;
46243     if(typeof size != "undefined"){
46244         this.el.setWidth(size);
46245     }
46246 };
46247 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46248     orientation: Roo.SplitBar.HORIZONTAL,
46249     getBox : function(){
46250         if(this.collapsed){
46251             return this.collapsedEl.getBox();
46252         }
46253         var box = this.el.getBox();
46254         if(this.split){
46255             var sw = this.split.el.getWidth();
46256             box.width += sw;
46257             box.x -= sw;
46258         }
46259         return box;
46260     },
46261
46262     updateBox : function(box){
46263         if(this.split && !this.collapsed){
46264             var sw = this.split.el.getWidth();
46265             box.width -= sw;
46266             this.split.el.setLeft(box.x);
46267             this.split.el.setTop(box.y);
46268             this.split.el.setHeight(box.height);
46269             box.x += sw;
46270         }
46271         if(this.collapsed){
46272             this.updateBody(null, box.height);
46273         }
46274         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46275     }
46276 });
46277
46278 Roo.WestLayoutRegion = function(mgr, config){
46279     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46280     if(this.split){
46281         this.split.placement = Roo.SplitBar.LEFT;
46282         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46283         this.split.el.addClass("x-layout-split-h");
46284     }
46285     var size = config.initialSize || config.width;
46286     if(typeof size != "undefined"){
46287         this.el.setWidth(size);
46288     }
46289 };
46290 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46291     orientation: Roo.SplitBar.HORIZONTAL,
46292     getBox : function(){
46293         if(this.collapsed){
46294             return this.collapsedEl.getBox();
46295         }
46296         var box = this.el.getBox();
46297         if(this.split){
46298             box.width += this.split.el.getWidth();
46299         }
46300         return box;
46301     },
46302     
46303     updateBox : function(box){
46304         if(this.split && !this.collapsed){
46305             var sw = this.split.el.getWidth();
46306             box.width -= sw;
46307             this.split.el.setLeft(box.x+box.width);
46308             this.split.el.setTop(box.y);
46309             this.split.el.setHeight(box.height);
46310         }
46311         if(this.collapsed){
46312             this.updateBody(null, box.height);
46313         }
46314         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46315     }
46316 });
46317 /*
46318  * Based on:
46319  * Ext JS Library 1.1.1
46320  * Copyright(c) 2006-2007, Ext JS, LLC.
46321  *
46322  * Originally Released Under LGPL - original licence link has changed is not relivant.
46323  *
46324  * Fork - LGPL
46325  * <script type="text/javascript">
46326  */
46327  
46328  
46329 /*
46330  * Private internal class for reading and applying state
46331  */
46332 Roo.LayoutStateManager = function(layout){
46333      // default empty state
46334      this.state = {
46335         north: {},
46336         south: {},
46337         east: {},
46338         west: {}       
46339     };
46340 };
46341
46342 Roo.LayoutStateManager.prototype = {
46343     init : function(layout, provider){
46344         this.provider = provider;
46345         var state = provider.get(layout.id+"-layout-state");
46346         if(state){
46347             var wasUpdating = layout.isUpdating();
46348             if(!wasUpdating){
46349                 layout.beginUpdate();
46350             }
46351             for(var key in state){
46352                 if(typeof state[key] != "function"){
46353                     var rstate = state[key];
46354                     var r = layout.getRegion(key);
46355                     if(r && rstate){
46356                         if(rstate.size){
46357                             r.resizeTo(rstate.size);
46358                         }
46359                         if(rstate.collapsed == true){
46360                             r.collapse(true);
46361                         }else{
46362                             r.expand(null, true);
46363                         }
46364                     }
46365                 }
46366             }
46367             if(!wasUpdating){
46368                 layout.endUpdate();
46369             }
46370             this.state = state; 
46371         }
46372         this.layout = layout;
46373         layout.on("regionresized", this.onRegionResized, this);
46374         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46375         layout.on("regionexpanded", this.onRegionExpanded, this);
46376     },
46377     
46378     storeState : function(){
46379         this.provider.set(this.layout.id+"-layout-state", this.state);
46380     },
46381     
46382     onRegionResized : function(region, newSize){
46383         this.state[region.getPosition()].size = newSize;
46384         this.storeState();
46385     },
46386     
46387     onRegionCollapsed : function(region){
46388         this.state[region.getPosition()].collapsed = true;
46389         this.storeState();
46390     },
46391     
46392     onRegionExpanded : function(region){
46393         this.state[region.getPosition()].collapsed = false;
46394         this.storeState();
46395     }
46396 };/*
46397  * Based on:
46398  * Ext JS Library 1.1.1
46399  * Copyright(c) 2006-2007, Ext JS, LLC.
46400  *
46401  * Originally Released Under LGPL - original licence link has changed is not relivant.
46402  *
46403  * Fork - LGPL
46404  * <script type="text/javascript">
46405  */
46406 /**
46407  * @class Roo.ContentPanel
46408  * @extends Roo.util.Observable
46409  * A basic ContentPanel element.
46410  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46411  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46412  * @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
46413  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46414  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46415  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46416  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46417  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46418  * @cfg {String} title          The title for this panel
46419  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46420  * @cfg {String} url            Calls {@link #setUrl} with this value
46421  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46422  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46423  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46424  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46425
46426  * @constructor
46427  * Create a new ContentPanel.
46428  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46429  * @param {String/Object} config A string to set only the title or a config object
46430  * @param {String} content (optional) Set the HTML content for this panel
46431  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46432  */
46433 Roo.ContentPanel = function(el, config, content){
46434     
46435      
46436     /*
46437     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46438         config = el;
46439         el = Roo.id();
46440     }
46441     if (config && config.parentLayout) { 
46442         el = config.parentLayout.el.createChild(); 
46443     }
46444     */
46445     if(el.autoCreate){ // xtype is available if this is called from factory
46446         config = el;
46447         el = Roo.id();
46448     }
46449     this.el = Roo.get(el);
46450     if(!this.el && config && config.autoCreate){
46451         if(typeof config.autoCreate == "object"){
46452             if(!config.autoCreate.id){
46453                 config.autoCreate.id = config.id||el;
46454             }
46455             this.el = Roo.DomHelper.append(document.body,
46456                         config.autoCreate, true);
46457         }else{
46458             this.el = Roo.DomHelper.append(document.body,
46459                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46460         }
46461     }
46462     this.closable = false;
46463     this.loaded = false;
46464     this.active = false;
46465     if(typeof config == "string"){
46466         this.title = config;
46467     }else{
46468         Roo.apply(this, config);
46469     }
46470     
46471     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46472         this.wrapEl = this.el.wrap();
46473         this.toolbar.container = this.el.insertSibling(false, 'before');
46474         this.toolbar = new Roo.Toolbar(this.toolbar);
46475     }
46476     
46477     
46478     
46479     if(this.resizeEl){
46480         this.resizeEl = Roo.get(this.resizeEl, true);
46481     }else{
46482         this.resizeEl = this.el;
46483     }
46484     this.addEvents({
46485         /**
46486          * @event activate
46487          * Fires when this panel is activated. 
46488          * @param {Roo.ContentPanel} this
46489          */
46490         "activate" : true,
46491         /**
46492          * @event deactivate
46493          * Fires when this panel is activated. 
46494          * @param {Roo.ContentPanel} this
46495          */
46496         "deactivate" : true,
46497
46498         /**
46499          * @event resize
46500          * Fires when this panel is resized if fitToFrame is true.
46501          * @param {Roo.ContentPanel} this
46502          * @param {Number} width The width after any component adjustments
46503          * @param {Number} height The height after any component adjustments
46504          */
46505         "resize" : true,
46506         
46507          /**
46508          * @event render
46509          * Fires when this tab is created
46510          * @param {Roo.ContentPanel} this
46511          */
46512         "render" : true
46513         
46514         
46515         
46516     });
46517     if(this.autoScroll){
46518         this.resizeEl.setStyle("overflow", "auto");
46519     } else {
46520         // fix randome scrolling
46521         this.el.on('scroll', function() {
46522             Roo.log('fix random scolling');
46523             this.scrollTo('top',0); 
46524         });
46525     }
46526     content = content || this.content;
46527     if(content){
46528         this.setContent(content);
46529     }
46530     if(config && config.url){
46531         this.setUrl(this.url, this.params, this.loadOnce);
46532     }
46533     
46534     
46535     
46536     Roo.ContentPanel.superclass.constructor.call(this);
46537     
46538     this.fireEvent('render', this);
46539 };
46540
46541 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46542     tabTip:'',
46543     setRegion : function(region){
46544         this.region = region;
46545         if(region){
46546            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46547         }else{
46548            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46549         } 
46550     },
46551     
46552     /**
46553      * Returns the toolbar for this Panel if one was configured. 
46554      * @return {Roo.Toolbar} 
46555      */
46556     getToolbar : function(){
46557         return this.toolbar;
46558     },
46559     
46560     setActiveState : function(active){
46561         this.active = active;
46562         if(!active){
46563             this.fireEvent("deactivate", this);
46564         }else{
46565             this.fireEvent("activate", this);
46566         }
46567     },
46568     /**
46569      * Updates this panel's element
46570      * @param {String} content The new content
46571      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46572     */
46573     setContent : function(content, loadScripts){
46574         this.el.update(content, loadScripts);
46575     },
46576
46577     ignoreResize : function(w, h){
46578         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46579             return true;
46580         }else{
46581             this.lastSize = {width: w, height: h};
46582             return false;
46583         }
46584     },
46585     /**
46586      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46587      * @return {Roo.UpdateManager} The UpdateManager
46588      */
46589     getUpdateManager : function(){
46590         return this.el.getUpdateManager();
46591     },
46592      /**
46593      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46594      * @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:
46595 <pre><code>
46596 panel.load({
46597     url: "your-url.php",
46598     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46599     callback: yourFunction,
46600     scope: yourObject, //(optional scope)
46601     discardUrl: false,
46602     nocache: false,
46603     text: "Loading...",
46604     timeout: 30,
46605     scripts: false
46606 });
46607 </code></pre>
46608      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46609      * 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.
46610      * @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}
46611      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46612      * @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.
46613      * @return {Roo.ContentPanel} this
46614      */
46615     load : function(){
46616         var um = this.el.getUpdateManager();
46617         um.update.apply(um, arguments);
46618         return this;
46619     },
46620
46621
46622     /**
46623      * 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.
46624      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46625      * @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)
46626      * @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)
46627      * @return {Roo.UpdateManager} The UpdateManager
46628      */
46629     setUrl : function(url, params, loadOnce){
46630         if(this.refreshDelegate){
46631             this.removeListener("activate", this.refreshDelegate);
46632         }
46633         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46634         this.on("activate", this.refreshDelegate);
46635         return this.el.getUpdateManager();
46636     },
46637     
46638     _handleRefresh : function(url, params, loadOnce){
46639         if(!loadOnce || !this.loaded){
46640             var updater = this.el.getUpdateManager();
46641             updater.update(url, params, this._setLoaded.createDelegate(this));
46642         }
46643     },
46644     
46645     _setLoaded : function(){
46646         this.loaded = true;
46647     }, 
46648     
46649     /**
46650      * Returns this panel's id
46651      * @return {String} 
46652      */
46653     getId : function(){
46654         return this.el.id;
46655     },
46656     
46657     /** 
46658      * Returns this panel's element - used by regiosn to add.
46659      * @return {Roo.Element} 
46660      */
46661     getEl : function(){
46662         return this.wrapEl || this.el;
46663     },
46664     
46665     adjustForComponents : function(width, height){
46666         if(this.resizeEl != this.el){
46667             width -= this.el.getFrameWidth('lr');
46668             height -= this.el.getFrameWidth('tb');
46669         }
46670         if(this.toolbar){
46671             var te = this.toolbar.getEl();
46672             height -= te.getHeight();
46673             te.setWidth(width);
46674         }
46675         if(this.adjustments){
46676             width += this.adjustments[0];
46677             height += this.adjustments[1];
46678         }
46679         return {"width": width, "height": height};
46680     },
46681     
46682     setSize : function(width, height){
46683         if(this.fitToFrame && !this.ignoreResize(width, height)){
46684             if(this.fitContainer && this.resizeEl != this.el){
46685                 this.el.setSize(width, height);
46686             }
46687             var size = this.adjustForComponents(width, height);
46688             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46689             this.fireEvent('resize', this, size.width, size.height);
46690         }
46691     },
46692     
46693     /**
46694      * Returns this panel's title
46695      * @return {String} 
46696      */
46697     getTitle : function(){
46698         return this.title;
46699     },
46700     
46701     /**
46702      * Set this panel's title
46703      * @param {String} title
46704      */
46705     setTitle : function(title){
46706         this.title = title;
46707         if(this.region){
46708             this.region.updatePanelTitle(this, title);
46709         }
46710     },
46711     
46712     /**
46713      * Returns true is this panel was configured to be closable
46714      * @return {Boolean} 
46715      */
46716     isClosable : function(){
46717         return this.closable;
46718     },
46719     
46720     beforeSlide : function(){
46721         this.el.clip();
46722         this.resizeEl.clip();
46723     },
46724     
46725     afterSlide : function(){
46726         this.el.unclip();
46727         this.resizeEl.unclip();
46728     },
46729     
46730     /**
46731      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46732      *   Will fail silently if the {@link #setUrl} method has not been called.
46733      *   This does not activate the panel, just updates its content.
46734      */
46735     refresh : function(){
46736         if(this.refreshDelegate){
46737            this.loaded = false;
46738            this.refreshDelegate();
46739         }
46740     },
46741     
46742     /**
46743      * Destroys this panel
46744      */
46745     destroy : function(){
46746         this.el.removeAllListeners();
46747         var tempEl = document.createElement("span");
46748         tempEl.appendChild(this.el.dom);
46749         tempEl.innerHTML = "";
46750         this.el.remove();
46751         this.el = null;
46752     },
46753     
46754     /**
46755      * form - if the content panel contains a form - this is a reference to it.
46756      * @type {Roo.form.Form}
46757      */
46758     form : false,
46759     /**
46760      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46761      *    This contains a reference to it.
46762      * @type {Roo.View}
46763      */
46764     view : false,
46765     
46766       /**
46767      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46768      * <pre><code>
46769
46770 layout.addxtype({
46771        xtype : 'Form',
46772        items: [ .... ]
46773    }
46774 );
46775
46776 </code></pre>
46777      * @param {Object} cfg Xtype definition of item to add.
46778      */
46779     
46780     addxtype : function(cfg) {
46781         // add form..
46782         if (cfg.xtype.match(/^Form$/)) {
46783             var el = this.el.createChild();
46784
46785             this.form = new  Roo.form.Form(cfg);
46786             
46787             
46788             if ( this.form.allItems.length) this.form.render(el.dom);
46789             return this.form;
46790         }
46791         // should only have one of theses..
46792         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46793             // views..
46794             cfg.el = this.el.appendChild(document.createElement("div"));
46795             // factory?
46796             
46797             var ret = new Roo.factory(cfg);
46798             ret.render && ret.render(false, ''); // render blank..
46799             this.view = ret;
46800             return ret;
46801         }
46802         return false;
46803     }
46804 });
46805
46806 /**
46807  * @class Roo.GridPanel
46808  * @extends Roo.ContentPanel
46809  * @constructor
46810  * Create a new GridPanel.
46811  * @param {Roo.grid.Grid} grid The grid for this panel
46812  * @param {String/Object} config A string to set only the panel's title, or a config object
46813  */
46814 Roo.GridPanel = function(grid, config){
46815     
46816   
46817     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46818         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46819         
46820     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46821     
46822     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46823     
46824     if(this.toolbar){
46825         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46826     }
46827     // xtype created footer. - not sure if will work as we normally have to render first..
46828     if (this.footer && !this.footer.el && this.footer.xtype) {
46829         
46830         this.footer.container = this.grid.getView().getFooterPanel(true);
46831         this.footer.dataSource = this.grid.dataSource;
46832         this.footer = Roo.factory(this.footer, Roo);
46833         
46834     }
46835     
46836     grid.monitorWindowResize = false; // turn off autosizing
46837     grid.autoHeight = false;
46838     grid.autoWidth = false;
46839     this.grid = grid;
46840     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46841 };
46842
46843 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46844     getId : function(){
46845         return this.grid.id;
46846     },
46847     
46848     /**
46849      * Returns the grid for this panel
46850      * @return {Roo.grid.Grid} 
46851      */
46852     getGrid : function(){
46853         return this.grid;    
46854     },
46855     
46856     setSize : function(width, height){
46857         if(!this.ignoreResize(width, height)){
46858             var grid = this.grid;
46859             var size = this.adjustForComponents(width, height);
46860             grid.getGridEl().setSize(size.width, size.height);
46861             grid.autoSize();
46862         }
46863     },
46864     
46865     beforeSlide : function(){
46866         this.grid.getView().scroller.clip();
46867     },
46868     
46869     afterSlide : function(){
46870         this.grid.getView().scroller.unclip();
46871     },
46872     
46873     destroy : function(){
46874         this.grid.destroy();
46875         delete this.grid;
46876         Roo.GridPanel.superclass.destroy.call(this); 
46877     }
46878 });
46879
46880
46881 /**
46882  * @class Roo.NestedLayoutPanel
46883  * @extends Roo.ContentPanel
46884  * @constructor
46885  * Create a new NestedLayoutPanel.
46886  * 
46887  * 
46888  * @param {Roo.BorderLayout} layout The layout for this panel
46889  * @param {String/Object} config A string to set only the title or a config object
46890  */
46891 Roo.NestedLayoutPanel = function(layout, config)
46892 {
46893     // construct with only one argument..
46894     /* FIXME - implement nicer consturctors
46895     if (layout.layout) {
46896         config = layout;
46897         layout = config.layout;
46898         delete config.layout;
46899     }
46900     if (layout.xtype && !layout.getEl) {
46901         // then layout needs constructing..
46902         layout = Roo.factory(layout, Roo);
46903     }
46904     */
46905     
46906     
46907     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46908     
46909     layout.monitorWindowResize = false; // turn off autosizing
46910     this.layout = layout;
46911     this.layout.getEl().addClass("x-layout-nested-layout");
46912     
46913     
46914     
46915     
46916 };
46917
46918 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46919
46920     setSize : function(width, height){
46921         if(!this.ignoreResize(width, height)){
46922             var size = this.adjustForComponents(width, height);
46923             var el = this.layout.getEl();
46924             el.setSize(size.width, size.height);
46925             var touch = el.dom.offsetWidth;
46926             this.layout.layout();
46927             // ie requires a double layout on the first pass
46928             if(Roo.isIE && !this.initialized){
46929                 this.initialized = true;
46930                 this.layout.layout();
46931             }
46932         }
46933     },
46934     
46935     // activate all subpanels if not currently active..
46936     
46937     setActiveState : function(active){
46938         this.active = active;
46939         if(!active){
46940             this.fireEvent("deactivate", this);
46941             return;
46942         }
46943         
46944         this.fireEvent("activate", this);
46945         // not sure if this should happen before or after..
46946         if (!this.layout) {
46947             return; // should not happen..
46948         }
46949         var reg = false;
46950         for (var r in this.layout.regions) {
46951             reg = this.layout.getRegion(r);
46952             if (reg.getActivePanel()) {
46953                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46954                 reg.setActivePanel(reg.getActivePanel());
46955                 continue;
46956             }
46957             if (!reg.panels.length) {
46958                 continue;
46959             }
46960             reg.showPanel(reg.getPanel(0));
46961         }
46962         
46963         
46964         
46965         
46966     },
46967     
46968     /**
46969      * Returns the nested BorderLayout for this panel
46970      * @return {Roo.BorderLayout} 
46971      */
46972     getLayout : function(){
46973         return this.layout;
46974     },
46975     
46976      /**
46977      * Adds a xtype elements to the layout of the nested panel
46978      * <pre><code>
46979
46980 panel.addxtype({
46981        xtype : 'ContentPanel',
46982        region: 'west',
46983        items: [ .... ]
46984    }
46985 );
46986
46987 panel.addxtype({
46988         xtype : 'NestedLayoutPanel',
46989         region: 'west',
46990         layout: {
46991            center: { },
46992            west: { }   
46993         },
46994         items : [ ... list of content panels or nested layout panels.. ]
46995    }
46996 );
46997 </code></pre>
46998      * @param {Object} cfg Xtype definition of item to add.
46999      */
47000     addxtype : function(cfg) {
47001         return this.layout.addxtype(cfg);
47002     
47003     }
47004 });
47005
47006 Roo.ScrollPanel = function(el, config, content){
47007     config = config || {};
47008     config.fitToFrame = true;
47009     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47010     
47011     this.el.dom.style.overflow = "hidden";
47012     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47013     this.el.removeClass("x-layout-inactive-content");
47014     this.el.on("mousewheel", this.onWheel, this);
47015
47016     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47017     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47018     up.unselectable(); down.unselectable();
47019     up.on("click", this.scrollUp, this);
47020     down.on("click", this.scrollDown, this);
47021     up.addClassOnOver("x-scroller-btn-over");
47022     down.addClassOnOver("x-scroller-btn-over");
47023     up.addClassOnClick("x-scroller-btn-click");
47024     down.addClassOnClick("x-scroller-btn-click");
47025     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47026
47027     this.resizeEl = this.el;
47028     this.el = wrap; this.up = up; this.down = down;
47029 };
47030
47031 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47032     increment : 100,
47033     wheelIncrement : 5,
47034     scrollUp : function(){
47035         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47036     },
47037
47038     scrollDown : function(){
47039         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47040     },
47041
47042     afterScroll : function(){
47043         var el = this.resizeEl;
47044         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47045         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47046         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47047     },
47048
47049     setSize : function(){
47050         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47051         this.afterScroll();
47052     },
47053
47054     onWheel : function(e){
47055         var d = e.getWheelDelta();
47056         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47057         this.afterScroll();
47058         e.stopEvent();
47059     },
47060
47061     setContent : function(content, loadScripts){
47062         this.resizeEl.update(content, loadScripts);
47063     }
47064
47065 });
47066
47067
47068
47069
47070
47071
47072
47073
47074
47075 /**
47076  * @class Roo.TreePanel
47077  * @extends Roo.ContentPanel
47078  * @constructor
47079  * Create a new TreePanel. - defaults to fit/scoll contents.
47080  * @param {String/Object} config A string to set only the panel's title, or a config object
47081  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47082  */
47083 Roo.TreePanel = function(config){
47084     var el = config.el;
47085     var tree = config.tree;
47086     delete config.tree; 
47087     delete config.el; // hopefull!
47088     
47089     // wrapper for IE7 strict & safari scroll issue
47090     
47091     var treeEl = el.createChild();
47092     config.resizeEl = treeEl;
47093     
47094     
47095     
47096     Roo.TreePanel.superclass.constructor.call(this, el, config);
47097  
47098  
47099     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47100     //console.log(tree);
47101     this.on('activate', function()
47102     {
47103         if (this.tree.rendered) {
47104             return;
47105         }
47106         //console.log('render tree');
47107         this.tree.render();
47108     });
47109     
47110     this.on('resize',  function (cp, w, h) {
47111             this.tree.innerCt.setWidth(w);
47112             this.tree.innerCt.setHeight(h);
47113             this.tree.innerCt.setStyle('overflow-y', 'auto');
47114     });
47115
47116         
47117     
47118 };
47119
47120 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47121     fitToFrame : true,
47122     autoScroll : true
47123 });
47124
47125
47126
47127
47128
47129
47130
47131
47132
47133
47134
47135 /*
47136  * Based on:
47137  * Ext JS Library 1.1.1
47138  * Copyright(c) 2006-2007, Ext JS, LLC.
47139  *
47140  * Originally Released Under LGPL - original licence link has changed is not relivant.
47141  *
47142  * Fork - LGPL
47143  * <script type="text/javascript">
47144  */
47145  
47146
47147 /**
47148  * @class Roo.ReaderLayout
47149  * @extends Roo.BorderLayout
47150  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47151  * center region containing two nested regions (a top one for a list view and one for item preview below),
47152  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47153  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47154  * expedites the setup of the overall layout and regions for this common application style.
47155  * Example:
47156  <pre><code>
47157 var reader = new Roo.ReaderLayout();
47158 var CP = Roo.ContentPanel;  // shortcut for adding
47159
47160 reader.beginUpdate();
47161 reader.add("north", new CP("north", "North"));
47162 reader.add("west", new CP("west", {title: "West"}));
47163 reader.add("east", new CP("east", {title: "East"}));
47164
47165 reader.regions.listView.add(new CP("listView", "List"));
47166 reader.regions.preview.add(new CP("preview", "Preview"));
47167 reader.endUpdate();
47168 </code></pre>
47169 * @constructor
47170 * Create a new ReaderLayout
47171 * @param {Object} config Configuration options
47172 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47173 * document.body if omitted)
47174 */
47175 Roo.ReaderLayout = function(config, renderTo){
47176     var c = config || {size:{}};
47177     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47178         north: c.north !== false ? Roo.apply({
47179             split:false,
47180             initialSize: 32,
47181             titlebar: false
47182         }, c.north) : false,
47183         west: c.west !== false ? Roo.apply({
47184             split:true,
47185             initialSize: 200,
47186             minSize: 175,
47187             maxSize: 400,
47188             titlebar: true,
47189             collapsible: true,
47190             animate: true,
47191             margins:{left:5,right:0,bottom:5,top:5},
47192             cmargins:{left:5,right:5,bottom:5,top:5}
47193         }, c.west) : false,
47194         east: c.east !== false ? Roo.apply({
47195             split:true,
47196             initialSize: 200,
47197             minSize: 175,
47198             maxSize: 400,
47199             titlebar: true,
47200             collapsible: true,
47201             animate: true,
47202             margins:{left:0,right:5,bottom:5,top:5},
47203             cmargins:{left:5,right:5,bottom:5,top:5}
47204         }, c.east) : false,
47205         center: Roo.apply({
47206             tabPosition: 'top',
47207             autoScroll:false,
47208             closeOnTab: true,
47209             titlebar:false,
47210             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47211         }, c.center)
47212     });
47213
47214     this.el.addClass('x-reader');
47215
47216     this.beginUpdate();
47217
47218     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47219         south: c.preview !== false ? Roo.apply({
47220             split:true,
47221             initialSize: 200,
47222             minSize: 100,
47223             autoScroll:true,
47224             collapsible:true,
47225             titlebar: true,
47226             cmargins:{top:5,left:0, right:0, bottom:0}
47227         }, c.preview) : false,
47228         center: Roo.apply({
47229             autoScroll:false,
47230             titlebar:false,
47231             minHeight:200
47232         }, c.listView)
47233     });
47234     this.add('center', new Roo.NestedLayoutPanel(inner,
47235             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47236
47237     this.endUpdate();
47238
47239     this.regions.preview = inner.getRegion('south');
47240     this.regions.listView = inner.getRegion('center');
47241 };
47242
47243 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47244  * Based on:
47245  * Ext JS Library 1.1.1
47246  * Copyright(c) 2006-2007, Ext JS, LLC.
47247  *
47248  * Originally Released Under LGPL - original licence link has changed is not relivant.
47249  *
47250  * Fork - LGPL
47251  * <script type="text/javascript">
47252  */
47253  
47254 /**
47255  * @class Roo.grid.Grid
47256  * @extends Roo.util.Observable
47257  * This class represents the primary interface of a component based grid control.
47258  * <br><br>Usage:<pre><code>
47259  var grid = new Roo.grid.Grid("my-container-id", {
47260      ds: myDataStore,
47261      cm: myColModel,
47262      selModel: mySelectionModel,
47263      autoSizeColumns: true,
47264      monitorWindowResize: false,
47265      trackMouseOver: true
47266  });
47267  // set any options
47268  grid.render();
47269  * </code></pre>
47270  * <b>Common Problems:</b><br/>
47271  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47272  * element will correct this<br/>
47273  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47274  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47275  * are unpredictable.<br/>
47276  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47277  * grid to calculate dimensions/offsets.<br/>
47278   * @constructor
47279  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47280  * The container MUST have some type of size defined for the grid to fill. The container will be
47281  * automatically set to position relative if it isn't already.
47282  * @param {Object} config A config object that sets properties on this grid.
47283  */
47284 Roo.grid.Grid = function(container, config){
47285         // initialize the container
47286         this.container = Roo.get(container);
47287         this.container.update("");
47288         this.container.setStyle("overflow", "hidden");
47289     this.container.addClass('x-grid-container');
47290
47291     this.id = this.container.id;
47292
47293     Roo.apply(this, config);
47294     // check and correct shorthanded configs
47295     if(this.ds){
47296         this.dataSource = this.ds;
47297         delete this.ds;
47298     }
47299     if(this.cm){
47300         this.colModel = this.cm;
47301         delete this.cm;
47302     }
47303     if(this.sm){
47304         this.selModel = this.sm;
47305         delete this.sm;
47306     }
47307
47308     if (this.selModel) {
47309         this.selModel = Roo.factory(this.selModel, Roo.grid);
47310         this.sm = this.selModel;
47311         this.sm.xmodule = this.xmodule || false;
47312     }
47313     if (typeof(this.colModel.config) == 'undefined') {
47314         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47315         this.cm = this.colModel;
47316         this.cm.xmodule = this.xmodule || false;
47317     }
47318     if (this.dataSource) {
47319         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47320         this.ds = this.dataSource;
47321         this.ds.xmodule = this.xmodule || false;
47322          
47323     }
47324     
47325     
47326     
47327     if(this.width){
47328         this.container.setWidth(this.width);
47329     }
47330
47331     if(this.height){
47332         this.container.setHeight(this.height);
47333     }
47334     /** @private */
47335         this.addEvents({
47336         // raw events
47337         /**
47338          * @event click
47339          * The raw click event for the entire grid.
47340          * @param {Roo.EventObject} e
47341          */
47342         "click" : true,
47343         /**
47344          * @event dblclick
47345          * The raw dblclick event for the entire grid.
47346          * @param {Roo.EventObject} e
47347          */
47348         "dblclick" : true,
47349         /**
47350          * @event contextmenu
47351          * The raw contextmenu event for the entire grid.
47352          * @param {Roo.EventObject} e
47353          */
47354         "contextmenu" : true,
47355         /**
47356          * @event mousedown
47357          * The raw mousedown event for the entire grid.
47358          * @param {Roo.EventObject} e
47359          */
47360         "mousedown" : true,
47361         /**
47362          * @event mouseup
47363          * The raw mouseup event for the entire grid.
47364          * @param {Roo.EventObject} e
47365          */
47366         "mouseup" : true,
47367         /**
47368          * @event mouseover
47369          * The raw mouseover event for the entire grid.
47370          * @param {Roo.EventObject} e
47371          */
47372         "mouseover" : true,
47373         /**
47374          * @event mouseout
47375          * The raw mouseout event for the entire grid.
47376          * @param {Roo.EventObject} e
47377          */
47378         "mouseout" : true,
47379         /**
47380          * @event keypress
47381          * The raw keypress event for the entire grid.
47382          * @param {Roo.EventObject} e
47383          */
47384         "keypress" : true,
47385         /**
47386          * @event keydown
47387          * The raw keydown event for the entire grid.
47388          * @param {Roo.EventObject} e
47389          */
47390         "keydown" : true,
47391
47392         // custom events
47393
47394         /**
47395          * @event cellclick
47396          * Fires when a cell is clicked
47397          * @param {Grid} this
47398          * @param {Number} rowIndex
47399          * @param {Number} columnIndex
47400          * @param {Roo.EventObject} e
47401          */
47402         "cellclick" : true,
47403         /**
47404          * @event celldblclick
47405          * Fires when a cell is double clicked
47406          * @param {Grid} this
47407          * @param {Number} rowIndex
47408          * @param {Number} columnIndex
47409          * @param {Roo.EventObject} e
47410          */
47411         "celldblclick" : true,
47412         /**
47413          * @event rowclick
47414          * Fires when a row is clicked
47415          * @param {Grid} this
47416          * @param {Number} rowIndex
47417          * @param {Roo.EventObject} e
47418          */
47419         "rowclick" : true,
47420         /**
47421          * @event rowdblclick
47422          * Fires when a row is double clicked
47423          * @param {Grid} this
47424          * @param {Number} rowIndex
47425          * @param {Roo.EventObject} e
47426          */
47427         "rowdblclick" : true,
47428         /**
47429          * @event headerclick
47430          * Fires when a header is clicked
47431          * @param {Grid} this
47432          * @param {Number} columnIndex
47433          * @param {Roo.EventObject} e
47434          */
47435         "headerclick" : true,
47436         /**
47437          * @event headerdblclick
47438          * Fires when a header cell is double clicked
47439          * @param {Grid} this
47440          * @param {Number} columnIndex
47441          * @param {Roo.EventObject} e
47442          */
47443         "headerdblclick" : true,
47444         /**
47445          * @event rowcontextmenu
47446          * Fires when a row is right clicked
47447          * @param {Grid} this
47448          * @param {Number} rowIndex
47449          * @param {Roo.EventObject} e
47450          */
47451         "rowcontextmenu" : true,
47452         /**
47453          * @event cellcontextmenu
47454          * Fires when a cell is right clicked
47455          * @param {Grid} this
47456          * @param {Number} rowIndex
47457          * @param {Number} cellIndex
47458          * @param {Roo.EventObject} e
47459          */
47460          "cellcontextmenu" : true,
47461         /**
47462          * @event headercontextmenu
47463          * Fires when a header is right clicked
47464          * @param {Grid} this
47465          * @param {Number} columnIndex
47466          * @param {Roo.EventObject} e
47467          */
47468         "headercontextmenu" : true,
47469         /**
47470          * @event bodyscroll
47471          * Fires when the body element is scrolled
47472          * @param {Number} scrollLeft
47473          * @param {Number} scrollTop
47474          */
47475         "bodyscroll" : true,
47476         /**
47477          * @event columnresize
47478          * Fires when the user resizes a column
47479          * @param {Number} columnIndex
47480          * @param {Number} newSize
47481          */
47482         "columnresize" : true,
47483         /**
47484          * @event columnmove
47485          * Fires when the user moves a column
47486          * @param {Number} oldIndex
47487          * @param {Number} newIndex
47488          */
47489         "columnmove" : true,
47490         /**
47491          * @event startdrag
47492          * Fires when row(s) start being dragged
47493          * @param {Grid} this
47494          * @param {Roo.GridDD} dd The drag drop object
47495          * @param {event} e The raw browser event
47496          */
47497         "startdrag" : true,
47498         /**
47499          * @event enddrag
47500          * Fires when a drag operation is complete
47501          * @param {Grid} this
47502          * @param {Roo.GridDD} dd The drag drop object
47503          * @param {event} e The raw browser event
47504          */
47505         "enddrag" : true,
47506         /**
47507          * @event dragdrop
47508          * Fires when dragged row(s) are dropped on a valid DD target
47509          * @param {Grid} this
47510          * @param {Roo.GridDD} dd The drag drop object
47511          * @param {String} targetId The target drag drop object
47512          * @param {event} e The raw browser event
47513          */
47514         "dragdrop" : true,
47515         /**
47516          * @event dragover
47517          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47518          * @param {Grid} this
47519          * @param {Roo.GridDD} dd The drag drop object
47520          * @param {String} targetId The target drag drop object
47521          * @param {event} e The raw browser event
47522          */
47523         "dragover" : true,
47524         /**
47525          * @event dragenter
47526          *  Fires when the dragged row(s) first cross another DD target while being dragged
47527          * @param {Grid} this
47528          * @param {Roo.GridDD} dd The drag drop object
47529          * @param {String} targetId The target drag drop object
47530          * @param {event} e The raw browser event
47531          */
47532         "dragenter" : true,
47533         /**
47534          * @event dragout
47535          * Fires when the dragged row(s) leave another DD target while being dragged
47536          * @param {Grid} this
47537          * @param {Roo.GridDD} dd The drag drop object
47538          * @param {String} targetId The target drag drop object
47539          * @param {event} e The raw browser event
47540          */
47541         "dragout" : true,
47542         /**
47543          * @event rowclass
47544          * Fires when a row is rendered, so you can change add a style to it.
47545          * @param {GridView} gridview   The grid view
47546          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47547          */
47548         'rowclass' : true,
47549
47550         /**
47551          * @event render
47552          * Fires when the grid is rendered
47553          * @param {Grid} grid
47554          */
47555         'render' : true
47556     });
47557
47558     Roo.grid.Grid.superclass.constructor.call(this);
47559 };
47560 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47561     
47562     /**
47563      * @cfg {String} ddGroup - drag drop group.
47564      */
47565
47566     /**
47567      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47568      */
47569     minColumnWidth : 25,
47570
47571     /**
47572      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47573      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47574      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47575      */
47576     autoSizeColumns : false,
47577
47578     /**
47579      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47580      */
47581     autoSizeHeaders : true,
47582
47583     /**
47584      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47585      */
47586     monitorWindowResize : true,
47587
47588     /**
47589      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47590      * rows measured to get a columns size. Default is 0 (all rows).
47591      */
47592     maxRowsToMeasure : 0,
47593
47594     /**
47595      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47596      */
47597     trackMouseOver : true,
47598
47599     /**
47600     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47601     */
47602     
47603     /**
47604     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47605     */
47606     enableDragDrop : false,
47607     
47608     /**
47609     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47610     */
47611     enableColumnMove : true,
47612     
47613     /**
47614     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47615     */
47616     enableColumnHide : true,
47617     
47618     /**
47619     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47620     */
47621     enableRowHeightSync : false,
47622     
47623     /**
47624     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47625     */
47626     stripeRows : true,
47627     
47628     /**
47629     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47630     */
47631     autoHeight : false,
47632
47633     /**
47634      * @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.
47635      */
47636     autoExpandColumn : false,
47637
47638     /**
47639     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47640     * Default is 50.
47641     */
47642     autoExpandMin : 50,
47643
47644     /**
47645     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47646     */
47647     autoExpandMax : 1000,
47648
47649     /**
47650     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47651     */
47652     view : null,
47653
47654     /**
47655     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47656     */
47657     loadMask : false,
47658     /**
47659     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47660     */
47661     dropTarget: false,
47662     
47663    
47664     
47665     // private
47666     rendered : false,
47667
47668     /**
47669     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47670     * of a fixed width. Default is false.
47671     */
47672     /**
47673     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47674     */
47675     /**
47676      * Called once after all setup has been completed and the grid is ready to be rendered.
47677      * @return {Roo.grid.Grid} this
47678      */
47679     render : function()
47680     {
47681         var c = this.container;
47682         // try to detect autoHeight/width mode
47683         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47684             this.autoHeight = true;
47685         }
47686         var view = this.getView();
47687         view.init(this);
47688
47689         c.on("click", this.onClick, this);
47690         c.on("dblclick", this.onDblClick, this);
47691         c.on("contextmenu", this.onContextMenu, this);
47692         c.on("keydown", this.onKeyDown, this);
47693
47694         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47695
47696         this.getSelectionModel().init(this);
47697
47698         view.render();
47699
47700         if(this.loadMask){
47701             this.loadMask = new Roo.LoadMask(this.container,
47702                     Roo.apply({store:this.dataSource}, this.loadMask));
47703         }
47704         
47705         
47706         if (this.toolbar && this.toolbar.xtype) {
47707             this.toolbar.container = this.getView().getHeaderPanel(true);
47708             this.toolbar = new Roo.Toolbar(this.toolbar);
47709         }
47710         if (this.footer && this.footer.xtype) {
47711             this.footer.dataSource = this.getDataSource();
47712             this.footer.container = this.getView().getFooterPanel(true);
47713             this.footer = Roo.factory(this.footer, Roo);
47714         }
47715         if (this.dropTarget && this.dropTarget.xtype) {
47716             delete this.dropTarget.xtype;
47717             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47718         }
47719         
47720         
47721         this.rendered = true;
47722         this.fireEvent('render', this);
47723         return this;
47724     },
47725
47726         /**
47727          * Reconfigures the grid to use a different Store and Column Model.
47728          * The View will be bound to the new objects and refreshed.
47729          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47730          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47731          */
47732     reconfigure : function(dataSource, colModel){
47733         if(this.loadMask){
47734             this.loadMask.destroy();
47735             this.loadMask = new Roo.LoadMask(this.container,
47736                     Roo.apply({store:dataSource}, this.loadMask));
47737         }
47738         this.view.bind(dataSource, colModel);
47739         this.dataSource = dataSource;
47740         this.colModel = colModel;
47741         this.view.refresh(true);
47742     },
47743
47744     // private
47745     onKeyDown : function(e){
47746         this.fireEvent("keydown", e);
47747     },
47748
47749     /**
47750      * Destroy this grid.
47751      * @param {Boolean} removeEl True to remove the element
47752      */
47753     destroy : function(removeEl, keepListeners){
47754         if(this.loadMask){
47755             this.loadMask.destroy();
47756         }
47757         var c = this.container;
47758         c.removeAllListeners();
47759         this.view.destroy();
47760         this.colModel.purgeListeners();
47761         if(!keepListeners){
47762             this.purgeListeners();
47763         }
47764         c.update("");
47765         if(removeEl === true){
47766             c.remove();
47767         }
47768     },
47769
47770     // private
47771     processEvent : function(name, e){
47772         this.fireEvent(name, e);
47773         var t = e.getTarget();
47774         var v = this.view;
47775         var header = v.findHeaderIndex(t);
47776         if(header !== false){
47777             this.fireEvent("header" + name, this, header, e);
47778         }else{
47779             var row = v.findRowIndex(t);
47780             var cell = v.findCellIndex(t);
47781             if(row !== false){
47782                 this.fireEvent("row" + name, this, row, e);
47783                 if(cell !== false){
47784                     this.fireEvent("cell" + name, this, row, cell, e);
47785                 }
47786             }
47787         }
47788     },
47789
47790     // private
47791     onClick : function(e){
47792         this.processEvent("click", e);
47793     },
47794
47795     // private
47796     onContextMenu : function(e, t){
47797         this.processEvent("contextmenu", e);
47798     },
47799
47800     // private
47801     onDblClick : function(e){
47802         this.processEvent("dblclick", e);
47803     },
47804
47805     // private
47806     walkCells : function(row, col, step, fn, scope){
47807         var cm = this.colModel, clen = cm.getColumnCount();
47808         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47809         if(step < 0){
47810             if(col < 0){
47811                 row--;
47812                 first = false;
47813             }
47814             while(row >= 0){
47815                 if(!first){
47816                     col = clen-1;
47817                 }
47818                 first = false;
47819                 while(col >= 0){
47820                     if(fn.call(scope || this, row, col, cm) === true){
47821                         return [row, col];
47822                     }
47823                     col--;
47824                 }
47825                 row--;
47826             }
47827         } else {
47828             if(col >= clen){
47829                 row++;
47830                 first = false;
47831             }
47832             while(row < rlen){
47833                 if(!first){
47834                     col = 0;
47835                 }
47836                 first = false;
47837                 while(col < clen){
47838                     if(fn.call(scope || this, row, col, cm) === true){
47839                         return [row, col];
47840                     }
47841                     col++;
47842                 }
47843                 row++;
47844             }
47845         }
47846         return null;
47847     },
47848
47849     // private
47850     getSelections : function(){
47851         return this.selModel.getSelections();
47852     },
47853
47854     /**
47855      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47856      * but if manual update is required this method will initiate it.
47857      */
47858     autoSize : function(){
47859         if(this.rendered){
47860             this.view.layout();
47861             if(this.view.adjustForScroll){
47862                 this.view.adjustForScroll();
47863             }
47864         }
47865     },
47866
47867     /**
47868      * Returns the grid's underlying element.
47869      * @return {Element} The element
47870      */
47871     getGridEl : function(){
47872         return this.container;
47873     },
47874
47875     // private for compatibility, overridden by editor grid
47876     stopEditing : function(){},
47877
47878     /**
47879      * Returns the grid's SelectionModel.
47880      * @return {SelectionModel}
47881      */
47882     getSelectionModel : function(){
47883         if(!this.selModel){
47884             this.selModel = new Roo.grid.RowSelectionModel();
47885         }
47886         return this.selModel;
47887     },
47888
47889     /**
47890      * Returns the grid's DataSource.
47891      * @return {DataSource}
47892      */
47893     getDataSource : function(){
47894         return this.dataSource;
47895     },
47896
47897     /**
47898      * Returns the grid's ColumnModel.
47899      * @return {ColumnModel}
47900      */
47901     getColumnModel : function(){
47902         return this.colModel;
47903     },
47904
47905     /**
47906      * Returns the grid's GridView object.
47907      * @return {GridView}
47908      */
47909     getView : function(){
47910         if(!this.view){
47911             this.view = new Roo.grid.GridView(this.viewConfig);
47912         }
47913         return this.view;
47914     },
47915     /**
47916      * Called to get grid's drag proxy text, by default returns this.ddText.
47917      * @return {String}
47918      */
47919     getDragDropText : function(){
47920         var count = this.selModel.getCount();
47921         return String.format(this.ddText, count, count == 1 ? '' : 's');
47922     }
47923 });
47924 /**
47925  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47926  * %0 is replaced with the number of selected rows.
47927  * @type String
47928  */
47929 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47930  * Based on:
47931  * Ext JS Library 1.1.1
47932  * Copyright(c) 2006-2007, Ext JS, LLC.
47933  *
47934  * Originally Released Under LGPL - original licence link has changed is not relivant.
47935  *
47936  * Fork - LGPL
47937  * <script type="text/javascript">
47938  */
47939  
47940 Roo.grid.AbstractGridView = function(){
47941         this.grid = null;
47942         
47943         this.events = {
47944             "beforerowremoved" : true,
47945             "beforerowsinserted" : true,
47946             "beforerefresh" : true,
47947             "rowremoved" : true,
47948             "rowsinserted" : true,
47949             "rowupdated" : true,
47950             "refresh" : true
47951         };
47952     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47953 };
47954
47955 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47956     rowClass : "x-grid-row",
47957     cellClass : "x-grid-cell",
47958     tdClass : "x-grid-td",
47959     hdClass : "x-grid-hd",
47960     splitClass : "x-grid-hd-split",
47961     
47962         init: function(grid){
47963         this.grid = grid;
47964                 var cid = this.grid.getGridEl().id;
47965         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47966         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47967         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47968         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47969         },
47970         
47971         getColumnRenderers : function(){
47972         var renderers = [];
47973         var cm = this.grid.colModel;
47974         var colCount = cm.getColumnCount();
47975         for(var i = 0; i < colCount; i++){
47976             renderers[i] = cm.getRenderer(i);
47977         }
47978         return renderers;
47979     },
47980     
47981     getColumnIds : function(){
47982         var ids = [];
47983         var cm = this.grid.colModel;
47984         var colCount = cm.getColumnCount();
47985         for(var i = 0; i < colCount; i++){
47986             ids[i] = cm.getColumnId(i);
47987         }
47988         return ids;
47989     },
47990     
47991     getDataIndexes : function(){
47992         if(!this.indexMap){
47993             this.indexMap = this.buildIndexMap();
47994         }
47995         return this.indexMap.colToData;
47996     },
47997     
47998     getColumnIndexByDataIndex : function(dataIndex){
47999         if(!this.indexMap){
48000             this.indexMap = this.buildIndexMap();
48001         }
48002         return this.indexMap.dataToCol[dataIndex];
48003     },
48004     
48005     /**
48006      * Set a css style for a column dynamically. 
48007      * @param {Number} colIndex The index of the column
48008      * @param {String} name The css property name
48009      * @param {String} value The css value
48010      */
48011     setCSSStyle : function(colIndex, name, value){
48012         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48013         Roo.util.CSS.updateRule(selector, name, value);
48014     },
48015     
48016     generateRules : function(cm){
48017         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48018         Roo.util.CSS.removeStyleSheet(rulesId);
48019         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48020             var cid = cm.getColumnId(i);
48021             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48022                          this.tdSelector, cid, " {\n}\n",
48023                          this.hdSelector, cid, " {\n}\n",
48024                          this.splitSelector, cid, " {\n}\n");
48025         }
48026         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48027     }
48028 });/*
48029  * Based on:
48030  * Ext JS Library 1.1.1
48031  * Copyright(c) 2006-2007, Ext JS, LLC.
48032  *
48033  * Originally Released Under LGPL - original licence link has changed is not relivant.
48034  *
48035  * Fork - LGPL
48036  * <script type="text/javascript">
48037  */
48038
48039 // private
48040 // This is a support class used internally by the Grid components
48041 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48042     this.grid = grid;
48043     this.view = grid.getView();
48044     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48045     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48046     if(hd2){
48047         this.setHandleElId(Roo.id(hd));
48048         this.setOuterHandleElId(Roo.id(hd2));
48049     }
48050     this.scroll = false;
48051 };
48052 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48053     maxDragWidth: 120,
48054     getDragData : function(e){
48055         var t = Roo.lib.Event.getTarget(e);
48056         var h = this.view.findHeaderCell(t);
48057         if(h){
48058             return {ddel: h.firstChild, header:h};
48059         }
48060         return false;
48061     },
48062
48063     onInitDrag : function(e){
48064         this.view.headersDisabled = true;
48065         var clone = this.dragData.ddel.cloneNode(true);
48066         clone.id = Roo.id();
48067         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48068         this.proxy.update(clone);
48069         return true;
48070     },
48071
48072     afterValidDrop : function(){
48073         var v = this.view;
48074         setTimeout(function(){
48075             v.headersDisabled = false;
48076         }, 50);
48077     },
48078
48079     afterInvalidDrop : function(){
48080         var v = this.view;
48081         setTimeout(function(){
48082             v.headersDisabled = false;
48083         }, 50);
48084     }
48085 });
48086 /*
48087  * Based on:
48088  * Ext JS Library 1.1.1
48089  * Copyright(c) 2006-2007, Ext JS, LLC.
48090  *
48091  * Originally Released Under LGPL - original licence link has changed is not relivant.
48092  *
48093  * Fork - LGPL
48094  * <script type="text/javascript">
48095  */
48096 // private
48097 // This is a support class used internally by the Grid components
48098 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48099     this.grid = grid;
48100     this.view = grid.getView();
48101     // split the proxies so they don't interfere with mouse events
48102     this.proxyTop = Roo.DomHelper.append(document.body, {
48103         cls:"col-move-top", html:"&#160;"
48104     }, true);
48105     this.proxyBottom = Roo.DomHelper.append(document.body, {
48106         cls:"col-move-bottom", html:"&#160;"
48107     }, true);
48108     this.proxyTop.hide = this.proxyBottom.hide = function(){
48109         this.setLeftTop(-100,-100);
48110         this.setStyle("visibility", "hidden");
48111     };
48112     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48113     // temporarily disabled
48114     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48115     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48116 };
48117 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48118     proxyOffsets : [-4, -9],
48119     fly: Roo.Element.fly,
48120
48121     getTargetFromEvent : function(e){
48122         var t = Roo.lib.Event.getTarget(e);
48123         var cindex = this.view.findCellIndex(t);
48124         if(cindex !== false){
48125             return this.view.getHeaderCell(cindex);
48126         }
48127         return null;
48128     },
48129
48130     nextVisible : function(h){
48131         var v = this.view, cm = this.grid.colModel;
48132         h = h.nextSibling;
48133         while(h){
48134             if(!cm.isHidden(v.getCellIndex(h))){
48135                 return h;
48136             }
48137             h = h.nextSibling;
48138         }
48139         return null;
48140     },
48141
48142     prevVisible : function(h){
48143         var v = this.view, cm = this.grid.colModel;
48144         h = h.prevSibling;
48145         while(h){
48146             if(!cm.isHidden(v.getCellIndex(h))){
48147                 return h;
48148             }
48149             h = h.prevSibling;
48150         }
48151         return null;
48152     },
48153
48154     positionIndicator : function(h, n, e){
48155         var x = Roo.lib.Event.getPageX(e);
48156         var r = Roo.lib.Dom.getRegion(n.firstChild);
48157         var px, pt, py = r.top + this.proxyOffsets[1];
48158         if((r.right - x) <= (r.right-r.left)/2){
48159             px = r.right+this.view.borderWidth;
48160             pt = "after";
48161         }else{
48162             px = r.left;
48163             pt = "before";
48164         }
48165         var oldIndex = this.view.getCellIndex(h);
48166         var newIndex = this.view.getCellIndex(n);
48167
48168         if(this.grid.colModel.isFixed(newIndex)){
48169             return false;
48170         }
48171
48172         var locked = this.grid.colModel.isLocked(newIndex);
48173
48174         if(pt == "after"){
48175             newIndex++;
48176         }
48177         if(oldIndex < newIndex){
48178             newIndex--;
48179         }
48180         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48181             return false;
48182         }
48183         px +=  this.proxyOffsets[0];
48184         this.proxyTop.setLeftTop(px, py);
48185         this.proxyTop.show();
48186         if(!this.bottomOffset){
48187             this.bottomOffset = this.view.mainHd.getHeight();
48188         }
48189         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48190         this.proxyBottom.show();
48191         return pt;
48192     },
48193
48194     onNodeEnter : function(n, dd, e, data){
48195         if(data.header != n){
48196             this.positionIndicator(data.header, n, e);
48197         }
48198     },
48199
48200     onNodeOver : function(n, dd, e, data){
48201         var result = false;
48202         if(data.header != n){
48203             result = this.positionIndicator(data.header, n, e);
48204         }
48205         if(!result){
48206             this.proxyTop.hide();
48207             this.proxyBottom.hide();
48208         }
48209         return result ? this.dropAllowed : this.dropNotAllowed;
48210     },
48211
48212     onNodeOut : function(n, dd, e, data){
48213         this.proxyTop.hide();
48214         this.proxyBottom.hide();
48215     },
48216
48217     onNodeDrop : function(n, dd, e, data){
48218         var h = data.header;
48219         if(h != n){
48220             var cm = this.grid.colModel;
48221             var x = Roo.lib.Event.getPageX(e);
48222             var r = Roo.lib.Dom.getRegion(n.firstChild);
48223             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48224             var oldIndex = this.view.getCellIndex(h);
48225             var newIndex = this.view.getCellIndex(n);
48226             var locked = cm.isLocked(newIndex);
48227             if(pt == "after"){
48228                 newIndex++;
48229             }
48230             if(oldIndex < newIndex){
48231                 newIndex--;
48232             }
48233             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48234                 return false;
48235             }
48236             cm.setLocked(oldIndex, locked, true);
48237             cm.moveColumn(oldIndex, newIndex);
48238             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48239             return true;
48240         }
48241         return false;
48242     }
48243 });
48244 /*
48245  * Based on:
48246  * Ext JS Library 1.1.1
48247  * Copyright(c) 2006-2007, Ext JS, LLC.
48248  *
48249  * Originally Released Under LGPL - original licence link has changed is not relivant.
48250  *
48251  * Fork - LGPL
48252  * <script type="text/javascript">
48253  */
48254   
48255 /**
48256  * @class Roo.grid.GridView
48257  * @extends Roo.util.Observable
48258  *
48259  * @constructor
48260  * @param {Object} config
48261  */
48262 Roo.grid.GridView = function(config){
48263     Roo.grid.GridView.superclass.constructor.call(this);
48264     this.el = null;
48265
48266     Roo.apply(this, config);
48267 };
48268
48269 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48270
48271     /**
48272      * Override this function to apply custom css classes to rows during rendering
48273      * @param {Record} record The record
48274      * @param {Number} index
48275      * @method getRowClass
48276      */
48277     rowClass : "x-grid-row",
48278
48279     cellClass : "x-grid-col",
48280
48281     tdClass : "x-grid-td",
48282
48283     hdClass : "x-grid-hd",
48284
48285     splitClass : "x-grid-split",
48286
48287     sortClasses : ["sort-asc", "sort-desc"],
48288
48289     enableMoveAnim : false,
48290
48291     hlColor: "C3DAF9",
48292
48293     dh : Roo.DomHelper,
48294
48295     fly : Roo.Element.fly,
48296
48297     css : Roo.util.CSS,
48298
48299     borderWidth: 1,
48300
48301     splitOffset: 3,
48302
48303     scrollIncrement : 22,
48304
48305     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48306
48307     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48308
48309     bind : function(ds, cm){
48310         if(this.ds){
48311             this.ds.un("load", this.onLoad, this);
48312             this.ds.un("datachanged", this.onDataChange, this);
48313             this.ds.un("add", this.onAdd, this);
48314             this.ds.un("remove", this.onRemove, this);
48315             this.ds.un("update", this.onUpdate, this);
48316             this.ds.un("clear", this.onClear, this);
48317         }
48318         if(ds){
48319             ds.on("load", this.onLoad, this);
48320             ds.on("datachanged", this.onDataChange, this);
48321             ds.on("add", this.onAdd, this);
48322             ds.on("remove", this.onRemove, this);
48323             ds.on("update", this.onUpdate, this);
48324             ds.on("clear", this.onClear, this);
48325         }
48326         this.ds = ds;
48327
48328         if(this.cm){
48329             this.cm.un("widthchange", this.onColWidthChange, this);
48330             this.cm.un("headerchange", this.onHeaderChange, this);
48331             this.cm.un("hiddenchange", this.onHiddenChange, this);
48332             this.cm.un("columnmoved", this.onColumnMove, this);
48333             this.cm.un("columnlockchange", this.onColumnLock, this);
48334         }
48335         if(cm){
48336             this.generateRules(cm);
48337             cm.on("widthchange", this.onColWidthChange, this);
48338             cm.on("headerchange", this.onHeaderChange, this);
48339             cm.on("hiddenchange", this.onHiddenChange, this);
48340             cm.on("columnmoved", this.onColumnMove, this);
48341             cm.on("columnlockchange", this.onColumnLock, this);
48342         }
48343         this.cm = cm;
48344     },
48345
48346     init: function(grid){
48347         Roo.grid.GridView.superclass.init.call(this, grid);
48348
48349         this.bind(grid.dataSource, grid.colModel);
48350
48351         grid.on("headerclick", this.handleHeaderClick, this);
48352
48353         if(grid.trackMouseOver){
48354             grid.on("mouseover", this.onRowOver, this);
48355             grid.on("mouseout", this.onRowOut, this);
48356         }
48357         grid.cancelTextSelection = function(){};
48358         this.gridId = grid.id;
48359
48360         var tpls = this.templates || {};
48361
48362         if(!tpls.master){
48363             tpls.master = new Roo.Template(
48364                '<div class="x-grid" hidefocus="true">',
48365                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48366                   '<div class="x-grid-topbar"></div>',
48367                   '<div class="x-grid-scroller"><div></div></div>',
48368                   '<div class="x-grid-locked">',
48369                       '<div class="x-grid-header">{lockedHeader}</div>',
48370                       '<div class="x-grid-body">{lockedBody}</div>',
48371                   "</div>",
48372                   '<div class="x-grid-viewport">',
48373                       '<div class="x-grid-header">{header}</div>',
48374                       '<div class="x-grid-body">{body}</div>',
48375                   "</div>",
48376                   '<div class="x-grid-bottombar"></div>',
48377                  
48378                   '<div class="x-grid-resize-proxy">&#160;</div>',
48379                "</div>"
48380             );
48381             tpls.master.disableformats = true;
48382         }
48383
48384         if(!tpls.header){
48385             tpls.header = new Roo.Template(
48386                '<table border="0" cellspacing="0" cellpadding="0">',
48387                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48388                "</table>{splits}"
48389             );
48390             tpls.header.disableformats = true;
48391         }
48392         tpls.header.compile();
48393
48394         if(!tpls.hcell){
48395             tpls.hcell = new Roo.Template(
48396                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48397                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48398                 "</div></td>"
48399              );
48400              tpls.hcell.disableFormats = true;
48401         }
48402         tpls.hcell.compile();
48403
48404         if(!tpls.hsplit){
48405             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48406             tpls.hsplit.disableFormats = true;
48407         }
48408         tpls.hsplit.compile();
48409
48410         if(!tpls.body){
48411             tpls.body = new Roo.Template(
48412                '<table border="0" cellspacing="0" cellpadding="0">',
48413                "<tbody>{rows}</tbody>",
48414                "</table>"
48415             );
48416             tpls.body.disableFormats = true;
48417         }
48418         tpls.body.compile();
48419
48420         if(!tpls.row){
48421             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48422             tpls.row.disableFormats = true;
48423         }
48424         tpls.row.compile();
48425
48426         if(!tpls.cell){
48427             tpls.cell = new Roo.Template(
48428                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48429                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48430                 "</td>"
48431             );
48432             tpls.cell.disableFormats = true;
48433         }
48434         tpls.cell.compile();
48435
48436         this.templates = tpls;
48437     },
48438
48439     // remap these for backwards compat
48440     onColWidthChange : function(){
48441         this.updateColumns.apply(this, arguments);
48442     },
48443     onHeaderChange : function(){
48444         this.updateHeaders.apply(this, arguments);
48445     }, 
48446     onHiddenChange : function(){
48447         this.handleHiddenChange.apply(this, arguments);
48448     },
48449     onColumnMove : function(){
48450         this.handleColumnMove.apply(this, arguments);
48451     },
48452     onColumnLock : function(){
48453         this.handleLockChange.apply(this, arguments);
48454     },
48455
48456     onDataChange : function(){
48457         this.refresh();
48458         this.updateHeaderSortState();
48459     },
48460
48461     onClear : function(){
48462         this.refresh();
48463     },
48464
48465     onUpdate : function(ds, record){
48466         this.refreshRow(record);
48467     },
48468
48469     refreshRow : function(record){
48470         var ds = this.ds, index;
48471         if(typeof record == 'number'){
48472             index = record;
48473             record = ds.getAt(index);
48474         }else{
48475             index = ds.indexOf(record);
48476         }
48477         this.insertRows(ds, index, index, true);
48478         this.onRemove(ds, record, index+1, true);
48479         this.syncRowHeights(index, index);
48480         this.layout();
48481         this.fireEvent("rowupdated", this, index, record);
48482     },
48483
48484     onAdd : function(ds, records, index){
48485         this.insertRows(ds, index, index + (records.length-1));
48486     },
48487
48488     onRemove : function(ds, record, index, isUpdate){
48489         if(isUpdate !== true){
48490             this.fireEvent("beforerowremoved", this, index, record);
48491         }
48492         var bt = this.getBodyTable(), lt = this.getLockedTable();
48493         if(bt.rows[index]){
48494             bt.firstChild.removeChild(bt.rows[index]);
48495         }
48496         if(lt.rows[index]){
48497             lt.firstChild.removeChild(lt.rows[index]);
48498         }
48499         if(isUpdate !== true){
48500             this.stripeRows(index);
48501             this.syncRowHeights(index, index);
48502             this.layout();
48503             this.fireEvent("rowremoved", this, index, record);
48504         }
48505     },
48506
48507     onLoad : function(){
48508         this.scrollToTop();
48509     },
48510
48511     /**
48512      * Scrolls the grid to the top
48513      */
48514     scrollToTop : function(){
48515         if(this.scroller){
48516             this.scroller.dom.scrollTop = 0;
48517             this.syncScroll();
48518         }
48519     },
48520
48521     /**
48522      * Gets a panel in the header of the grid that can be used for toolbars etc.
48523      * After modifying the contents of this panel a call to grid.autoSize() may be
48524      * required to register any changes in size.
48525      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48526      * @return Roo.Element
48527      */
48528     getHeaderPanel : function(doShow){
48529         if(doShow){
48530             this.headerPanel.show();
48531         }
48532         return this.headerPanel;
48533     },
48534
48535     /**
48536      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48537      * After modifying the contents of this panel a call to grid.autoSize() may be
48538      * required to register any changes in size.
48539      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48540      * @return Roo.Element
48541      */
48542     getFooterPanel : function(doShow){
48543         if(doShow){
48544             this.footerPanel.show();
48545         }
48546         return this.footerPanel;
48547     },
48548
48549     initElements : function(){
48550         var E = Roo.Element;
48551         var el = this.grid.getGridEl().dom.firstChild;
48552         var cs = el.childNodes;
48553
48554         this.el = new E(el);
48555         
48556          this.focusEl = new E(el.firstChild);
48557         this.focusEl.swallowEvent("click", true);
48558         
48559         this.headerPanel = new E(cs[1]);
48560         this.headerPanel.enableDisplayMode("block");
48561
48562         this.scroller = new E(cs[2]);
48563         this.scrollSizer = new E(this.scroller.dom.firstChild);
48564
48565         this.lockedWrap = new E(cs[3]);
48566         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48567         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48568
48569         this.mainWrap = new E(cs[4]);
48570         this.mainHd = new E(this.mainWrap.dom.firstChild);
48571         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48572
48573         this.footerPanel = new E(cs[5]);
48574         this.footerPanel.enableDisplayMode("block");
48575
48576         this.resizeProxy = new E(cs[6]);
48577
48578         this.headerSelector = String.format(
48579            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48580            this.lockedHd.id, this.mainHd.id
48581         );
48582
48583         this.splitterSelector = String.format(
48584            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48585            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48586         );
48587     },
48588     idToCssName : function(s)
48589     {
48590         return s.replace(/[^a-z0-9]+/ig, '-');
48591     },
48592
48593     getHeaderCell : function(index){
48594         return Roo.DomQuery.select(this.headerSelector)[index];
48595     },
48596
48597     getHeaderCellMeasure : function(index){
48598         return this.getHeaderCell(index).firstChild;
48599     },
48600
48601     getHeaderCellText : function(index){
48602         return this.getHeaderCell(index).firstChild.firstChild;
48603     },
48604
48605     getLockedTable : function(){
48606         return this.lockedBody.dom.firstChild;
48607     },
48608
48609     getBodyTable : function(){
48610         return this.mainBody.dom.firstChild;
48611     },
48612
48613     getLockedRow : function(index){
48614         return this.getLockedTable().rows[index];
48615     },
48616
48617     getRow : function(index){
48618         return this.getBodyTable().rows[index];
48619     },
48620
48621     getRowComposite : function(index){
48622         if(!this.rowEl){
48623             this.rowEl = new Roo.CompositeElementLite();
48624         }
48625         var els = [], lrow, mrow;
48626         if(lrow = this.getLockedRow(index)){
48627             els.push(lrow);
48628         }
48629         if(mrow = this.getRow(index)){
48630             els.push(mrow);
48631         }
48632         this.rowEl.elements = els;
48633         return this.rowEl;
48634     },
48635     /**
48636      * Gets the 'td' of the cell
48637      * 
48638      * @param {Integer} rowIndex row to select
48639      * @param {Integer} colIndex column to select
48640      * 
48641      * @return {Object} 
48642      */
48643     getCell : function(rowIndex, colIndex){
48644         var locked = this.cm.getLockedCount();
48645         var source;
48646         if(colIndex < locked){
48647             source = this.lockedBody.dom.firstChild;
48648         }else{
48649             source = this.mainBody.dom.firstChild;
48650             colIndex -= locked;
48651         }
48652         return source.rows[rowIndex].childNodes[colIndex];
48653     },
48654
48655     getCellText : function(rowIndex, colIndex){
48656         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48657     },
48658
48659     getCellBox : function(cell){
48660         var b = this.fly(cell).getBox();
48661         if(Roo.isOpera){ // opera fails to report the Y
48662             b.y = cell.offsetTop + this.mainBody.getY();
48663         }
48664         return b;
48665     },
48666
48667     getCellIndex : function(cell){
48668         var id = String(cell.className).match(this.cellRE);
48669         if(id){
48670             return parseInt(id[1], 10);
48671         }
48672         return 0;
48673     },
48674
48675     findHeaderIndex : function(n){
48676         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48677         return r ? this.getCellIndex(r) : false;
48678     },
48679
48680     findHeaderCell : function(n){
48681         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48682         return r ? r : false;
48683     },
48684
48685     findRowIndex : function(n){
48686         if(!n){
48687             return false;
48688         }
48689         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48690         return r ? r.rowIndex : false;
48691     },
48692
48693     findCellIndex : function(node){
48694         var stop = this.el.dom;
48695         while(node && node != stop){
48696             if(this.findRE.test(node.className)){
48697                 return this.getCellIndex(node);
48698             }
48699             node = node.parentNode;
48700         }
48701         return false;
48702     },
48703
48704     getColumnId : function(index){
48705         return this.cm.getColumnId(index);
48706     },
48707
48708     getSplitters : function()
48709     {
48710         if(this.splitterSelector){
48711            return Roo.DomQuery.select(this.splitterSelector);
48712         }else{
48713             return null;
48714       }
48715     },
48716
48717     getSplitter : function(index){
48718         return this.getSplitters()[index];
48719     },
48720
48721     onRowOver : function(e, t){
48722         var row;
48723         if((row = this.findRowIndex(t)) !== false){
48724             this.getRowComposite(row).addClass("x-grid-row-over");
48725         }
48726     },
48727
48728     onRowOut : function(e, t){
48729         var row;
48730         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48731             this.getRowComposite(row).removeClass("x-grid-row-over");
48732         }
48733     },
48734
48735     renderHeaders : function(){
48736         var cm = this.cm;
48737         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48738         var cb = [], lb = [], sb = [], lsb = [], p = {};
48739         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48740             p.cellId = "x-grid-hd-0-" + i;
48741             p.splitId = "x-grid-csplit-0-" + i;
48742             p.id = cm.getColumnId(i);
48743             p.title = cm.getColumnTooltip(i) || "";
48744             p.value = cm.getColumnHeader(i) || "";
48745             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48746             if(!cm.isLocked(i)){
48747                 cb[cb.length] = ct.apply(p);
48748                 sb[sb.length] = st.apply(p);
48749             }else{
48750                 lb[lb.length] = ct.apply(p);
48751                 lsb[lsb.length] = st.apply(p);
48752             }
48753         }
48754         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48755                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48756     },
48757
48758     updateHeaders : function(){
48759         var html = this.renderHeaders();
48760         this.lockedHd.update(html[0]);
48761         this.mainHd.update(html[1]);
48762     },
48763
48764     /**
48765      * Focuses the specified row.
48766      * @param {Number} row The row index
48767      */
48768     focusRow : function(row)
48769     {
48770         //Roo.log('GridView.focusRow');
48771         var x = this.scroller.dom.scrollLeft;
48772         this.focusCell(row, 0, false);
48773         this.scroller.dom.scrollLeft = x;
48774     },
48775
48776     /**
48777      * Focuses the specified cell.
48778      * @param {Number} row The row index
48779      * @param {Number} col The column index
48780      * @param {Boolean} hscroll false to disable horizontal scrolling
48781      */
48782     focusCell : function(row, col, hscroll)
48783     {
48784         //Roo.log('GridView.focusCell');
48785         var el = this.ensureVisible(row, col, hscroll);
48786         this.focusEl.alignTo(el, "tl-tl");
48787         if(Roo.isGecko){
48788             this.focusEl.focus();
48789         }else{
48790             this.focusEl.focus.defer(1, this.focusEl);
48791         }
48792     },
48793
48794     /**
48795      * Scrolls the specified cell into view
48796      * @param {Number} row The row index
48797      * @param {Number} col The column index
48798      * @param {Boolean} hscroll false to disable horizontal scrolling
48799      */
48800     ensureVisible : function(row, col, hscroll)
48801     {
48802         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48803         //return null; //disable for testing.
48804         if(typeof row != "number"){
48805             row = row.rowIndex;
48806         }
48807         if(row < 0 && row >= this.ds.getCount()){
48808             return  null;
48809         }
48810         col = (col !== undefined ? col : 0);
48811         var cm = this.grid.colModel;
48812         while(cm.isHidden(col)){
48813             col++;
48814         }
48815
48816         var el = this.getCell(row, col);
48817         if(!el){
48818             return null;
48819         }
48820         var c = this.scroller.dom;
48821
48822         var ctop = parseInt(el.offsetTop, 10);
48823         var cleft = parseInt(el.offsetLeft, 10);
48824         var cbot = ctop + el.offsetHeight;
48825         var cright = cleft + el.offsetWidth;
48826         
48827         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48828         var stop = parseInt(c.scrollTop, 10);
48829         var sleft = parseInt(c.scrollLeft, 10);
48830         var sbot = stop + ch;
48831         var sright = sleft + c.clientWidth;
48832         /*
48833         Roo.log('GridView.ensureVisible:' +
48834                 ' ctop:' + ctop +
48835                 ' c.clientHeight:' + c.clientHeight +
48836                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48837                 ' stop:' + stop +
48838                 ' cbot:' + cbot +
48839                 ' sbot:' + sbot +
48840                 ' ch:' + ch  
48841                 );
48842         */
48843         if(ctop < stop){
48844              c.scrollTop = ctop;
48845             //Roo.log("set scrolltop to ctop DISABLE?");
48846         }else if(cbot > sbot){
48847             //Roo.log("set scrolltop to cbot-ch");
48848             c.scrollTop = cbot-ch;
48849         }
48850         
48851         if(hscroll !== false){
48852             if(cleft < sleft){
48853                 c.scrollLeft = cleft;
48854             }else if(cright > sright){
48855                 c.scrollLeft = cright-c.clientWidth;
48856             }
48857         }
48858          
48859         return el;
48860     },
48861
48862     updateColumns : function(){
48863         this.grid.stopEditing();
48864         var cm = this.grid.colModel, colIds = this.getColumnIds();
48865         //var totalWidth = cm.getTotalWidth();
48866         var pos = 0;
48867         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48868             //if(cm.isHidden(i)) continue;
48869             var w = cm.getColumnWidth(i);
48870             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48871             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48872         }
48873         this.updateSplitters();
48874     },
48875
48876     generateRules : function(cm){
48877         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48878         Roo.util.CSS.removeStyleSheet(rulesId);
48879         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48880             var cid = cm.getColumnId(i);
48881             var align = '';
48882             if(cm.config[i].align){
48883                 align = 'text-align:'+cm.config[i].align+';';
48884             }
48885             var hidden = '';
48886             if(cm.isHidden(i)){
48887                 hidden = 'display:none;';
48888             }
48889             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48890             ruleBuf.push(
48891                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48892                     this.hdSelector, cid, " {\n", align, width, "}\n",
48893                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48894                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48895         }
48896         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48897     },
48898
48899     updateSplitters : function(){
48900         var cm = this.cm, s = this.getSplitters();
48901         if(s){ // splitters not created yet
48902             var pos = 0, locked = true;
48903             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48904                 if(cm.isHidden(i)) continue;
48905                 var w = cm.getColumnWidth(i); // make sure it's a number
48906                 if(!cm.isLocked(i) && locked){
48907                     pos = 0;
48908                     locked = false;
48909                 }
48910                 pos += w;
48911                 s[i].style.left = (pos-this.splitOffset) + "px";
48912             }
48913         }
48914     },
48915
48916     handleHiddenChange : function(colModel, colIndex, hidden){
48917         if(hidden){
48918             this.hideColumn(colIndex);
48919         }else{
48920             this.unhideColumn(colIndex);
48921         }
48922     },
48923
48924     hideColumn : function(colIndex){
48925         var cid = this.getColumnId(colIndex);
48926         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48927         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48928         if(Roo.isSafari){
48929             this.updateHeaders();
48930         }
48931         this.updateSplitters();
48932         this.layout();
48933     },
48934
48935     unhideColumn : function(colIndex){
48936         var cid = this.getColumnId(colIndex);
48937         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48938         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48939
48940         if(Roo.isSafari){
48941             this.updateHeaders();
48942         }
48943         this.updateSplitters();
48944         this.layout();
48945     },
48946
48947     insertRows : function(dm, firstRow, lastRow, isUpdate){
48948         if(firstRow == 0 && lastRow == dm.getCount()-1){
48949             this.refresh();
48950         }else{
48951             if(!isUpdate){
48952                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48953             }
48954             var s = this.getScrollState();
48955             var markup = this.renderRows(firstRow, lastRow);
48956             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48957             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48958             this.restoreScroll(s);
48959             if(!isUpdate){
48960                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48961                 this.syncRowHeights(firstRow, lastRow);
48962                 this.stripeRows(firstRow);
48963                 this.layout();
48964             }
48965         }
48966     },
48967
48968     bufferRows : function(markup, target, index){
48969         var before = null, trows = target.rows, tbody = target.tBodies[0];
48970         if(index < trows.length){
48971             before = trows[index];
48972         }
48973         var b = document.createElement("div");
48974         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48975         var rows = b.firstChild.rows;
48976         for(var i = 0, len = rows.length; i < len; i++){
48977             if(before){
48978                 tbody.insertBefore(rows[0], before);
48979             }else{
48980                 tbody.appendChild(rows[0]);
48981             }
48982         }
48983         b.innerHTML = "";
48984         b = null;
48985     },
48986
48987     deleteRows : function(dm, firstRow, lastRow){
48988         if(dm.getRowCount()<1){
48989             this.fireEvent("beforerefresh", this);
48990             this.mainBody.update("");
48991             this.lockedBody.update("");
48992             this.fireEvent("refresh", this);
48993         }else{
48994             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48995             var bt = this.getBodyTable();
48996             var tbody = bt.firstChild;
48997             var rows = bt.rows;
48998             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48999                 tbody.removeChild(rows[firstRow]);
49000             }
49001             this.stripeRows(firstRow);
49002             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49003         }
49004     },
49005
49006     updateRows : function(dataSource, firstRow, lastRow){
49007         var s = this.getScrollState();
49008         this.refresh();
49009         this.restoreScroll(s);
49010     },
49011
49012     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49013         if(!noRefresh){
49014            this.refresh();
49015         }
49016         this.updateHeaderSortState();
49017     },
49018
49019     getScrollState : function(){
49020         
49021         var sb = this.scroller.dom;
49022         return {left: sb.scrollLeft, top: sb.scrollTop};
49023     },
49024
49025     stripeRows : function(startRow){
49026         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49027             return;
49028         }
49029         startRow = startRow || 0;
49030         var rows = this.getBodyTable().rows;
49031         var lrows = this.getLockedTable().rows;
49032         var cls = ' x-grid-row-alt ';
49033         for(var i = startRow, len = rows.length; i < len; i++){
49034             var row = rows[i], lrow = lrows[i];
49035             var isAlt = ((i+1) % 2 == 0);
49036             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49037             if(isAlt == hasAlt){
49038                 continue;
49039             }
49040             if(isAlt){
49041                 row.className += " x-grid-row-alt";
49042             }else{
49043                 row.className = row.className.replace("x-grid-row-alt", "");
49044             }
49045             if(lrow){
49046                 lrow.className = row.className;
49047             }
49048         }
49049     },
49050
49051     restoreScroll : function(state){
49052         //Roo.log('GridView.restoreScroll');
49053         var sb = this.scroller.dom;
49054         sb.scrollLeft = state.left;
49055         sb.scrollTop = state.top;
49056         this.syncScroll();
49057     },
49058
49059     syncScroll : function(){
49060         //Roo.log('GridView.syncScroll');
49061         var sb = this.scroller.dom;
49062         var sh = this.mainHd.dom;
49063         var bs = this.mainBody.dom;
49064         var lv = this.lockedBody.dom;
49065         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49066         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49067     },
49068
49069     handleScroll : function(e){
49070         this.syncScroll();
49071         var sb = this.scroller.dom;
49072         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49073         e.stopEvent();
49074     },
49075
49076     handleWheel : function(e){
49077         var d = e.getWheelDelta();
49078         this.scroller.dom.scrollTop -= d*22;
49079         // set this here to prevent jumpy scrolling on large tables
49080         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49081         e.stopEvent();
49082     },
49083
49084     renderRows : function(startRow, endRow){
49085         // pull in all the crap needed to render rows
49086         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49087         var colCount = cm.getColumnCount();
49088
49089         if(ds.getCount() < 1){
49090             return ["", ""];
49091         }
49092
49093         // build a map for all the columns
49094         var cs = [];
49095         for(var i = 0; i < colCount; i++){
49096             var name = cm.getDataIndex(i);
49097             cs[i] = {
49098                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49099                 renderer : cm.getRenderer(i),
49100                 id : cm.getColumnId(i),
49101                 locked : cm.isLocked(i)
49102             };
49103         }
49104
49105         startRow = startRow || 0;
49106         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49107
49108         // records to render
49109         var rs = ds.getRange(startRow, endRow);
49110
49111         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49112     },
49113
49114     // As much as I hate to duplicate code, this was branched because FireFox really hates
49115     // [].join("") on strings. The performance difference was substantial enough to
49116     // branch this function
49117     doRender : Roo.isGecko ?
49118             function(cs, rs, ds, startRow, colCount, stripe){
49119                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49120                 // buffers
49121                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49122                 
49123                 var hasListener = this.grid.hasListener('rowclass');
49124                 var rowcfg = {};
49125                 for(var j = 0, len = rs.length; j < len; j++){
49126                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49127                     for(var i = 0; i < colCount; i++){
49128                         c = cs[i];
49129                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49130                         p.id = c.id;
49131                         p.css = p.attr = "";
49132                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49133                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49134                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49135                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49136                         }
49137                         var markup = ct.apply(p);
49138                         if(!c.locked){
49139                             cb+= markup;
49140                         }else{
49141                             lcb+= markup;
49142                         }
49143                     }
49144                     var alt = [];
49145                     if(stripe && ((rowIndex+1) % 2 == 0)){
49146                         alt.push("x-grid-row-alt")
49147                     }
49148                     if(r.dirty){
49149                         alt.push(  " x-grid-dirty-row");
49150                     }
49151                     rp.cells = lcb;
49152                     if(this.getRowClass){
49153                         alt.push(this.getRowClass(r, rowIndex));
49154                     }
49155                     if (hasListener) {
49156                         rowcfg = {
49157                              
49158                             record: r,
49159                             rowIndex : rowIndex,
49160                             rowClass : ''
49161                         }
49162                         this.grid.fireEvent('rowclass', this, rowcfg);
49163                         alt.push(rowcfg.rowClass);
49164                     }
49165                     rp.alt = alt.join(" ");
49166                     lbuf+= rt.apply(rp);
49167                     rp.cells = cb;
49168                     buf+=  rt.apply(rp);
49169                 }
49170                 return [lbuf, buf];
49171             } :
49172             function(cs, rs, ds, startRow, colCount, stripe){
49173                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49174                 // buffers
49175                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49176                 var hasListener = this.grid.hasListener('rowclass');
49177                 var rowcfg = {};
49178                 for(var j = 0, len = rs.length; j < len; j++){
49179                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49180                     for(var i = 0; i < colCount; i++){
49181                         c = cs[i];
49182                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49183                         p.id = c.id;
49184                         p.css = p.attr = "";
49185                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49186                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49187                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49188                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49189                         }
49190                         var markup = ct.apply(p);
49191                         if(!c.locked){
49192                             cb[cb.length] = markup;
49193                         }else{
49194                             lcb[lcb.length] = markup;
49195                         }
49196                     }
49197                     var alt = [];
49198                     if(stripe && ((rowIndex+1) % 2 == 0)){
49199                         alt.push( "x-grid-row-alt");
49200                     }
49201                     if(r.dirty){
49202                         alt.push(" x-grid-dirty-row");
49203                     }
49204                     rp.cells = lcb;
49205                     if(this.getRowClass){
49206                         alt.push( this.getRowClass(r, rowIndex));
49207                     }
49208                     if (hasListener) {
49209                         rowcfg = {
49210                              
49211                             record: r,
49212                             rowIndex : rowIndex,
49213                             rowClass : ''
49214                         }
49215                         this.grid.fireEvent('rowclass', this, rowcfg);
49216                         alt.push(rowcfg.rowClass);
49217                     }
49218                     rp.alt = alt.join(" ");
49219                     rp.cells = lcb.join("");
49220                     lbuf[lbuf.length] = rt.apply(rp);
49221                     rp.cells = cb.join("");
49222                     buf[buf.length] =  rt.apply(rp);
49223                 }
49224                 return [lbuf.join(""), buf.join("")];
49225             },
49226
49227     renderBody : function(){
49228         var markup = this.renderRows();
49229         var bt = this.templates.body;
49230         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49231     },
49232
49233     /**
49234      * Refreshes the grid
49235      * @param {Boolean} headersToo
49236      */
49237     refresh : function(headersToo){
49238         this.fireEvent("beforerefresh", this);
49239         this.grid.stopEditing();
49240         var result = this.renderBody();
49241         this.lockedBody.update(result[0]);
49242         this.mainBody.update(result[1]);
49243         if(headersToo === true){
49244             this.updateHeaders();
49245             this.updateColumns();
49246             this.updateSplitters();
49247             this.updateHeaderSortState();
49248         }
49249         this.syncRowHeights();
49250         this.layout();
49251         this.fireEvent("refresh", this);
49252     },
49253
49254     handleColumnMove : function(cm, oldIndex, newIndex){
49255         this.indexMap = null;
49256         var s = this.getScrollState();
49257         this.refresh(true);
49258         this.restoreScroll(s);
49259         this.afterMove(newIndex);
49260     },
49261
49262     afterMove : function(colIndex){
49263         if(this.enableMoveAnim && Roo.enableFx){
49264             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49265         }
49266         // if multisort - fix sortOrder, and reload..
49267         if (this.grid.dataSource.multiSort) {
49268             // the we can call sort again..
49269             var dm = this.grid.dataSource;
49270             var cm = this.grid.colModel;
49271             var so = [];
49272             for(var i = 0; i < cm.config.length; i++ ) {
49273                 
49274                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49275                     continue; // dont' bother, it's not in sort list or being set.
49276                 }
49277                 
49278                 so.push(cm.config[i].dataIndex);
49279             };
49280             dm.sortOrder = so;
49281             dm.load(dm.lastOptions);
49282             
49283             
49284         }
49285         
49286     },
49287
49288     updateCell : function(dm, rowIndex, dataIndex){
49289         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49290         if(typeof colIndex == "undefined"){ // not present in grid
49291             return;
49292         }
49293         var cm = this.grid.colModel;
49294         var cell = this.getCell(rowIndex, colIndex);
49295         var cellText = this.getCellText(rowIndex, colIndex);
49296
49297         var p = {
49298             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49299             id : cm.getColumnId(colIndex),
49300             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49301         };
49302         var renderer = cm.getRenderer(colIndex);
49303         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49304         if(typeof val == "undefined" || val === "") val = "&#160;";
49305         cellText.innerHTML = val;
49306         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49307         this.syncRowHeights(rowIndex, rowIndex);
49308     },
49309
49310     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49311         var maxWidth = 0;
49312         if(this.grid.autoSizeHeaders){
49313             var h = this.getHeaderCellMeasure(colIndex);
49314             maxWidth = Math.max(maxWidth, h.scrollWidth);
49315         }
49316         var tb, index;
49317         if(this.cm.isLocked(colIndex)){
49318             tb = this.getLockedTable();
49319             index = colIndex;
49320         }else{
49321             tb = this.getBodyTable();
49322             index = colIndex - this.cm.getLockedCount();
49323         }
49324         if(tb && tb.rows){
49325             var rows = tb.rows;
49326             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49327             for(var i = 0; i < stopIndex; i++){
49328                 var cell = rows[i].childNodes[index].firstChild;
49329                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49330             }
49331         }
49332         return maxWidth + /*margin for error in IE*/ 5;
49333     },
49334     /**
49335      * Autofit a column to its content.
49336      * @param {Number} colIndex
49337      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49338      */
49339      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49340          if(this.cm.isHidden(colIndex)){
49341              return; // can't calc a hidden column
49342          }
49343         if(forceMinSize){
49344             var cid = this.cm.getColumnId(colIndex);
49345             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49346            if(this.grid.autoSizeHeaders){
49347                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49348            }
49349         }
49350         var newWidth = this.calcColumnWidth(colIndex);
49351         this.cm.setColumnWidth(colIndex,
49352             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49353         if(!suppressEvent){
49354             this.grid.fireEvent("columnresize", colIndex, newWidth);
49355         }
49356     },
49357
49358     /**
49359      * Autofits all columns to their content and then expands to fit any extra space in the grid
49360      */
49361      autoSizeColumns : function(){
49362         var cm = this.grid.colModel;
49363         var colCount = cm.getColumnCount();
49364         for(var i = 0; i < colCount; i++){
49365             this.autoSizeColumn(i, true, true);
49366         }
49367         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49368             this.fitColumns();
49369         }else{
49370             this.updateColumns();
49371             this.layout();
49372         }
49373     },
49374
49375     /**
49376      * Autofits all columns to the grid's width proportionate with their current size
49377      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49378      */
49379     fitColumns : function(reserveScrollSpace){
49380         var cm = this.grid.colModel;
49381         var colCount = cm.getColumnCount();
49382         var cols = [];
49383         var width = 0;
49384         var i, w;
49385         for (i = 0; i < colCount; i++){
49386             if(!cm.isHidden(i) && !cm.isFixed(i)){
49387                 w = cm.getColumnWidth(i);
49388                 cols.push(i);
49389                 cols.push(w);
49390                 width += w;
49391             }
49392         }
49393         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49394         if(reserveScrollSpace){
49395             avail -= 17;
49396         }
49397         var frac = (avail - cm.getTotalWidth())/width;
49398         while (cols.length){
49399             w = cols.pop();
49400             i = cols.pop();
49401             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49402         }
49403         this.updateColumns();
49404         this.layout();
49405     },
49406
49407     onRowSelect : function(rowIndex){
49408         var row = this.getRowComposite(rowIndex);
49409         row.addClass("x-grid-row-selected");
49410     },
49411
49412     onRowDeselect : function(rowIndex){
49413         var row = this.getRowComposite(rowIndex);
49414         row.removeClass("x-grid-row-selected");
49415     },
49416
49417     onCellSelect : function(row, col){
49418         var cell = this.getCell(row, col);
49419         if(cell){
49420             Roo.fly(cell).addClass("x-grid-cell-selected");
49421         }
49422     },
49423
49424     onCellDeselect : function(row, col){
49425         var cell = this.getCell(row, col);
49426         if(cell){
49427             Roo.fly(cell).removeClass("x-grid-cell-selected");
49428         }
49429     },
49430
49431     updateHeaderSortState : function(){
49432         
49433         // sort state can be single { field: xxx, direction : yyy}
49434         // or   { xxx=>ASC , yyy : DESC ..... }
49435         
49436         var mstate = {};
49437         if (!this.ds.multiSort) { 
49438             var state = this.ds.getSortState();
49439             if(!state){
49440                 return;
49441             }
49442             mstate[state.field] = state.direction;
49443             // FIXME... - this is not used here.. but might be elsewhere..
49444             this.sortState = state;
49445             
49446         } else {
49447             mstate = this.ds.sortToggle;
49448         }
49449         //remove existing sort classes..
49450         
49451         var sc = this.sortClasses;
49452         var hds = this.el.select(this.headerSelector).removeClass(sc);
49453         
49454         for(var f in mstate) {
49455         
49456             var sortColumn = this.cm.findColumnIndex(f);
49457             
49458             if(sortColumn != -1){
49459                 var sortDir = mstate[f];        
49460                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49461             }
49462         }
49463         
49464          
49465         
49466     },
49467
49468
49469     handleHeaderClick : function(g, index){
49470         if(this.headersDisabled){
49471             return;
49472         }
49473         var dm = g.dataSource, cm = g.colModel;
49474         if(!cm.isSortable(index)){
49475             return;
49476         }
49477         g.stopEditing();
49478         
49479         if (dm.multiSort) {
49480             // update the sortOrder
49481             var so = [];
49482             for(var i = 0; i < cm.config.length; i++ ) {
49483                 
49484                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49485                     continue; // dont' bother, it's not in sort list or being set.
49486                 }
49487                 
49488                 so.push(cm.config[i].dataIndex);
49489             };
49490             dm.sortOrder = so;
49491         }
49492         
49493         
49494         dm.sort(cm.getDataIndex(index));
49495     },
49496
49497
49498     destroy : function(){
49499         if(this.colMenu){
49500             this.colMenu.removeAll();
49501             Roo.menu.MenuMgr.unregister(this.colMenu);
49502             this.colMenu.getEl().remove();
49503             delete this.colMenu;
49504         }
49505         if(this.hmenu){
49506             this.hmenu.removeAll();
49507             Roo.menu.MenuMgr.unregister(this.hmenu);
49508             this.hmenu.getEl().remove();
49509             delete this.hmenu;
49510         }
49511         if(this.grid.enableColumnMove){
49512             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49513             if(dds){
49514                 for(var dd in dds){
49515                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49516                         var elid = dds[dd].dragElId;
49517                         dds[dd].unreg();
49518                         Roo.get(elid).remove();
49519                     } else if(dds[dd].config.isTarget){
49520                         dds[dd].proxyTop.remove();
49521                         dds[dd].proxyBottom.remove();
49522                         dds[dd].unreg();
49523                     }
49524                     if(Roo.dd.DDM.locationCache[dd]){
49525                         delete Roo.dd.DDM.locationCache[dd];
49526                     }
49527                 }
49528                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49529             }
49530         }
49531         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49532         this.bind(null, null);
49533         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49534     },
49535
49536     handleLockChange : function(){
49537         this.refresh(true);
49538     },
49539
49540     onDenyColumnLock : function(){
49541
49542     },
49543
49544     onDenyColumnHide : function(){
49545
49546     },
49547
49548     handleHdMenuClick : function(item){
49549         var index = this.hdCtxIndex;
49550         var cm = this.cm, ds = this.ds;
49551         switch(item.id){
49552             case "asc":
49553                 ds.sort(cm.getDataIndex(index), "ASC");
49554                 break;
49555             case "desc":
49556                 ds.sort(cm.getDataIndex(index), "DESC");
49557                 break;
49558             case "lock":
49559                 var lc = cm.getLockedCount();
49560                 if(cm.getColumnCount(true) <= lc+1){
49561                     this.onDenyColumnLock();
49562                     return;
49563                 }
49564                 if(lc != index){
49565                     cm.setLocked(index, true, true);
49566                     cm.moveColumn(index, lc);
49567                     this.grid.fireEvent("columnmove", index, lc);
49568                 }else{
49569                     cm.setLocked(index, true);
49570                 }
49571             break;
49572             case "unlock":
49573                 var lc = cm.getLockedCount();
49574                 if((lc-1) != index){
49575                     cm.setLocked(index, false, true);
49576                     cm.moveColumn(index, lc-1);
49577                     this.grid.fireEvent("columnmove", index, lc-1);
49578                 }else{
49579                     cm.setLocked(index, false);
49580                 }
49581             break;
49582             default:
49583                 index = cm.getIndexById(item.id.substr(4));
49584                 if(index != -1){
49585                     if(item.checked && cm.getColumnCount(true) <= 1){
49586                         this.onDenyColumnHide();
49587                         return false;
49588                     }
49589                     cm.setHidden(index, item.checked);
49590                 }
49591         }
49592         return true;
49593     },
49594
49595     beforeColMenuShow : function(){
49596         var cm = this.cm,  colCount = cm.getColumnCount();
49597         this.colMenu.removeAll();
49598         for(var i = 0; i < colCount; i++){
49599             this.colMenu.add(new Roo.menu.CheckItem({
49600                 id: "col-"+cm.getColumnId(i),
49601                 text: cm.getColumnHeader(i),
49602                 checked: !cm.isHidden(i),
49603                 hideOnClick:false
49604             }));
49605         }
49606     },
49607
49608     handleHdCtx : function(g, index, e){
49609         e.stopEvent();
49610         var hd = this.getHeaderCell(index);
49611         this.hdCtxIndex = index;
49612         var ms = this.hmenu.items, cm = this.cm;
49613         ms.get("asc").setDisabled(!cm.isSortable(index));
49614         ms.get("desc").setDisabled(!cm.isSortable(index));
49615         if(this.grid.enableColLock !== false){
49616             ms.get("lock").setDisabled(cm.isLocked(index));
49617             ms.get("unlock").setDisabled(!cm.isLocked(index));
49618         }
49619         this.hmenu.show(hd, "tl-bl");
49620     },
49621
49622     handleHdOver : function(e){
49623         var hd = this.findHeaderCell(e.getTarget());
49624         if(hd && !this.headersDisabled){
49625             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49626                this.fly(hd).addClass("x-grid-hd-over");
49627             }
49628         }
49629     },
49630
49631     handleHdOut : function(e){
49632         var hd = this.findHeaderCell(e.getTarget());
49633         if(hd){
49634             this.fly(hd).removeClass("x-grid-hd-over");
49635         }
49636     },
49637
49638     handleSplitDblClick : function(e, t){
49639         var i = this.getCellIndex(t);
49640         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49641             this.autoSizeColumn(i, true);
49642             this.layout();
49643         }
49644     },
49645
49646     render : function(){
49647
49648         var cm = this.cm;
49649         var colCount = cm.getColumnCount();
49650
49651         if(this.grid.monitorWindowResize === true){
49652             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49653         }
49654         var header = this.renderHeaders();
49655         var body = this.templates.body.apply({rows:""});
49656         var html = this.templates.master.apply({
49657             lockedBody: body,
49658             body: body,
49659             lockedHeader: header[0],
49660             header: header[1]
49661         });
49662
49663         //this.updateColumns();
49664
49665         this.grid.getGridEl().dom.innerHTML = html;
49666
49667         this.initElements();
49668         
49669         // a kludge to fix the random scolling effect in webkit
49670         this.el.on("scroll", function() {
49671             this.el.dom.scrollTop=0; // hopefully not recursive..
49672         },this);
49673
49674         this.scroller.on("scroll", this.handleScroll, this);
49675         this.lockedBody.on("mousewheel", this.handleWheel, this);
49676         this.mainBody.on("mousewheel", this.handleWheel, this);
49677
49678         this.mainHd.on("mouseover", this.handleHdOver, this);
49679         this.mainHd.on("mouseout", this.handleHdOut, this);
49680         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49681                 {delegate: "."+this.splitClass});
49682
49683         this.lockedHd.on("mouseover", this.handleHdOver, this);
49684         this.lockedHd.on("mouseout", this.handleHdOut, this);
49685         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49686                 {delegate: "."+this.splitClass});
49687
49688         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49689             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49690         }
49691
49692         this.updateSplitters();
49693
49694         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49695             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49696             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49697         }
49698
49699         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49700             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49701             this.hmenu.add(
49702                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49703                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49704             );
49705             if(this.grid.enableColLock !== false){
49706                 this.hmenu.add('-',
49707                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49708                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49709                 );
49710             }
49711             if(this.grid.enableColumnHide !== false){
49712
49713                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49714                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49715                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49716
49717                 this.hmenu.add('-',
49718                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49719                 );
49720             }
49721             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49722
49723             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49724         }
49725
49726         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49727             this.dd = new Roo.grid.GridDragZone(this.grid, {
49728                 ddGroup : this.grid.ddGroup || 'GridDD'
49729             });
49730         }
49731
49732         /*
49733         for(var i = 0; i < colCount; i++){
49734             if(cm.isHidden(i)){
49735                 this.hideColumn(i);
49736             }
49737             if(cm.config[i].align){
49738                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49739                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49740             }
49741         }*/
49742         
49743         this.updateHeaderSortState();
49744
49745         this.beforeInitialResize();
49746         this.layout(true);
49747
49748         // two part rendering gives faster view to the user
49749         this.renderPhase2.defer(1, this);
49750     },
49751
49752     renderPhase2 : function(){
49753         // render the rows now
49754         this.refresh();
49755         if(this.grid.autoSizeColumns){
49756             this.autoSizeColumns();
49757         }
49758     },
49759
49760     beforeInitialResize : function(){
49761
49762     },
49763
49764     onColumnSplitterMoved : function(i, w){
49765         this.userResized = true;
49766         var cm = this.grid.colModel;
49767         cm.setColumnWidth(i, w, true);
49768         var cid = cm.getColumnId(i);
49769         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49770         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49771         this.updateSplitters();
49772         this.layout();
49773         this.grid.fireEvent("columnresize", i, w);
49774     },
49775
49776     syncRowHeights : function(startIndex, endIndex){
49777         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49778             startIndex = startIndex || 0;
49779             var mrows = this.getBodyTable().rows;
49780             var lrows = this.getLockedTable().rows;
49781             var len = mrows.length-1;
49782             endIndex = Math.min(endIndex || len, len);
49783             for(var i = startIndex; i <= endIndex; i++){
49784                 var m = mrows[i], l = lrows[i];
49785                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49786                 m.style.height = l.style.height = h + "px";
49787             }
49788         }
49789     },
49790
49791     layout : function(initialRender, is2ndPass){
49792         var g = this.grid;
49793         var auto = g.autoHeight;
49794         var scrollOffset = 16;
49795         var c = g.getGridEl(), cm = this.cm,
49796                 expandCol = g.autoExpandColumn,
49797                 gv = this;
49798         //c.beginMeasure();
49799
49800         if(!c.dom.offsetWidth){ // display:none?
49801             if(initialRender){
49802                 this.lockedWrap.show();
49803                 this.mainWrap.show();
49804             }
49805             return;
49806         }
49807
49808         var hasLock = this.cm.isLocked(0);
49809
49810         var tbh = this.headerPanel.getHeight();
49811         var bbh = this.footerPanel.getHeight();
49812
49813         if(auto){
49814             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49815             var newHeight = ch + c.getBorderWidth("tb");
49816             if(g.maxHeight){
49817                 newHeight = Math.min(g.maxHeight, newHeight);
49818             }
49819             c.setHeight(newHeight);
49820         }
49821
49822         if(g.autoWidth){
49823             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49824         }
49825
49826         var s = this.scroller;
49827
49828         var csize = c.getSize(true);
49829
49830         this.el.setSize(csize.width, csize.height);
49831
49832         this.headerPanel.setWidth(csize.width);
49833         this.footerPanel.setWidth(csize.width);
49834
49835         var hdHeight = this.mainHd.getHeight();
49836         var vw = csize.width;
49837         var vh = csize.height - (tbh + bbh);
49838
49839         s.setSize(vw, vh);
49840
49841         var bt = this.getBodyTable();
49842         var ltWidth = hasLock ?
49843                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49844
49845         var scrollHeight = bt.offsetHeight;
49846         var scrollWidth = ltWidth + bt.offsetWidth;
49847         var vscroll = false, hscroll = false;
49848
49849         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49850
49851         var lw = this.lockedWrap, mw = this.mainWrap;
49852         var lb = this.lockedBody, mb = this.mainBody;
49853
49854         setTimeout(function(){
49855             var t = s.dom.offsetTop;
49856             var w = s.dom.clientWidth,
49857                 h = s.dom.clientHeight;
49858
49859             lw.setTop(t);
49860             lw.setSize(ltWidth, h);
49861
49862             mw.setLeftTop(ltWidth, t);
49863             mw.setSize(w-ltWidth, h);
49864
49865             lb.setHeight(h-hdHeight);
49866             mb.setHeight(h-hdHeight);
49867
49868             if(is2ndPass !== true && !gv.userResized && expandCol){
49869                 // high speed resize without full column calculation
49870                 
49871                 var ci = cm.getIndexById(expandCol);
49872                 if (ci < 0) {
49873                     ci = cm.findColumnIndex(expandCol);
49874                 }
49875                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49876                 var expandId = cm.getColumnId(ci);
49877                 var  tw = cm.getTotalWidth(false);
49878                 var currentWidth = cm.getColumnWidth(ci);
49879                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49880                 if(currentWidth != cw){
49881                     cm.setColumnWidth(ci, cw, true);
49882                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49883                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49884                     gv.updateSplitters();
49885                     gv.layout(false, true);
49886                 }
49887             }
49888
49889             if(initialRender){
49890                 lw.show();
49891                 mw.show();
49892             }
49893             //c.endMeasure();
49894         }, 10);
49895     },
49896
49897     onWindowResize : function(){
49898         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49899             return;
49900         }
49901         this.layout();
49902     },
49903
49904     appendFooter : function(parentEl){
49905         return null;
49906     },
49907
49908     sortAscText : "Sort Ascending",
49909     sortDescText : "Sort Descending",
49910     lockText : "Lock Column",
49911     unlockText : "Unlock Column",
49912     columnsText : "Columns"
49913 });
49914
49915
49916 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49917     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49918     this.proxy.el.addClass('x-grid3-col-dd');
49919 };
49920
49921 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49922     handleMouseDown : function(e){
49923
49924     },
49925
49926     callHandleMouseDown : function(e){
49927         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49928     }
49929 });
49930 /*
49931  * Based on:
49932  * Ext JS Library 1.1.1
49933  * Copyright(c) 2006-2007, Ext JS, LLC.
49934  *
49935  * Originally Released Under LGPL - original licence link has changed is not relivant.
49936  *
49937  * Fork - LGPL
49938  * <script type="text/javascript">
49939  */
49940  
49941 // private
49942 // This is a support class used internally by the Grid components
49943 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49944     this.grid = grid;
49945     this.view = grid.getView();
49946     this.proxy = this.view.resizeProxy;
49947     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49948         "gridSplitters" + this.grid.getGridEl().id, {
49949         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49950     });
49951     this.setHandleElId(Roo.id(hd));
49952     this.setOuterHandleElId(Roo.id(hd2));
49953     this.scroll = false;
49954 };
49955 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49956     fly: Roo.Element.fly,
49957
49958     b4StartDrag : function(x, y){
49959         this.view.headersDisabled = true;
49960         this.proxy.setHeight(this.view.mainWrap.getHeight());
49961         var w = this.cm.getColumnWidth(this.cellIndex);
49962         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49963         this.resetConstraints();
49964         this.setXConstraint(minw, 1000);
49965         this.setYConstraint(0, 0);
49966         this.minX = x - minw;
49967         this.maxX = x + 1000;
49968         this.startPos = x;
49969         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49970     },
49971
49972
49973     handleMouseDown : function(e){
49974         ev = Roo.EventObject.setEvent(e);
49975         var t = this.fly(ev.getTarget());
49976         if(t.hasClass("x-grid-split")){
49977             this.cellIndex = this.view.getCellIndex(t.dom);
49978             this.split = t.dom;
49979             this.cm = this.grid.colModel;
49980             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49981                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49982             }
49983         }
49984     },
49985
49986     endDrag : function(e){
49987         this.view.headersDisabled = false;
49988         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49989         var diff = endX - this.startPos;
49990         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49991     },
49992
49993     autoOffset : function(){
49994         this.setDelta(0,0);
49995     }
49996 });/*
49997  * Based on:
49998  * Ext JS Library 1.1.1
49999  * Copyright(c) 2006-2007, Ext JS, LLC.
50000  *
50001  * Originally Released Under LGPL - original licence link has changed is not relivant.
50002  *
50003  * Fork - LGPL
50004  * <script type="text/javascript">
50005  */
50006  
50007 // private
50008 // This is a support class used internally by the Grid components
50009 Roo.grid.GridDragZone = function(grid, config){
50010     this.view = grid.getView();
50011     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50012     if(this.view.lockedBody){
50013         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50014         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50015     }
50016     this.scroll = false;
50017     this.grid = grid;
50018     this.ddel = document.createElement('div');
50019     this.ddel.className = 'x-grid-dd-wrap';
50020 };
50021
50022 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50023     ddGroup : "GridDD",
50024
50025     getDragData : function(e){
50026         var t = Roo.lib.Event.getTarget(e);
50027         var rowIndex = this.view.findRowIndex(t);
50028         if(rowIndex !== false){
50029             var sm = this.grid.selModel;
50030             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50031               //  sm.mouseDown(e, t);
50032             //}
50033             if (e.hasModifier()){
50034                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50035             }
50036             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50037         }
50038         return false;
50039     },
50040
50041     onInitDrag : function(e){
50042         var data = this.dragData;
50043         this.ddel.innerHTML = this.grid.getDragDropText();
50044         this.proxy.update(this.ddel);
50045         // fire start drag?
50046     },
50047
50048     afterRepair : function(){
50049         this.dragging = false;
50050     },
50051
50052     getRepairXY : function(e, data){
50053         return false;
50054     },
50055
50056     onEndDrag : function(data, e){
50057         // fire end drag?
50058     },
50059
50060     onValidDrop : function(dd, e, id){
50061         // fire drag drop?
50062         this.hideProxy();
50063     },
50064
50065     beforeInvalidDrop : function(e, id){
50066
50067     }
50068 });/*
50069  * Based on:
50070  * Ext JS Library 1.1.1
50071  * Copyright(c) 2006-2007, Ext JS, LLC.
50072  *
50073  * Originally Released Under LGPL - original licence link has changed is not relivant.
50074  *
50075  * Fork - LGPL
50076  * <script type="text/javascript">
50077  */
50078  
50079
50080 /**
50081  * @class Roo.grid.ColumnModel
50082  * @extends Roo.util.Observable
50083  * This is the default implementation of a ColumnModel used by the Grid. It defines
50084  * the columns in the grid.
50085  * <br>Usage:<br>
50086  <pre><code>
50087  var colModel = new Roo.grid.ColumnModel([
50088         {header: "Ticker", width: 60, sortable: true, locked: true},
50089         {header: "Company Name", width: 150, sortable: true},
50090         {header: "Market Cap.", width: 100, sortable: true},
50091         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50092         {header: "Employees", width: 100, sortable: true, resizable: false}
50093  ]);
50094  </code></pre>
50095  * <p>
50096  
50097  * The config options listed for this class are options which may appear in each
50098  * individual column definition.
50099  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50100  * @constructor
50101  * @param {Object} config An Array of column config objects. See this class's
50102  * config objects for details.
50103 */
50104 Roo.grid.ColumnModel = function(config){
50105         /**
50106      * The config passed into the constructor
50107      */
50108     this.config = config;
50109     this.lookup = {};
50110
50111     // if no id, create one
50112     // if the column does not have a dataIndex mapping,
50113     // map it to the order it is in the config
50114     for(var i = 0, len = config.length; i < len; i++){
50115         var c = config[i];
50116         if(typeof c.dataIndex == "undefined"){
50117             c.dataIndex = i;
50118         }
50119         if(typeof c.renderer == "string"){
50120             c.renderer = Roo.util.Format[c.renderer];
50121         }
50122         if(typeof c.id == "undefined"){
50123             c.id = Roo.id();
50124         }
50125         if(c.editor && c.editor.xtype){
50126             c.editor  = Roo.factory(c.editor, Roo.grid);
50127         }
50128         if(c.editor && c.editor.isFormField){
50129             c.editor = new Roo.grid.GridEditor(c.editor);
50130         }
50131         this.lookup[c.id] = c;
50132     }
50133
50134     /**
50135      * The width of columns which have no width specified (defaults to 100)
50136      * @type Number
50137      */
50138     this.defaultWidth = 100;
50139
50140     /**
50141      * Default sortable of columns which have no sortable specified (defaults to false)
50142      * @type Boolean
50143      */
50144     this.defaultSortable = false;
50145
50146     this.addEvents({
50147         /**
50148              * @event widthchange
50149              * Fires when the width of a column changes.
50150              * @param {ColumnModel} this
50151              * @param {Number} columnIndex The column index
50152              * @param {Number} newWidth The new width
50153              */
50154             "widthchange": true,
50155         /**
50156              * @event headerchange
50157              * Fires when the text of a header changes.
50158              * @param {ColumnModel} this
50159              * @param {Number} columnIndex The column index
50160              * @param {Number} newText The new header text
50161              */
50162             "headerchange": true,
50163         /**
50164              * @event hiddenchange
50165              * Fires when a column is hidden or "unhidden".
50166              * @param {ColumnModel} this
50167              * @param {Number} columnIndex The column index
50168              * @param {Boolean} hidden true if hidden, false otherwise
50169              */
50170             "hiddenchange": true,
50171             /**
50172          * @event columnmoved
50173          * Fires when a column is moved.
50174          * @param {ColumnModel} this
50175          * @param {Number} oldIndex
50176          * @param {Number} newIndex
50177          */
50178         "columnmoved" : true,
50179         /**
50180          * @event columlockchange
50181          * Fires when a column's locked state is changed
50182          * @param {ColumnModel} this
50183          * @param {Number} colIndex
50184          * @param {Boolean} locked true if locked
50185          */
50186         "columnlockchange" : true
50187     });
50188     Roo.grid.ColumnModel.superclass.constructor.call(this);
50189 };
50190 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50191     /**
50192      * @cfg {String} header The header text to display in the Grid view.
50193      */
50194     /**
50195      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50196      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50197      * specified, the column's index is used as an index into the Record's data Array.
50198      */
50199     /**
50200      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50201      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50202      */
50203     /**
50204      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50205      * Defaults to the value of the {@link #defaultSortable} property.
50206      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50207      */
50208     /**
50209      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50210      */
50211     /**
50212      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50213      */
50214     /**
50215      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50216      */
50217     /**
50218      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50219      */
50220     /**
50221      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50222      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50223      * default renderer uses the raw data value.
50224      */
50225        /**
50226      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50227      */
50228     /**
50229      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50230      */
50231
50232     /**
50233      * Returns the id of the column at the specified index.
50234      * @param {Number} index The column index
50235      * @return {String} the id
50236      */
50237     getColumnId : function(index){
50238         return this.config[index].id;
50239     },
50240
50241     /**
50242      * Returns the column for a specified id.
50243      * @param {String} id The column id
50244      * @return {Object} the column
50245      */
50246     getColumnById : function(id){
50247         return this.lookup[id];
50248     },
50249
50250     
50251     /**
50252      * Returns the column for a specified dataIndex.
50253      * @param {String} dataIndex The column dataIndex
50254      * @return {Object|Boolean} the column or false if not found
50255      */
50256     getColumnByDataIndex: function(dataIndex){
50257         var index = this.findColumnIndex(dataIndex);
50258         return index > -1 ? this.config[index] : false;
50259     },
50260     
50261     /**
50262      * Returns the index for a specified column id.
50263      * @param {String} id The column id
50264      * @return {Number} the index, or -1 if not found
50265      */
50266     getIndexById : function(id){
50267         for(var i = 0, len = this.config.length; i < len; i++){
50268             if(this.config[i].id == id){
50269                 return i;
50270             }
50271         }
50272         return -1;
50273     },
50274     
50275     /**
50276      * Returns the index for a specified column dataIndex.
50277      * @param {String} dataIndex The column dataIndex
50278      * @return {Number} the index, or -1 if not found
50279      */
50280     
50281     findColumnIndex : function(dataIndex){
50282         for(var i = 0, len = this.config.length; i < len; i++){
50283             if(this.config[i].dataIndex == dataIndex){
50284                 return i;
50285             }
50286         }
50287         return -1;
50288     },
50289     
50290     
50291     moveColumn : function(oldIndex, newIndex){
50292         var c = this.config[oldIndex];
50293         this.config.splice(oldIndex, 1);
50294         this.config.splice(newIndex, 0, c);
50295         this.dataMap = null;
50296         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50297     },
50298
50299     isLocked : function(colIndex){
50300         return this.config[colIndex].locked === true;
50301     },
50302
50303     setLocked : function(colIndex, value, suppressEvent){
50304         if(this.isLocked(colIndex) == value){
50305             return;
50306         }
50307         this.config[colIndex].locked = value;
50308         if(!suppressEvent){
50309             this.fireEvent("columnlockchange", this, colIndex, value);
50310         }
50311     },
50312
50313     getTotalLockedWidth : function(){
50314         var totalWidth = 0;
50315         for(var i = 0; i < this.config.length; i++){
50316             if(this.isLocked(i) && !this.isHidden(i)){
50317                 this.totalWidth += this.getColumnWidth(i);
50318             }
50319         }
50320         return totalWidth;
50321     },
50322
50323     getLockedCount : function(){
50324         for(var i = 0, len = this.config.length; i < len; i++){
50325             if(!this.isLocked(i)){
50326                 return i;
50327             }
50328         }
50329     },
50330
50331     /**
50332      * Returns the number of columns.
50333      * @return {Number}
50334      */
50335     getColumnCount : function(visibleOnly){
50336         if(visibleOnly === true){
50337             var c = 0;
50338             for(var i = 0, len = this.config.length; i < len; i++){
50339                 if(!this.isHidden(i)){
50340                     c++;
50341                 }
50342             }
50343             return c;
50344         }
50345         return this.config.length;
50346     },
50347
50348     /**
50349      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50350      * @param {Function} fn
50351      * @param {Object} scope (optional)
50352      * @return {Array} result
50353      */
50354     getColumnsBy : function(fn, scope){
50355         var r = [];
50356         for(var i = 0, len = this.config.length; i < len; i++){
50357             var c = this.config[i];
50358             if(fn.call(scope||this, c, i) === true){
50359                 r[r.length] = c;
50360             }
50361         }
50362         return r;
50363     },
50364
50365     /**
50366      * Returns true if the specified column is sortable.
50367      * @param {Number} col The column index
50368      * @return {Boolean}
50369      */
50370     isSortable : function(col){
50371         if(typeof this.config[col].sortable == "undefined"){
50372             return this.defaultSortable;
50373         }
50374         return this.config[col].sortable;
50375     },
50376
50377     /**
50378      * Returns the rendering (formatting) function defined for the column.
50379      * @param {Number} col The column index.
50380      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50381      */
50382     getRenderer : function(col){
50383         if(!this.config[col].renderer){
50384             return Roo.grid.ColumnModel.defaultRenderer;
50385         }
50386         return this.config[col].renderer;
50387     },
50388
50389     /**
50390      * Sets the rendering (formatting) function for a column.
50391      * @param {Number} col The column index
50392      * @param {Function} fn The function to use to process the cell's raw data
50393      * to return HTML markup for the grid view. The render function is called with
50394      * the following parameters:<ul>
50395      * <li>Data value.</li>
50396      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50397      * <li>css A CSS style string to apply to the table cell.</li>
50398      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50399      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50400      * <li>Row index</li>
50401      * <li>Column index</li>
50402      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50403      */
50404     setRenderer : function(col, fn){
50405         this.config[col].renderer = fn;
50406     },
50407
50408     /**
50409      * Returns the width for the specified column.
50410      * @param {Number} col The column index
50411      * @return {Number}
50412      */
50413     getColumnWidth : function(col){
50414         return this.config[col].width * 1 || this.defaultWidth;
50415     },
50416
50417     /**
50418      * Sets the width for a column.
50419      * @param {Number} col The column index
50420      * @param {Number} width The new width
50421      */
50422     setColumnWidth : function(col, width, suppressEvent){
50423         this.config[col].width = width;
50424         this.totalWidth = null;
50425         if(!suppressEvent){
50426              this.fireEvent("widthchange", this, col, width);
50427         }
50428     },
50429
50430     /**
50431      * Returns the total width of all columns.
50432      * @param {Boolean} includeHidden True to include hidden column widths
50433      * @return {Number}
50434      */
50435     getTotalWidth : function(includeHidden){
50436         if(!this.totalWidth){
50437             this.totalWidth = 0;
50438             for(var i = 0, len = this.config.length; i < len; i++){
50439                 if(includeHidden || !this.isHidden(i)){
50440                     this.totalWidth += this.getColumnWidth(i);
50441                 }
50442             }
50443         }
50444         return this.totalWidth;
50445     },
50446
50447     /**
50448      * Returns the header for the specified column.
50449      * @param {Number} col The column index
50450      * @return {String}
50451      */
50452     getColumnHeader : function(col){
50453         return this.config[col].header;
50454     },
50455
50456     /**
50457      * Sets the header for a column.
50458      * @param {Number} col The column index
50459      * @param {String} header The new header
50460      */
50461     setColumnHeader : function(col, header){
50462         this.config[col].header = header;
50463         this.fireEvent("headerchange", this, col, header);
50464     },
50465
50466     /**
50467      * Returns the tooltip for the specified column.
50468      * @param {Number} col The column index
50469      * @return {String}
50470      */
50471     getColumnTooltip : function(col){
50472             return this.config[col].tooltip;
50473     },
50474     /**
50475      * Sets the tooltip for a column.
50476      * @param {Number} col The column index
50477      * @param {String} tooltip The new tooltip
50478      */
50479     setColumnTooltip : function(col, tooltip){
50480             this.config[col].tooltip = tooltip;
50481     },
50482
50483     /**
50484      * Returns the dataIndex for the specified column.
50485      * @param {Number} col The column index
50486      * @return {Number}
50487      */
50488     getDataIndex : function(col){
50489         return this.config[col].dataIndex;
50490     },
50491
50492     /**
50493      * Sets the dataIndex for a column.
50494      * @param {Number} col The column index
50495      * @param {Number} dataIndex The new dataIndex
50496      */
50497     setDataIndex : function(col, dataIndex){
50498         this.config[col].dataIndex = dataIndex;
50499     },
50500
50501     
50502     
50503     /**
50504      * Returns true if the cell is editable.
50505      * @param {Number} colIndex The column index
50506      * @param {Number} rowIndex The row index
50507      * @return {Boolean}
50508      */
50509     isCellEditable : function(colIndex, rowIndex){
50510         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50511     },
50512
50513     /**
50514      * Returns the editor defined for the cell/column.
50515      * return false or null to disable editing.
50516      * @param {Number} colIndex The column index
50517      * @param {Number} rowIndex The row index
50518      * @return {Object}
50519      */
50520     getCellEditor : function(colIndex, rowIndex){
50521         return this.config[colIndex].editor;
50522     },
50523
50524     /**
50525      * Sets if a column is editable.
50526      * @param {Number} col The column index
50527      * @param {Boolean} editable True if the column is editable
50528      */
50529     setEditable : function(col, editable){
50530         this.config[col].editable = editable;
50531     },
50532
50533
50534     /**
50535      * Returns true if the column is hidden.
50536      * @param {Number} colIndex The column index
50537      * @return {Boolean}
50538      */
50539     isHidden : function(colIndex){
50540         return this.config[colIndex].hidden;
50541     },
50542
50543
50544     /**
50545      * Returns true if the column width cannot be changed
50546      */
50547     isFixed : function(colIndex){
50548         return this.config[colIndex].fixed;
50549     },
50550
50551     /**
50552      * Returns true if the column can be resized
50553      * @return {Boolean}
50554      */
50555     isResizable : function(colIndex){
50556         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50557     },
50558     /**
50559      * Sets if a column is hidden.
50560      * @param {Number} colIndex The column index
50561      * @param {Boolean} hidden True if the column is hidden
50562      */
50563     setHidden : function(colIndex, hidden){
50564         this.config[colIndex].hidden = hidden;
50565         this.totalWidth = null;
50566         this.fireEvent("hiddenchange", this, colIndex, hidden);
50567     },
50568
50569     /**
50570      * Sets the editor for a column.
50571      * @param {Number} col The column index
50572      * @param {Object} editor The editor object
50573      */
50574     setEditor : function(col, editor){
50575         this.config[col].editor = editor;
50576     }
50577 });
50578
50579 Roo.grid.ColumnModel.defaultRenderer = function(value){
50580         if(typeof value == "string" && value.length < 1){
50581             return "&#160;";
50582         }
50583         return value;
50584 };
50585
50586 // Alias for backwards compatibility
50587 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50588 /*
50589  * Based on:
50590  * Ext JS Library 1.1.1
50591  * Copyright(c) 2006-2007, Ext JS, LLC.
50592  *
50593  * Originally Released Under LGPL - original licence link has changed is not relivant.
50594  *
50595  * Fork - LGPL
50596  * <script type="text/javascript">
50597  */
50598
50599 /**
50600  * @class Roo.grid.AbstractSelectionModel
50601  * @extends Roo.util.Observable
50602  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50603  * implemented by descendant classes.  This class should not be directly instantiated.
50604  * @constructor
50605  */
50606 Roo.grid.AbstractSelectionModel = function(){
50607     this.locked = false;
50608     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50609 };
50610
50611 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50612     /** @ignore Called by the grid automatically. Do not call directly. */
50613     init : function(grid){
50614         this.grid = grid;
50615         this.initEvents();
50616     },
50617
50618     /**
50619      * Locks the selections.
50620      */
50621     lock : function(){
50622         this.locked = true;
50623     },
50624
50625     /**
50626      * Unlocks the selections.
50627      */
50628     unlock : function(){
50629         this.locked = false;
50630     },
50631
50632     /**
50633      * Returns true if the selections are locked.
50634      * @return {Boolean}
50635      */
50636     isLocked : function(){
50637         return this.locked;
50638     }
50639 });/*
50640  * Based on:
50641  * Ext JS Library 1.1.1
50642  * Copyright(c) 2006-2007, Ext JS, LLC.
50643  *
50644  * Originally Released Under LGPL - original licence link has changed is not relivant.
50645  *
50646  * Fork - LGPL
50647  * <script type="text/javascript">
50648  */
50649 /**
50650  * @extends Roo.grid.AbstractSelectionModel
50651  * @class Roo.grid.RowSelectionModel
50652  * The default SelectionModel used by {@link Roo.grid.Grid}.
50653  * It supports multiple selections and keyboard selection/navigation. 
50654  * @constructor
50655  * @param {Object} config
50656  */
50657 Roo.grid.RowSelectionModel = function(config){
50658     Roo.apply(this, config);
50659     this.selections = new Roo.util.MixedCollection(false, function(o){
50660         return o.id;
50661     });
50662
50663     this.last = false;
50664     this.lastActive = false;
50665
50666     this.addEvents({
50667         /**
50668              * @event selectionchange
50669              * Fires when the selection changes
50670              * @param {SelectionModel} this
50671              */
50672             "selectionchange" : true,
50673         /**
50674              * @event afterselectionchange
50675              * Fires after the selection changes (eg. by key press or clicking)
50676              * @param {SelectionModel} this
50677              */
50678             "afterselectionchange" : true,
50679         /**
50680              * @event beforerowselect
50681              * Fires when a row is selected being selected, return false to cancel.
50682              * @param {SelectionModel} this
50683              * @param {Number} rowIndex The selected index
50684              * @param {Boolean} keepExisting False if other selections will be cleared
50685              */
50686             "beforerowselect" : true,
50687         /**
50688              * @event rowselect
50689              * Fires when a row is selected.
50690              * @param {SelectionModel} this
50691              * @param {Number} rowIndex The selected index
50692              * @param {Roo.data.Record} r The record
50693              */
50694             "rowselect" : true,
50695         /**
50696              * @event rowdeselect
50697              * Fires when a row is deselected.
50698              * @param {SelectionModel} this
50699              * @param {Number} rowIndex The selected index
50700              */
50701         "rowdeselect" : true
50702     });
50703     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50704     this.locked = false;
50705 };
50706
50707 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50708     /**
50709      * @cfg {Boolean} singleSelect
50710      * True to allow selection of only one row at a time (defaults to false)
50711      */
50712     singleSelect : false,
50713
50714     // private
50715     initEvents : function(){
50716
50717         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50718             this.grid.on("mousedown", this.handleMouseDown, this);
50719         }else{ // allow click to work like normal
50720             this.grid.on("rowclick", this.handleDragableRowClick, this);
50721         }
50722
50723         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50724             "up" : function(e){
50725                 if(!e.shiftKey){
50726                     this.selectPrevious(e.shiftKey);
50727                 }else if(this.last !== false && this.lastActive !== false){
50728                     var last = this.last;
50729                     this.selectRange(this.last,  this.lastActive-1);
50730                     this.grid.getView().focusRow(this.lastActive);
50731                     if(last !== false){
50732                         this.last = last;
50733                     }
50734                 }else{
50735                     this.selectFirstRow();
50736                 }
50737                 this.fireEvent("afterselectionchange", this);
50738             },
50739             "down" : function(e){
50740                 if(!e.shiftKey){
50741                     this.selectNext(e.shiftKey);
50742                 }else if(this.last !== false && this.lastActive !== false){
50743                     var last = this.last;
50744                     this.selectRange(this.last,  this.lastActive+1);
50745                     this.grid.getView().focusRow(this.lastActive);
50746                     if(last !== false){
50747                         this.last = last;
50748                     }
50749                 }else{
50750                     this.selectFirstRow();
50751                 }
50752                 this.fireEvent("afterselectionchange", this);
50753             },
50754             scope: this
50755         });
50756
50757         var view = this.grid.view;
50758         view.on("refresh", this.onRefresh, this);
50759         view.on("rowupdated", this.onRowUpdated, this);
50760         view.on("rowremoved", this.onRemove, this);
50761     },
50762
50763     // private
50764     onRefresh : function(){
50765         var ds = this.grid.dataSource, i, v = this.grid.view;
50766         var s = this.selections;
50767         s.each(function(r){
50768             if((i = ds.indexOfId(r.id)) != -1){
50769                 v.onRowSelect(i);
50770             }else{
50771                 s.remove(r);
50772             }
50773         });
50774     },
50775
50776     // private
50777     onRemove : function(v, index, r){
50778         this.selections.remove(r);
50779     },
50780
50781     // private
50782     onRowUpdated : function(v, index, r){
50783         if(this.isSelected(r)){
50784             v.onRowSelect(index);
50785         }
50786     },
50787
50788     /**
50789      * Select records.
50790      * @param {Array} records The records to select
50791      * @param {Boolean} keepExisting (optional) True to keep existing selections
50792      */
50793     selectRecords : function(records, keepExisting){
50794         if(!keepExisting){
50795             this.clearSelections();
50796         }
50797         var ds = this.grid.dataSource;
50798         for(var i = 0, len = records.length; i < len; i++){
50799             this.selectRow(ds.indexOf(records[i]), true);
50800         }
50801     },
50802
50803     /**
50804      * Gets the number of selected rows.
50805      * @return {Number}
50806      */
50807     getCount : function(){
50808         return this.selections.length;
50809     },
50810
50811     /**
50812      * Selects the first row in the grid.
50813      */
50814     selectFirstRow : function(){
50815         this.selectRow(0);
50816     },
50817
50818     /**
50819      * Select the last row.
50820      * @param {Boolean} keepExisting (optional) True to keep existing selections
50821      */
50822     selectLastRow : function(keepExisting){
50823         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50824     },
50825
50826     /**
50827      * Selects the row immediately following the last selected row.
50828      * @param {Boolean} keepExisting (optional) True to keep existing selections
50829      */
50830     selectNext : function(keepExisting){
50831         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50832             this.selectRow(this.last+1, keepExisting);
50833             this.grid.getView().focusRow(this.last);
50834         }
50835     },
50836
50837     /**
50838      * Selects the row that precedes the last selected row.
50839      * @param {Boolean} keepExisting (optional) True to keep existing selections
50840      */
50841     selectPrevious : function(keepExisting){
50842         if(this.last){
50843             this.selectRow(this.last-1, keepExisting);
50844             this.grid.getView().focusRow(this.last);
50845         }
50846     },
50847
50848     /**
50849      * Returns the selected records
50850      * @return {Array} Array of selected records
50851      */
50852     getSelections : function(){
50853         return [].concat(this.selections.items);
50854     },
50855
50856     /**
50857      * Returns the first selected record.
50858      * @return {Record}
50859      */
50860     getSelected : function(){
50861         return this.selections.itemAt(0);
50862     },
50863
50864
50865     /**
50866      * Clears all selections.
50867      */
50868     clearSelections : function(fast){
50869         if(this.locked) return;
50870         if(fast !== true){
50871             var ds = this.grid.dataSource;
50872             var s = this.selections;
50873             s.each(function(r){
50874                 this.deselectRow(ds.indexOfId(r.id));
50875             }, this);
50876             s.clear();
50877         }else{
50878             this.selections.clear();
50879         }
50880         this.last = false;
50881     },
50882
50883
50884     /**
50885      * Selects all rows.
50886      */
50887     selectAll : function(){
50888         if(this.locked) return;
50889         this.selections.clear();
50890         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50891             this.selectRow(i, true);
50892         }
50893     },
50894
50895     /**
50896      * Returns True if there is a selection.
50897      * @return {Boolean}
50898      */
50899     hasSelection : function(){
50900         return this.selections.length > 0;
50901     },
50902
50903     /**
50904      * Returns True if the specified row is selected.
50905      * @param {Number/Record} record The record or index of the record to check
50906      * @return {Boolean}
50907      */
50908     isSelected : function(index){
50909         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50910         return (r && this.selections.key(r.id) ? true : false);
50911     },
50912
50913     /**
50914      * Returns True if the specified record id is selected.
50915      * @param {String} id The id of record to check
50916      * @return {Boolean}
50917      */
50918     isIdSelected : function(id){
50919         return (this.selections.key(id) ? true : false);
50920     },
50921
50922     // private
50923     handleMouseDown : function(e, t){
50924         var view = this.grid.getView(), rowIndex;
50925         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50926             return;
50927         };
50928         if(e.shiftKey && this.last !== false){
50929             var last = this.last;
50930             this.selectRange(last, rowIndex, e.ctrlKey);
50931             this.last = last; // reset the last
50932             view.focusRow(rowIndex);
50933         }else{
50934             var isSelected = this.isSelected(rowIndex);
50935             if(e.button !== 0 && isSelected){
50936                 view.focusRow(rowIndex);
50937             }else if(e.ctrlKey && isSelected){
50938                 this.deselectRow(rowIndex);
50939             }else if(!isSelected){
50940                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50941                 view.focusRow(rowIndex);
50942             }
50943         }
50944         this.fireEvent("afterselectionchange", this);
50945     },
50946     // private
50947     handleDragableRowClick :  function(grid, rowIndex, e) 
50948     {
50949         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50950             this.selectRow(rowIndex, false);
50951             grid.view.focusRow(rowIndex);
50952              this.fireEvent("afterselectionchange", this);
50953         }
50954     },
50955     
50956     /**
50957      * Selects multiple rows.
50958      * @param {Array} rows Array of the indexes of the row to select
50959      * @param {Boolean} keepExisting (optional) True to keep existing selections
50960      */
50961     selectRows : function(rows, keepExisting){
50962         if(!keepExisting){
50963             this.clearSelections();
50964         }
50965         for(var i = 0, len = rows.length; i < len; i++){
50966             this.selectRow(rows[i], true);
50967         }
50968     },
50969
50970     /**
50971      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50972      * @param {Number} startRow The index of the first row in the range
50973      * @param {Number} endRow The index of the last row in the range
50974      * @param {Boolean} keepExisting (optional) True to retain existing selections
50975      */
50976     selectRange : function(startRow, endRow, keepExisting){
50977         if(this.locked) return;
50978         if(!keepExisting){
50979             this.clearSelections();
50980         }
50981         if(startRow <= endRow){
50982             for(var i = startRow; i <= endRow; i++){
50983                 this.selectRow(i, true);
50984             }
50985         }else{
50986             for(var i = startRow; i >= endRow; i--){
50987                 this.selectRow(i, true);
50988             }
50989         }
50990     },
50991
50992     /**
50993      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50994      * @param {Number} startRow The index of the first row in the range
50995      * @param {Number} endRow The index of the last row in the range
50996      */
50997     deselectRange : function(startRow, endRow, preventViewNotify){
50998         if(this.locked) return;
50999         for(var i = startRow; i <= endRow; i++){
51000             this.deselectRow(i, preventViewNotify);
51001         }
51002     },
51003
51004     /**
51005      * Selects a row.
51006      * @param {Number} row The index of the row to select
51007      * @param {Boolean} keepExisting (optional) True to keep existing selections
51008      */
51009     selectRow : function(index, keepExisting, preventViewNotify){
51010         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51011         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51012             if(!keepExisting || this.singleSelect){
51013                 this.clearSelections();
51014             }
51015             var r = this.grid.dataSource.getAt(index);
51016             this.selections.add(r);
51017             this.last = this.lastActive = index;
51018             if(!preventViewNotify){
51019                 this.grid.getView().onRowSelect(index);
51020             }
51021             this.fireEvent("rowselect", this, index, r);
51022             this.fireEvent("selectionchange", this);
51023         }
51024     },
51025
51026     /**
51027      * Deselects a row.
51028      * @param {Number} row The index of the row to deselect
51029      */
51030     deselectRow : function(index, preventViewNotify){
51031         if(this.locked) return;
51032         if(this.last == index){
51033             this.last = false;
51034         }
51035         if(this.lastActive == index){
51036             this.lastActive = false;
51037         }
51038         var r = this.grid.dataSource.getAt(index);
51039         this.selections.remove(r);
51040         if(!preventViewNotify){
51041             this.grid.getView().onRowDeselect(index);
51042         }
51043         this.fireEvent("rowdeselect", this, index);
51044         this.fireEvent("selectionchange", this);
51045     },
51046
51047     // private
51048     restoreLast : function(){
51049         if(this._last){
51050             this.last = this._last;
51051         }
51052     },
51053
51054     // private
51055     acceptsNav : function(row, col, cm){
51056         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51057     },
51058
51059     // private
51060     onEditorKey : function(field, e){
51061         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51062         if(k == e.TAB){
51063             e.stopEvent();
51064             ed.completeEdit();
51065             if(e.shiftKey){
51066                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51067             }else{
51068                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51069             }
51070         }else if(k == e.ENTER && !e.ctrlKey){
51071             e.stopEvent();
51072             ed.completeEdit();
51073             if(e.shiftKey){
51074                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51075             }else{
51076                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51077             }
51078         }else if(k == e.ESC){
51079             ed.cancelEdit();
51080         }
51081         if(newCell){
51082             g.startEditing(newCell[0], newCell[1]);
51083         }
51084     }
51085 });/*
51086  * Based on:
51087  * Ext JS Library 1.1.1
51088  * Copyright(c) 2006-2007, Ext JS, LLC.
51089  *
51090  * Originally Released Under LGPL - original licence link has changed is not relivant.
51091  *
51092  * Fork - LGPL
51093  * <script type="text/javascript">
51094  */
51095 /**
51096  * @class Roo.grid.CellSelectionModel
51097  * @extends Roo.grid.AbstractSelectionModel
51098  * This class provides the basic implementation for cell selection in a grid.
51099  * @constructor
51100  * @param {Object} config The object containing the configuration of this model.
51101  */
51102 Roo.grid.CellSelectionModel = function(config){
51103     Roo.apply(this, config);
51104
51105     this.selection = null;
51106
51107     this.addEvents({
51108         /**
51109              * @event beforerowselect
51110              * Fires before a cell is selected.
51111              * @param {SelectionModel} this
51112              * @param {Number} rowIndex The selected row index
51113              * @param {Number} colIndex The selected cell index
51114              */
51115             "beforecellselect" : true,
51116         /**
51117              * @event cellselect
51118              * Fires when a cell is selected.
51119              * @param {SelectionModel} this
51120              * @param {Number} rowIndex The selected row index
51121              * @param {Number} colIndex The selected cell index
51122              */
51123             "cellselect" : true,
51124         /**
51125              * @event selectionchange
51126              * Fires when the active selection changes.
51127              * @param {SelectionModel} this
51128              * @param {Object} selection null for no selection or an object (o) with two properties
51129                 <ul>
51130                 <li>o.record: the record object for the row the selection is in</li>
51131                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51132                 </ul>
51133              */
51134             "selectionchange" : true
51135     });
51136     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51137 };
51138
51139 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51140
51141     /** @ignore */
51142     initEvents : function(){
51143         this.grid.on("mousedown", this.handleMouseDown, this);
51144         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51145         var view = this.grid.view;
51146         view.on("refresh", this.onViewChange, this);
51147         view.on("rowupdated", this.onRowUpdated, this);
51148         view.on("beforerowremoved", this.clearSelections, this);
51149         view.on("beforerowsinserted", this.clearSelections, this);
51150         if(this.grid.isEditor){
51151             this.grid.on("beforeedit", this.beforeEdit,  this);
51152         }
51153     },
51154
51155         //private
51156     beforeEdit : function(e){
51157         this.select(e.row, e.column, false, true, e.record);
51158     },
51159
51160         //private
51161     onRowUpdated : function(v, index, r){
51162         if(this.selection && this.selection.record == r){
51163             v.onCellSelect(index, this.selection.cell[1]);
51164         }
51165     },
51166
51167         //private
51168     onViewChange : function(){
51169         this.clearSelections(true);
51170     },
51171
51172         /**
51173          * Returns the currently selected cell,.
51174          * @return {Array} The selected cell (row, column) or null if none selected.
51175          */
51176     getSelectedCell : function(){
51177         return this.selection ? this.selection.cell : null;
51178     },
51179
51180     /**
51181      * Clears all selections.
51182      * @param {Boolean} true to prevent the gridview from being notified about the change.
51183      */
51184     clearSelections : function(preventNotify){
51185         var s = this.selection;
51186         if(s){
51187             if(preventNotify !== true){
51188                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51189             }
51190             this.selection = null;
51191             this.fireEvent("selectionchange", this, null);
51192         }
51193     },
51194
51195     /**
51196      * Returns true if there is a selection.
51197      * @return {Boolean}
51198      */
51199     hasSelection : function(){
51200         return this.selection ? true : false;
51201     },
51202
51203     /** @ignore */
51204     handleMouseDown : function(e, t){
51205         var v = this.grid.getView();
51206         if(this.isLocked()){
51207             return;
51208         };
51209         var row = v.findRowIndex(t);
51210         var cell = v.findCellIndex(t);
51211         if(row !== false && cell !== false){
51212             this.select(row, cell);
51213         }
51214     },
51215
51216     /**
51217      * Selects a cell.
51218      * @param {Number} rowIndex
51219      * @param {Number} collIndex
51220      */
51221     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51222         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51223             this.clearSelections();
51224             r = r || this.grid.dataSource.getAt(rowIndex);
51225             this.selection = {
51226                 record : r,
51227                 cell : [rowIndex, colIndex]
51228             };
51229             if(!preventViewNotify){
51230                 var v = this.grid.getView();
51231                 v.onCellSelect(rowIndex, colIndex);
51232                 if(preventFocus !== true){
51233                     v.focusCell(rowIndex, colIndex);
51234                 }
51235             }
51236             this.fireEvent("cellselect", this, rowIndex, colIndex);
51237             this.fireEvent("selectionchange", this, this.selection);
51238         }
51239     },
51240
51241         //private
51242     isSelectable : function(rowIndex, colIndex, cm){
51243         return !cm.isHidden(colIndex);
51244     },
51245
51246     /** @ignore */
51247     handleKeyDown : function(e){
51248         //Roo.log('Cell Sel Model handleKeyDown');
51249         if(!e.isNavKeyPress()){
51250             return;
51251         }
51252         var g = this.grid, s = this.selection;
51253         if(!s){
51254             e.stopEvent();
51255             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51256             if(cell){
51257                 this.select(cell[0], cell[1]);
51258             }
51259             return;
51260         }
51261         var sm = this;
51262         var walk = function(row, col, step){
51263             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51264         };
51265         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51266         var newCell;
51267
51268         switch(k){
51269             case e.TAB:
51270                 // handled by onEditorKey
51271                 if (g.isEditor && g.editing) {
51272                     return;
51273                 }
51274                 if(e.shiftKey){
51275                      newCell = walk(r, c-1, -1);
51276                 }else{
51277                      newCell = walk(r, c+1, 1);
51278                 }
51279              break;
51280              case e.DOWN:
51281                  newCell = walk(r+1, c, 1);
51282              break;
51283              case e.UP:
51284                  newCell = walk(r-1, c, -1);
51285              break;
51286              case e.RIGHT:
51287                  newCell = walk(r, c+1, 1);
51288              break;
51289              case e.LEFT:
51290                  newCell = walk(r, c-1, -1);
51291              break;
51292              case e.ENTER:
51293                  if(g.isEditor && !g.editing){
51294                     g.startEditing(r, c);
51295                     e.stopEvent();
51296                     return;
51297                 }
51298              break;
51299         };
51300         if(newCell){
51301             this.select(newCell[0], newCell[1]);
51302             e.stopEvent();
51303         }
51304     },
51305
51306     acceptsNav : function(row, col, cm){
51307         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51308     },
51309     /**
51310      * Selects a cell.
51311      * @param {Number} field (not used) - as it's normally used as a listener
51312      * @param {Number} e - event - fake it by using
51313      *
51314      * var e = Roo.EventObjectImpl.prototype;
51315      * e.keyCode = e.TAB
51316      *
51317      * 
51318      */
51319     onEditorKey : function(field, e){
51320         
51321         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51322         ///Roo.log('onEditorKey' + k);
51323         if (!ed) {
51324             
51325             
51326             
51327         }
51328         if(k == e.TAB){
51329             if(e.shiftKey){
51330                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51331             }else{
51332                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51333             }
51334             
51335             e.stopEvent();
51336             
51337         }else if(k == e.ENTER &&  !e.ctrlKey){
51338             ed.completeEdit();
51339             e.stopEvent();
51340             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51341         }else if(k == e.ESC){
51342             ed.cancelEdit();
51343         }
51344         
51345         
51346         if(newCell){
51347             //Roo.log('next cell after edit');
51348             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51349         }
51350     }
51351 });/*
51352  * Based on:
51353  * Ext JS Library 1.1.1
51354  * Copyright(c) 2006-2007, Ext JS, LLC.
51355  *
51356  * Originally Released Under LGPL - original licence link has changed is not relivant.
51357  *
51358  * Fork - LGPL
51359  * <script type="text/javascript">
51360  */
51361  
51362 /**
51363  * @class Roo.grid.EditorGrid
51364  * @extends Roo.grid.Grid
51365  * Class for creating and editable grid.
51366  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51367  * The container MUST have some type of size defined for the grid to fill. The container will be 
51368  * automatically set to position relative if it isn't already.
51369  * @param {Object} dataSource The data model to bind to
51370  * @param {Object} colModel The column model with info about this grid's columns
51371  */
51372 Roo.grid.EditorGrid = function(container, config){
51373     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51374     this.getGridEl().addClass("xedit-grid");
51375
51376     if(!this.selModel){
51377         this.selModel = new Roo.grid.CellSelectionModel();
51378     }
51379
51380     this.activeEditor = null;
51381
51382         this.addEvents({
51383             /**
51384              * @event beforeedit
51385              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51386              * <ul style="padding:5px;padding-left:16px;">
51387              * <li>grid - This grid</li>
51388              * <li>record - The record being edited</li>
51389              * <li>field - The field name being edited</li>
51390              * <li>value - The value for the field being edited.</li>
51391              * <li>row - The grid row index</li>
51392              * <li>column - The grid column index</li>
51393              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51394              * </ul>
51395              * @param {Object} e An edit event (see above for description)
51396              */
51397             "beforeedit" : true,
51398             /**
51399              * @event afteredit
51400              * Fires after a cell is edited. <br />
51401              * <ul style="padding:5px;padding-left:16px;">
51402              * <li>grid - This grid</li>
51403              * <li>record - The record being edited</li>
51404              * <li>field - The field name being edited</li>
51405              * <li>value - The value being set</li>
51406              * <li>originalValue - The original value for the field, before the edit.</li>
51407              * <li>row - The grid row index</li>
51408              * <li>column - The grid column index</li>
51409              * </ul>
51410              * @param {Object} e An edit event (see above for description)
51411              */
51412             "afteredit" : true,
51413             /**
51414              * @event validateedit
51415              * Fires after a cell is edited, but before the value is set in the record. 
51416          * You can use this to modify the value being set in the field, Return false
51417              * to cancel the change. The edit event object has the following properties <br />
51418              * <ul style="padding:5px;padding-left:16px;">
51419          * <li>editor - This editor</li>
51420              * <li>grid - This grid</li>
51421              * <li>record - The record being edited</li>
51422              * <li>field - The field name being edited</li>
51423              * <li>value - The value being set</li>
51424              * <li>originalValue - The original value for the field, before the edit.</li>
51425              * <li>row - The grid row index</li>
51426              * <li>column - The grid column index</li>
51427              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51428              * </ul>
51429              * @param {Object} e An edit event (see above for description)
51430              */
51431             "validateedit" : true
51432         });
51433     this.on("bodyscroll", this.stopEditing,  this);
51434     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51435 };
51436
51437 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51438     /**
51439      * @cfg {Number} clicksToEdit
51440      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51441      */
51442     clicksToEdit: 2,
51443
51444     // private
51445     isEditor : true,
51446     // private
51447     trackMouseOver: false, // causes very odd FF errors
51448
51449     onCellDblClick : function(g, row, col){
51450         this.startEditing(row, col);
51451     },
51452
51453     onEditComplete : function(ed, value, startValue){
51454         this.editing = false;
51455         this.activeEditor = null;
51456         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51457         var r = ed.record;
51458         var field = this.colModel.getDataIndex(ed.col);
51459         var e = {
51460             grid: this,
51461             record: r,
51462             field: field,
51463             originalValue: startValue,
51464             value: value,
51465             row: ed.row,
51466             column: ed.col,
51467             cancel:false,
51468             editor: ed
51469         };
51470         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51471         cell.show();
51472           
51473         if(String(value) !== String(startValue)){
51474             
51475             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51476                 r.set(field, e.value);
51477                 // if we are dealing with a combo box..
51478                 // then we also set the 'name' colum to be the displayField
51479                 if (ed.field.displayField && ed.field.name) {
51480                     r.set(ed.field.name, ed.field.el.dom.value);
51481                 }
51482                 
51483                 delete e.cancel; //?? why!!!
51484                 this.fireEvent("afteredit", e);
51485             }
51486         } else {
51487             this.fireEvent("afteredit", e); // always fire it!
51488         }
51489         this.view.focusCell(ed.row, ed.col);
51490     },
51491
51492     /**
51493      * Starts editing the specified for the specified row/column
51494      * @param {Number} rowIndex
51495      * @param {Number} colIndex
51496      */
51497     startEditing : function(row, col){
51498         this.stopEditing();
51499         if(this.colModel.isCellEditable(col, row)){
51500             this.view.ensureVisible(row, col, true);
51501           
51502             var r = this.dataSource.getAt(row);
51503             var field = this.colModel.getDataIndex(col);
51504             var cell = Roo.get(this.view.getCell(row,col));
51505             var e = {
51506                 grid: this,
51507                 record: r,
51508                 field: field,
51509                 value: r.data[field],
51510                 row: row,
51511                 column: col,
51512                 cancel:false 
51513             };
51514             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51515                 this.editing = true;
51516                 var ed = this.colModel.getCellEditor(col, row);
51517                 
51518                 if (!ed) {
51519                     return;
51520                 }
51521                 if(!ed.rendered){
51522                     ed.render(ed.parentEl || document.body);
51523                 }
51524                 ed.field.reset();
51525                
51526                 cell.hide();
51527                 
51528                 (function(){ // complex but required for focus issues in safari, ie and opera
51529                     ed.row = row;
51530                     ed.col = col;
51531                     ed.record = r;
51532                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51533                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51534                     this.activeEditor = ed;
51535                     var v = r.data[field];
51536                     ed.startEdit(this.view.getCell(row, col), v);
51537                     // combo's with 'displayField and name set
51538                     if (ed.field.displayField && ed.field.name) {
51539                         ed.field.el.dom.value = r.data[ed.field.name];
51540                     }
51541                     
51542                     
51543                 }).defer(50, this);
51544             }
51545         }
51546     },
51547         
51548     /**
51549      * Stops any active editing
51550      */
51551     stopEditing : function(){
51552         if(this.activeEditor){
51553             this.activeEditor.completeEdit();
51554         }
51555         this.activeEditor = null;
51556     }
51557 });/*
51558  * Based on:
51559  * Ext JS Library 1.1.1
51560  * Copyright(c) 2006-2007, Ext JS, LLC.
51561  *
51562  * Originally Released Under LGPL - original licence link has changed is not relivant.
51563  *
51564  * Fork - LGPL
51565  * <script type="text/javascript">
51566  */
51567
51568 // private - not really -- you end up using it !
51569 // This is a support class used internally by the Grid components
51570
51571 /**
51572  * @class Roo.grid.GridEditor
51573  * @extends Roo.Editor
51574  * Class for creating and editable grid elements.
51575  * @param {Object} config any settings (must include field)
51576  */
51577 Roo.grid.GridEditor = function(field, config){
51578     if (!config && field.field) {
51579         config = field;
51580         field = Roo.factory(config.field, Roo.form);
51581     }
51582     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51583     field.monitorTab = false;
51584 };
51585
51586 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51587     
51588     /**
51589      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51590      */
51591     
51592     alignment: "tl-tl",
51593     autoSize: "width",
51594     hideEl : false,
51595     cls: "x-small-editor x-grid-editor",
51596     shim:false,
51597     shadow:"frame"
51598 });/*
51599  * Based on:
51600  * Ext JS Library 1.1.1
51601  * Copyright(c) 2006-2007, Ext JS, LLC.
51602  *
51603  * Originally Released Under LGPL - original licence link has changed is not relivant.
51604  *
51605  * Fork - LGPL
51606  * <script type="text/javascript">
51607  */
51608   
51609
51610   
51611 Roo.grid.PropertyRecord = Roo.data.Record.create([
51612     {name:'name',type:'string'},  'value'
51613 ]);
51614
51615
51616 Roo.grid.PropertyStore = function(grid, source){
51617     this.grid = grid;
51618     this.store = new Roo.data.Store({
51619         recordType : Roo.grid.PropertyRecord
51620     });
51621     this.store.on('update', this.onUpdate,  this);
51622     if(source){
51623         this.setSource(source);
51624     }
51625     Roo.grid.PropertyStore.superclass.constructor.call(this);
51626 };
51627
51628
51629
51630 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51631     setSource : function(o){
51632         this.source = o;
51633         this.store.removeAll();
51634         var data = [];
51635         for(var k in o){
51636             if(this.isEditableValue(o[k])){
51637                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51638             }
51639         }
51640         this.store.loadRecords({records: data}, {}, true);
51641     },
51642
51643     onUpdate : function(ds, record, type){
51644         if(type == Roo.data.Record.EDIT){
51645             var v = record.data['value'];
51646             var oldValue = record.modified['value'];
51647             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51648                 this.source[record.id] = v;
51649                 record.commit();
51650                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51651             }else{
51652                 record.reject();
51653             }
51654         }
51655     },
51656
51657     getProperty : function(row){
51658        return this.store.getAt(row);
51659     },
51660
51661     isEditableValue: function(val){
51662         if(val && val instanceof Date){
51663             return true;
51664         }else if(typeof val == 'object' || typeof val == 'function'){
51665             return false;
51666         }
51667         return true;
51668     },
51669
51670     setValue : function(prop, value){
51671         this.source[prop] = value;
51672         this.store.getById(prop).set('value', value);
51673     },
51674
51675     getSource : function(){
51676         return this.source;
51677     }
51678 });
51679
51680 Roo.grid.PropertyColumnModel = function(grid, store){
51681     this.grid = grid;
51682     var g = Roo.grid;
51683     g.PropertyColumnModel.superclass.constructor.call(this, [
51684         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51685         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51686     ]);
51687     this.store = store;
51688     this.bselect = Roo.DomHelper.append(document.body, {
51689         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51690             {tag: 'option', value: 'true', html: 'true'},
51691             {tag: 'option', value: 'false', html: 'false'}
51692         ]
51693     });
51694     Roo.id(this.bselect);
51695     var f = Roo.form;
51696     this.editors = {
51697         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51698         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51699         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51700         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51701         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51702     };
51703     this.renderCellDelegate = this.renderCell.createDelegate(this);
51704     this.renderPropDelegate = this.renderProp.createDelegate(this);
51705 };
51706
51707 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51708     
51709     
51710     nameText : 'Name',
51711     valueText : 'Value',
51712     
51713     dateFormat : 'm/j/Y',
51714     
51715     
51716     renderDate : function(dateVal){
51717         return dateVal.dateFormat(this.dateFormat);
51718     },
51719
51720     renderBool : function(bVal){
51721         return bVal ? 'true' : 'false';
51722     },
51723
51724     isCellEditable : function(colIndex, rowIndex){
51725         return colIndex == 1;
51726     },
51727
51728     getRenderer : function(col){
51729         return col == 1 ?
51730             this.renderCellDelegate : this.renderPropDelegate;
51731     },
51732
51733     renderProp : function(v){
51734         return this.getPropertyName(v);
51735     },
51736
51737     renderCell : function(val){
51738         var rv = val;
51739         if(val instanceof Date){
51740             rv = this.renderDate(val);
51741         }else if(typeof val == 'boolean'){
51742             rv = this.renderBool(val);
51743         }
51744         return Roo.util.Format.htmlEncode(rv);
51745     },
51746
51747     getPropertyName : function(name){
51748         var pn = this.grid.propertyNames;
51749         return pn && pn[name] ? pn[name] : name;
51750     },
51751
51752     getCellEditor : function(colIndex, rowIndex){
51753         var p = this.store.getProperty(rowIndex);
51754         var n = p.data['name'], val = p.data['value'];
51755         
51756         if(typeof(this.grid.customEditors[n]) == 'string'){
51757             return this.editors[this.grid.customEditors[n]];
51758         }
51759         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51760             return this.grid.customEditors[n];
51761         }
51762         if(val instanceof Date){
51763             return this.editors['date'];
51764         }else if(typeof val == 'number'){
51765             return this.editors['number'];
51766         }else if(typeof val == 'boolean'){
51767             return this.editors['boolean'];
51768         }else{
51769             return this.editors['string'];
51770         }
51771     }
51772 });
51773
51774 /**
51775  * @class Roo.grid.PropertyGrid
51776  * @extends Roo.grid.EditorGrid
51777  * This class represents the  interface of a component based property grid control.
51778  * <br><br>Usage:<pre><code>
51779  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51780       
51781  });
51782  // set any options
51783  grid.render();
51784  * </code></pre>
51785   
51786  * @constructor
51787  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51788  * The container MUST have some type of size defined for the grid to fill. The container will be
51789  * automatically set to position relative if it isn't already.
51790  * @param {Object} config A config object that sets properties on this grid.
51791  */
51792 Roo.grid.PropertyGrid = function(container, config){
51793     config = config || {};
51794     var store = new Roo.grid.PropertyStore(this);
51795     this.store = store;
51796     var cm = new Roo.grid.PropertyColumnModel(this, store);
51797     store.store.sort('name', 'ASC');
51798     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51799         ds: store.store,
51800         cm: cm,
51801         enableColLock:false,
51802         enableColumnMove:false,
51803         stripeRows:false,
51804         trackMouseOver: false,
51805         clicksToEdit:1
51806     }, config));
51807     this.getGridEl().addClass('x-props-grid');
51808     this.lastEditRow = null;
51809     this.on('columnresize', this.onColumnResize, this);
51810     this.addEvents({
51811          /**
51812              * @event beforepropertychange
51813              * Fires before a property changes (return false to stop?)
51814              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51815              * @param {String} id Record Id
51816              * @param {String} newval New Value
51817          * @param {String} oldval Old Value
51818              */
51819         "beforepropertychange": true,
51820         /**
51821              * @event propertychange
51822              * Fires after a property changes
51823              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51824              * @param {String} id Record Id
51825              * @param {String} newval New Value
51826          * @param {String} oldval Old Value
51827              */
51828         "propertychange": true
51829     });
51830     this.customEditors = this.customEditors || {};
51831 };
51832 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51833     
51834      /**
51835      * @cfg {Object} customEditors map of colnames=> custom editors.
51836      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51837      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51838      * false disables editing of the field.
51839          */
51840     
51841       /**
51842      * @cfg {Object} propertyNames map of property Names to their displayed value
51843          */
51844     
51845     render : function(){
51846         Roo.grid.PropertyGrid.superclass.render.call(this);
51847         this.autoSize.defer(100, this);
51848     },
51849
51850     autoSize : function(){
51851         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51852         if(this.view){
51853             this.view.fitColumns();
51854         }
51855     },
51856
51857     onColumnResize : function(){
51858         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51859         this.autoSize();
51860     },
51861     /**
51862      * Sets the data for the Grid
51863      * accepts a Key => Value object of all the elements avaiable.
51864      * @param {Object} data  to appear in grid.
51865      */
51866     setSource : function(source){
51867         this.store.setSource(source);
51868         //this.autoSize();
51869     },
51870     /**
51871      * Gets all the data from the grid.
51872      * @return {Object} data  data stored in grid
51873      */
51874     getSource : function(){
51875         return this.store.getSource();
51876     }
51877 });/*
51878  * Based on:
51879  * Ext JS Library 1.1.1
51880  * Copyright(c) 2006-2007, Ext JS, LLC.
51881  *
51882  * Originally Released Under LGPL - original licence link has changed is not relivant.
51883  *
51884  * Fork - LGPL
51885  * <script type="text/javascript">
51886  */
51887  
51888 /**
51889  * @class Roo.LoadMask
51890  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51891  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51892  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51893  * element's UpdateManager load indicator and will be destroyed after the initial load.
51894  * @constructor
51895  * Create a new LoadMask
51896  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51897  * @param {Object} config The config object
51898  */
51899 Roo.LoadMask = function(el, config){
51900     this.el = Roo.get(el);
51901     Roo.apply(this, config);
51902     if(this.store){
51903         this.store.on('beforeload', this.onBeforeLoad, this);
51904         this.store.on('load', this.onLoad, this);
51905         this.store.on('loadexception', this.onLoad, this);
51906         this.removeMask = false;
51907     }else{
51908         var um = this.el.getUpdateManager();
51909         um.showLoadIndicator = false; // disable the default indicator
51910         um.on('beforeupdate', this.onBeforeLoad, this);
51911         um.on('update', this.onLoad, this);
51912         um.on('failure', this.onLoad, this);
51913         this.removeMask = true;
51914     }
51915 };
51916
51917 Roo.LoadMask.prototype = {
51918     /**
51919      * @cfg {Boolean} removeMask
51920      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51921      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51922      */
51923     /**
51924      * @cfg {String} msg
51925      * The text to display in a centered loading message box (defaults to 'Loading...')
51926      */
51927     msg : 'Loading...',
51928     /**
51929      * @cfg {String} msgCls
51930      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51931      */
51932     msgCls : 'x-mask-loading',
51933
51934     /**
51935      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51936      * @type Boolean
51937      */
51938     disabled: false,
51939
51940     /**
51941      * Disables the mask to prevent it from being displayed
51942      */
51943     disable : function(){
51944        this.disabled = true;
51945     },
51946
51947     /**
51948      * Enables the mask so that it can be displayed
51949      */
51950     enable : function(){
51951         this.disabled = false;
51952     },
51953
51954     // private
51955     onLoad : function(){
51956         this.el.unmask(this.removeMask);
51957     },
51958
51959     // private
51960     onBeforeLoad : function(){
51961         if(!this.disabled){
51962             this.el.mask(this.msg, this.msgCls);
51963         }
51964     },
51965
51966     // private
51967     destroy : function(){
51968         if(this.store){
51969             this.store.un('beforeload', this.onBeforeLoad, this);
51970             this.store.un('load', this.onLoad, this);
51971             this.store.un('loadexception', this.onLoad, this);
51972         }else{
51973             var um = this.el.getUpdateManager();
51974             um.un('beforeupdate', this.onBeforeLoad, this);
51975             um.un('update', this.onLoad, this);
51976             um.un('failure', this.onLoad, this);
51977         }
51978     }
51979 };/*
51980  * Based on:
51981  * Ext JS Library 1.1.1
51982  * Copyright(c) 2006-2007, Ext JS, LLC.
51983  *
51984  * Originally Released Under LGPL - original licence link has changed is not relivant.
51985  *
51986  * Fork - LGPL
51987  * <script type="text/javascript">
51988  */
51989 Roo.XTemplate = function(){
51990     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51991     var s = this.html;
51992
51993     s = ['<tpl>', s, '</tpl>'].join('');
51994
51995     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51996
51997     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51998     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51999     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52000     var m, id = 0;
52001     var tpls = [];
52002
52003     while(m = s.match(re)){
52004        var m2 = m[0].match(nameRe);
52005        var m3 = m[0].match(ifRe);
52006        var m4 = m[0].match(execRe);
52007        var exp = null, fn = null, exec = null;
52008        var name = m2 && m2[1] ? m2[1] : '';
52009        if(m3){
52010            exp = m3 && m3[1] ? m3[1] : null;
52011            if(exp){
52012                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52013            }
52014        }
52015        if(m4){
52016            exp = m4 && m4[1] ? m4[1] : null;
52017            if(exp){
52018                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52019            }
52020        }
52021        if(name){
52022            switch(name){
52023                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52024                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52025                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52026            }
52027        }
52028        tpls.push({
52029             id: id,
52030             target: name,
52031             exec: exec,
52032             test: fn,
52033             body: m[1]||''
52034         });
52035        s = s.replace(m[0], '{xtpl'+ id + '}');
52036        ++id;
52037     }
52038     for(var i = tpls.length-1; i >= 0; --i){
52039         this.compileTpl(tpls[i]);
52040     }
52041     this.master = tpls[tpls.length-1];
52042     this.tpls = tpls;
52043 };
52044 Roo.extend(Roo.XTemplate, Roo.Template, {
52045
52046     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52047
52048     applySubTemplate : function(id, values, parent){
52049         var t = this.tpls[id];
52050         if(t.test && !t.test.call(this, values, parent)){
52051             return '';
52052         }
52053         if(t.exec && t.exec.call(this, values, parent)){
52054             return '';
52055         }
52056         var vs = t.target ? t.target.call(this, values, parent) : values;
52057         parent = t.target ? values : parent;
52058         if(t.target && vs instanceof Array){
52059             var buf = [];
52060             for(var i = 0, len = vs.length; i < len; i++){
52061                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52062             }
52063             return buf.join('');
52064         }
52065         return t.compiled.call(this, vs, parent);
52066     },
52067
52068     compileTpl : function(tpl){
52069         var fm = Roo.util.Format;
52070         var useF = this.disableFormats !== true;
52071         var sep = Roo.isGecko ? "+" : ",";
52072         var fn = function(m, name, format, args){
52073             if(name.substr(0, 4) == 'xtpl'){
52074                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52075             }
52076             var v;
52077             if(name.indexOf('.') != -1){
52078                 v = name;
52079             }else{
52080                 v = "values['" + name + "']";
52081             }
52082             if(format && useF){
52083                 args = args ? ',' + args : "";
52084                 if(format.substr(0, 5) != "this."){
52085                     format = "fm." + format + '(';
52086                 }else{
52087                     format = 'this.call("'+ format.substr(5) + '", ';
52088                     args = ", values";
52089                 }
52090             }else{
52091                 args= ''; format = "("+v+" === undefined ? '' : ";
52092             }
52093             return "'"+ sep + format + v + args + ")"+sep+"'";
52094         };
52095         var body;
52096         // branched to use + in gecko and [].join() in others
52097         if(Roo.isGecko){
52098             body = "tpl.compiled = function(values, parent){ return '" +
52099                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52100                     "';};";
52101         }else{
52102             body = ["tpl.compiled = function(values, parent){ return ['"];
52103             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52104             body.push("'].join('');};");
52105             body = body.join('');
52106         }
52107         /** eval:var:zzzzzzz */
52108         eval(body);
52109         return this;
52110     },
52111
52112     applyTemplate : function(values){
52113         return this.master.compiled.call(this, values, {});
52114         var s = this.subs;
52115     },
52116
52117     apply : function(){
52118         return this.applyTemplate.apply(this, arguments);
52119     },
52120
52121     compile : function(){return this;}
52122 });
52123
52124 Roo.XTemplate.from = function(el){
52125     el = Roo.getDom(el);
52126     return new Roo.XTemplate(el.value || el.innerHTML);
52127 };/*
52128  * Original code for Roojs - LGPL
52129  * <script type="text/javascript">
52130  */
52131  
52132 /**
52133  * @class Roo.XComponent
52134  * A delayed Element creator...
52135  * Or a way to group chunks of interface together.
52136  * 
52137  * Mypart.xyx = new Roo.XComponent({
52138
52139     parent : 'Mypart.xyz', // empty == document.element.!!
52140     order : '001',
52141     name : 'xxxx'
52142     region : 'xxxx'
52143     disabled : function() {} 
52144      
52145     tree : function() { // return an tree of xtype declared components
52146         var MODULE = this;
52147         return 
52148         {
52149             xtype : 'NestedLayoutPanel',
52150             // technicall
52151         }
52152      ]
52153  *})
52154  *
52155  *
52156  * It can be used to build a big heiracy, with parent etc.
52157  * or you can just use this to render a single compoent to a dom element
52158  * MYPART.render(Roo.Element | String(id) | dom_element )
52159  * 
52160  * @extends Roo.util.Observable
52161  * @constructor
52162  * @param cfg {Object} configuration of component
52163  * 
52164  */
52165 Roo.XComponent = function(cfg) {
52166     Roo.apply(this, cfg);
52167     this.addEvents({ 
52168         /**
52169              * @event built
52170              * Fires when this the componnt is built
52171              * @param {Roo.XComponent} c the component
52172              */
52173         'built' : true,
52174         /**
52175              * @event buildcomplete
52176              * Fires on the top level element when all elements have been built
52177              * @param {Roo.XComponent} c the top level component.
52178          */
52179         'buildcomplete' : true
52180         
52181     });
52182     this.region = this.region || 'center'; // default..
52183     Roo.XComponent.register(this);
52184     this.modules = false;
52185     this.el = false; // where the layout goes..
52186     
52187     
52188 }
52189 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52190     /**
52191      * @property el
52192      * The created element (with Roo.factory())
52193      * @type {Roo.Layout}
52194      */
52195     el  : false,
52196     
52197     /**
52198      * @property el
52199      * for BC  - use el in new code
52200      * @type {Roo.Layout}
52201      */
52202     panel : false,
52203     
52204     /**
52205      * @property layout
52206      * for BC  - use el in new code
52207      * @type {Roo.Layout}
52208      */
52209     layout : false,
52210     
52211      /**
52212      * @cfg {Function|boolean} disabled
52213      * If this module is disabled by some rule, return true from the funtion
52214      */
52215     disabled : false,
52216     
52217     /**
52218      * @cfg {String} parent 
52219      * Name of parent element which it get xtype added to..
52220      */
52221     parent: false,
52222     
52223     /**
52224      * @cfg {String} order
52225      * Used to set the order in which elements are created (usefull for multiple tabs)
52226      */
52227     
52228     order : false,
52229     /**
52230      * @cfg {String} name
52231      * String to display while loading.
52232      */
52233     name : false,
52234     /**
52235      * @cfg {String} region
52236      * Region to render component to (defaults to center)
52237      */
52238     region : 'center',
52239     
52240     /**
52241      * @cfg {Array} items
52242      * A single item array - the first element is the root of the tree..
52243      * It's done this way to stay compatible with the Xtype system...
52244      */
52245     items : false,
52246     
52247     
52248      /**
52249      * render
52250      * render element to dom or tree
52251      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52252      */
52253     
52254     render : function(el)
52255     {
52256         
52257         el = el || false;
52258         var hp = this.parent ? 1 : 0;
52259         
52260         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52261             // if parent is a '#.....' string, then let's use that..
52262             var ename = this.parent.substr(1)
52263             this.parent = false;
52264             el = Roo.get(ename);
52265             if (!el) {
52266                 Roo.log("Warning - element can not be found :#" + ename );
52267                 return;
52268             }
52269         }
52270         
52271         
52272         if (!this.parent) {
52273             
52274             el = el ? Roo.get(el) : false;
52275             
52276             // it's a top level one..
52277             this.parent =  {
52278                 el : new Roo.BorderLayout(el || document.body, {
52279                 
52280                      center: {
52281                          titlebar: false,
52282                          autoScroll:false,
52283                          closeOnTab: true,
52284                          tabPosition: 'top',
52285                           //resizeTabs: true,
52286                          alwaysShowTabs: el && hp? false :  true,
52287                          hideTabs: el || !hp ? true :  false,
52288                          minTabWidth: 140
52289                      }
52290                  })
52291             }
52292         }
52293         
52294         
52295             
52296         var tree = this.tree();
52297         tree.region = tree.region || this.region;
52298         this.el = this.parent.el.addxtype(tree);
52299         this.fireEvent('built', this);
52300         
52301         this.panel = this.el;
52302         this.layout = this.panel.layout;    
52303          
52304     }
52305     
52306 });
52307
52308 Roo.apply(Roo.XComponent, {
52309     
52310     /**
52311      * @property  buildCompleted
52312      * True when the builder has completed building the interface.
52313      * @type Boolean
52314      */
52315     buildCompleted : false,
52316      
52317     /**
52318      * @property  topModule
52319      * the upper most module - uses document.element as it's constructor.
52320      * @type Object
52321      */
52322      
52323     topModule  : false,
52324       
52325     /**
52326      * @property  modules
52327      * array of modules to be created by registration system.
52328      * @type {Array} of Roo.XComponent
52329      */
52330     
52331     modules : [],
52332     /**
52333      * @property  elmodules
52334      * array of modules to be created by which use #ID 
52335      * @type {Array} of Roo.XComponent
52336      */
52337      
52338     elmodules : [],
52339
52340     
52341     /**
52342      * Register components to be built later.
52343      *
52344      * This solves the following issues
52345      * - Building is not done on page load, but after an authentication process has occured.
52346      * - Interface elements are registered on page load
52347      * - Parent Interface elements may not be loaded before child, so this handles that..
52348      * 
52349      *
52350      * example:
52351      * 
52352      * MyApp.register({
52353           order : '000001',
52354           module : 'Pman.Tab.projectMgr',
52355           region : 'center',
52356           parent : 'Pman.layout',
52357           disabled : false,  // or use a function..
52358         })
52359      
52360      * * @param {Object} details about module
52361      */
52362     register : function(obj) {
52363         this.modules.push(obj);
52364          
52365     },
52366     /**
52367      * convert a string to an object..
52368      * eg. 'AAA.BBB' -> finds AAA.BBB
52369
52370      */
52371     
52372     toObject : function(str)
52373     {
52374         if (!str || typeof(str) == 'object') {
52375             return str;
52376         }
52377         if (str.substring(0,1) == '#') {
52378             return str;
52379         }
52380
52381         var ar = str.split('.');
52382         var rt, o;
52383         rt = ar.shift();
52384             /** eval:var:o */
52385         try {
52386             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52387         } catch (e) {
52388             throw "Module not found : " + str;
52389         }
52390         
52391         if (o === false) {
52392             throw "Module not found : " + str;
52393         }
52394         Roo.each(ar, function(e) {
52395             if (typeof(o[e]) == 'undefined') {
52396                 throw "Module not found : " + str;
52397             }
52398             o = o[e];
52399         });
52400         
52401         return o;
52402         
52403     },
52404     
52405     
52406     /**
52407      * move modules into their correct place in the tree..
52408      * 
52409      */
52410     preBuild : function ()
52411     {
52412         var _t = this;
52413         Roo.each(this.modules , function (obj)
52414         {
52415             var opar = obj.parent;
52416             try { 
52417                 obj.parent = this.toObject(opar);
52418             } catch(e) {
52419                 Roo.log(e.toString());
52420                 return;
52421             }
52422             
52423             if (!obj.parent) {
52424                 this.topModule = obj;
52425                 return;
52426             }
52427             if (typeof(obj.parent) == 'string') {
52428                 this.elmodules.push(obj);
52429                 return;
52430             }
52431             if (obj.parent.constructor != Roo.XComponent) {
52432                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52433             }
52434             if (!obj.parent.modules) {
52435                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52436                     function(o) { return o.order + '' }
52437                 );
52438             }
52439             
52440             obj.parent.modules.add(obj);
52441         }, this);
52442     },
52443     
52444      /**
52445      * make a list of modules to build.
52446      * @return {Array} list of modules. 
52447      */ 
52448     
52449     buildOrder : function()
52450     {
52451         var _this = this;
52452         var cmp = function(a,b) {   
52453             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52454         };
52455         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52456             throw "No top level modules to build";
52457         }
52458         
52459         // make a flat list in order of modules to build.
52460         var mods = this.topModule ? [ this.topModule ] : [];
52461         Roo.each(this.elmodules,function(e) { mods.push(e) });
52462
52463         
52464         // add modules to their parents..
52465         var addMod = function(m) {
52466            // Roo.debug && Roo.log(m.modKey);
52467             
52468             mods.push(m);
52469             if (m.modules) {
52470                 m.modules.keySort('ASC',  cmp );
52471                 m.modules.each(addMod);
52472             }
52473             // not sure if this is used any more..
52474             if (m.finalize) {
52475                 m.finalize.name = m.name + " (clean up) ";
52476                 mods.push(m.finalize);
52477             }
52478             
52479         }
52480         if (this.topModule) { 
52481             this.topModule.modules.keySort('ASC',  cmp );
52482             this.topModule.modules.each(addMod);
52483         }
52484         return mods;
52485     },
52486     
52487      /**
52488      * Build the registered modules.
52489      * @param {Object} parent element.
52490      * @param {Function} optional method to call after module has been added.
52491      * 
52492      */ 
52493    
52494     build : function() 
52495     {
52496         
52497         this.preBuild();
52498         var mods = this.buildOrder();
52499       
52500         //this.allmods = mods;
52501         //Roo.debug && Roo.log(mods);
52502         //return;
52503         if (!mods.length) { // should not happen
52504             throw "NO modules!!!";
52505         }
52506         
52507         
52508         
52509         // flash it up as modal - so we store the mask!?
52510         Roo.MessageBox.show({ title: 'loading' });
52511         Roo.MessageBox.show({
52512            title: "Please wait...",
52513            msg: "Building Interface...",
52514            width:450,
52515            progress:true,
52516            closable:false,
52517            modal: false
52518           
52519         });
52520         var total = mods.length;
52521         
52522         var _this = this;
52523         var progressRun = function() {
52524             if (!mods.length) {
52525                 Roo.debug && Roo.log('hide?');
52526                 Roo.MessageBox.hide();
52527                 if (_this.topModule) { 
52528                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52529                 }
52530                 // THE END...
52531                 return false;   
52532             }
52533             
52534             var m = mods.shift();
52535             
52536             
52537             Roo.debug && Roo.log(m);
52538             // not sure if this is supported any more.. - modules that are are just function
52539             if (typeof(m) == 'function') { 
52540                 m.call(this);
52541                 return progressRun.defer(10, _this);
52542             } 
52543             
52544             
52545             
52546             Roo.MessageBox.updateProgress(
52547                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52548                     " of " + total + 
52549                     (m.name ? (' - ' + m.name) : '')
52550                     );
52551             
52552          
52553             // is the module disabled?
52554             var disabled = (typeof(m.disabled) == 'function') ?
52555                 m.disabled.call(m.module.disabled) : m.disabled;    
52556             
52557             
52558             if (disabled) {
52559                 return progressRun(); // we do not update the display!
52560             }
52561             
52562             // now build 
52563             
52564             m.render();
52565             // it's 10 on top level, and 1 on others??? why...
52566             return progressRun.defer(10, _this);
52567              
52568         }
52569         progressRun.defer(1, _this);
52570      
52571         
52572         
52573     }
52574     
52575      
52576    
52577     
52578     
52579 });
52580  //<script type="text/javascript">
52581
52582
52583 /**
52584  * @class Roo.Login
52585  * @extends Roo.LayoutDialog
52586  * A generic Login Dialog..... - only one needed in theory!?!?
52587  *
52588  * Fires XComponent builder on success...
52589  * 
52590  * Sends 
52591  *    username,password, lang = for login actions.
52592  *    check = 1 for periodic checking that sesion is valid.
52593  *    passwordRequest = email request password
52594  *    logout = 1 = to logout
52595  * 
52596  * Affects: (this id="????" elements)
52597  *   loading  (removed) (used to indicate application is loading)
52598  *   loading-mask (hides) (used to hide application when it's building loading)
52599  *   
52600  * 
52601  * Usage: 
52602  *    
52603  * 
52604  * Myapp.login = Roo.Login({
52605      url: xxxx,
52606    
52607      realm : 'Myapp', 
52608      
52609      
52610      method : 'POST',
52611      
52612      
52613      * 
52614  })
52615  * 
52616  * 
52617  * 
52618  **/
52619  
52620 Roo.Login = function(cfg)
52621 {
52622     this.addEvents({
52623         'refreshed' : true
52624     });
52625     
52626     Roo.apply(this,cfg);
52627     
52628     Roo.onReady(function() {
52629         this.onLoad();
52630     }, this);
52631     // call parent..
52632     
52633    
52634     Roo.Login.superclass.constructor.call(this, this);
52635     //this.addxtype(this.items[0]);
52636     
52637     
52638 }
52639
52640
52641 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52642     
52643     /**
52644      * @cfg {String} method
52645      * Method used to query for login details.
52646      */
52647     
52648     method : 'POST',
52649     /**
52650      * @cfg {String} url
52651      * URL to query login data. - eg. baseURL + '/Login.php'
52652      */
52653     url : '',
52654     
52655     /**
52656      * @property user
52657      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52658      * @type {Object} 
52659      */
52660     user : false,
52661     /**
52662      * @property checkFails
52663      * Number of times we have attempted to get authentication check, and failed.
52664      * @type {Number} 
52665      */
52666     checkFails : 0,
52667       /**
52668      * @property intervalID
52669      * The window interval that does the constant login checking.
52670      * @type {Number} 
52671      */
52672     intervalID : 0,
52673     
52674     
52675     onLoad : function() // called on page load...
52676     {
52677         // load 
52678          
52679         if (Roo.get('loading')) { // clear any loading indicator..
52680             Roo.get('loading').remove();
52681         }
52682         
52683         //this.switchLang('en'); // set the language to english..
52684        
52685         this.check({
52686             success:  function(response, opts)  {  // check successfull...
52687             
52688                 var res = this.processResponse(response);
52689                 this.checkFails =0;
52690                 if (!res.success) { // error!
52691                     this.checkFails = 5;
52692                     //console.log('call failure');
52693                     return this.failure(response,opts);
52694                 }
52695                 
52696                 if (!res.data.id) { // id=0 == login failure.
52697                     return this.show();
52698                 }
52699                 
52700                               
52701                         //console.log(success);
52702                 this.fillAuth(res.data);   
52703                 this.checkFails =0;
52704                 Roo.XComponent.build();
52705             },
52706             failure : this.show
52707         });
52708         
52709     }, 
52710     
52711     
52712     check: function(cfg) // called every so often to refresh cookie etc..
52713     {
52714         if (cfg.again) { // could be undefined..
52715             this.checkFails++;
52716         } else {
52717             this.checkFails = 0;
52718         }
52719         var _this = this;
52720         if (this.sending) {
52721             if ( this.checkFails > 4) {
52722                 Roo.MessageBox.alert("Error",  
52723                     "Error getting authentication status. - try reloading, or wait a while", function() {
52724                         _this.sending = false;
52725                     }); 
52726                 return;
52727             }
52728             cfg.again = true;
52729             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52730             return;
52731         }
52732         this.sending = true;
52733         
52734         Roo.Ajax.request({  
52735             url: this.url,
52736             params: {
52737                 getAuthUser: true
52738             },  
52739             method: this.method,
52740             success:  cfg.success || this.success,
52741             failure : cfg.failure || this.failure,
52742             scope : this,
52743             callCfg : cfg
52744               
52745         });  
52746     }, 
52747     
52748     
52749     logout: function()
52750     {
52751         window.onbeforeunload = function() { }; // false does not work for IE..
52752         this.user = false;
52753         var _this = this;
52754         
52755         Roo.Ajax.request({  
52756             url: this.url,
52757             params: {
52758                 logout: 1
52759             },  
52760             method: 'GET',
52761             failure : function() {
52762                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52763                     document.location = document.location.toString() + '?ts=' + Math.random();
52764                 });
52765                 
52766             },
52767             success : function() {
52768                 _this.user = false;
52769                 this.checkFails =0;
52770                 // fixme..
52771                 document.location = document.location.toString() + '?ts=' + Math.random();
52772             }
52773               
52774               
52775         }); 
52776     },
52777     
52778     processResponse : function (response)
52779     {
52780         var res = '';
52781         try {
52782             res = Roo.decode(response.responseText);
52783             // oops...
52784             if (typeof(res) != 'object') {
52785                 res = { success : false, errorMsg : res, errors : true };
52786             }
52787             if (typeof(res.success) == 'undefined') {
52788                 res.success = false;
52789             }
52790             
52791         } catch(e) {
52792             res = { success : false,  errorMsg : response.responseText, errors : true };
52793         }
52794         return res;
52795     },
52796     
52797     success : function(response, opts)  // check successfull...
52798     {  
52799         this.sending = false;
52800         var res = this.processResponse(response);
52801         if (!res.success) {
52802             return this.failure(response, opts);
52803         }
52804         if (!res.data || !res.data.id) {
52805             return this.failure(response,opts);
52806         }
52807         //console.log(res);
52808         this.fillAuth(res.data);
52809         
52810         this.checkFails =0;
52811         
52812     },
52813     
52814     
52815     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52816     {
52817         this.authUser = -1;
52818         this.sending = false;
52819         var res = this.processResponse(response);
52820         //console.log(res);
52821         if ( this.checkFails > 2) {
52822         
52823             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52824                 "Error getting authentication status. - try reloading"); 
52825             return;
52826         }
52827         opts.callCfg.again = true;
52828         this.check.defer(1000, this, [ opts.callCfg ]);
52829         return;  
52830     },
52831     
52832     
52833     
52834     fillAuth: function(au) {
52835         this.startAuthCheck();
52836         this.authUserId = au.id;
52837         this.authUser = au;
52838         this.lastChecked = new Date();
52839         this.fireEvent('refreshed', au);
52840         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52841         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52842         au.lang = au.lang || 'en';
52843         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52844         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52845         this.switchLang(au.lang );
52846         
52847      
52848         // open system... - -on setyp..
52849         if (this.authUserId  < 0) {
52850             Roo.MessageBox.alert("Warning", 
52851                 "This is an open system - please set up a admin user with a password.");  
52852         }
52853          
52854         //Pman.onload(); // which should do nothing if it's a re-auth result...
52855         
52856              
52857     },
52858     
52859     startAuthCheck : function() // starter for timeout checking..
52860     {
52861         if (this.intervalID) { // timer already in place...
52862             return false;
52863         }
52864         var _this = this;
52865         this.intervalID =  window.setInterval(function() {
52866               _this.check(false);
52867             }, 120000); // every 120 secs = 2mins..
52868         
52869         
52870     },
52871          
52872     
52873     switchLang : function (lang) 
52874     {
52875         _T = typeof(_T) == 'undefined' ? false : _T;
52876           if (!_T || !lang.length) {
52877             return;
52878         }
52879         
52880         if (!_T && lang != 'en') {
52881             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52882             return;
52883         }
52884         
52885         if (typeof(_T.en) == 'undefined') {
52886             _T.en = {};
52887             Roo.apply(_T.en, _T);
52888         }
52889         
52890         if (typeof(_T[lang]) == 'undefined') {
52891             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52892             return;
52893         }
52894         
52895         
52896         Roo.apply(_T, _T[lang]);
52897         // just need to set the text values for everything...
52898         var _this = this;
52899         /* this will not work ...
52900         if (this.form) { 
52901             
52902                
52903             function formLabel(name, val) {
52904                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52905             }
52906             
52907             formLabel('password', "Password"+':');
52908             formLabel('username', "Email Address"+':');
52909             formLabel('lang', "Language"+':');
52910             this.dialog.setTitle("Login");
52911             this.dialog.buttons[0].setText("Forgot Password");
52912             this.dialog.buttons[1].setText("Login");
52913         }
52914         */
52915         
52916         
52917     },
52918     
52919     
52920     title: "Login",
52921     modal: true,
52922     width:  350,
52923     //height: 230,
52924     height: 180,
52925     shadow: true,
52926     minWidth:200,
52927     minHeight:180,
52928     //proxyDrag: true,
52929     closable: false,
52930     draggable: false,
52931     collapsible: false,
52932     resizable: false,
52933     center: {  // needed??
52934         autoScroll:false,
52935         titlebar: false,
52936        // tabPosition: 'top',
52937         hideTabs: true,
52938         closeOnTab: true,
52939         alwaysShowTabs: false
52940     } ,
52941     listeners : {
52942         
52943         show  : function(dlg)
52944         {
52945             //console.log(this);
52946             this.form = this.layout.getRegion('center').activePanel.form;
52947             this.form.dialog = dlg;
52948             this.buttons[0].form = this.form;
52949             this.buttons[0].dialog = dlg;
52950             this.buttons[1].form = this.form;
52951             this.buttons[1].dialog = dlg;
52952            
52953            //this.resizeToLogo.defer(1000,this);
52954             // this is all related to resizing for logos..
52955             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52956            //// if (!sz) {
52957              //   this.resizeToLogo.defer(1000,this);
52958              //   return;
52959            // }
52960             //var w = Ext.lib.Dom.getViewWidth() - 100;
52961             //var h = Ext.lib.Dom.getViewHeight() - 100;
52962             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52963             //this.center();
52964             if (this.disabled) {
52965                 this.hide();
52966                 return;
52967             }
52968             
52969             if (this.user.id < 0) { // used for inital setup situations.
52970                 return;
52971             }
52972             
52973             if (this.intervalID) {
52974                 // remove the timer
52975                 window.clearInterval(this.intervalID);
52976                 this.intervalID = false;
52977             }
52978             
52979             
52980             if (Roo.get('loading')) {
52981                 Roo.get('loading').remove();
52982             }
52983             if (Roo.get('loading-mask')) {
52984                 Roo.get('loading-mask').hide();
52985             }
52986             
52987             //incomming._node = tnode;
52988             this.form.reset();
52989             //this.dialog.modal = !modal;
52990             //this.dialog.show();
52991             this.el.unmask(); 
52992             
52993             
52994             this.form.setValues({
52995                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52996                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52997             });
52998             
52999             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53000             if (this.form.findField('username').getValue().length > 0 ){
53001                 this.form.findField('password').focus();
53002             } else {
53003                this.form.findField('username').focus();
53004             }
53005     
53006         }
53007     },
53008     items : [
53009          {
53010        
53011             xtype : 'ContentPanel',
53012             xns : Roo,
53013             region: 'center',
53014             fitToFrame : true,
53015             
53016             items : [
53017     
53018                 {
53019                
53020                     xtype : 'Form',
53021                     xns : Roo.form,
53022                     labelWidth: 100,
53023                     style : 'margin: 10px;',
53024                     
53025                     listeners : {
53026                         actionfailed : function(f, act) {
53027                             // form can return { errors: .... }
53028                                 
53029                             //act.result.errors // invalid form element list...
53030                             //act.result.errorMsg// invalid form element list...
53031                             
53032                             this.dialog.el.unmask();
53033                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53034                                         "Login failed - communication error - try again.");
53035                                       
53036                         },
53037                         actioncomplete: function(re, act) {
53038                              
53039                             Roo.state.Manager.set(
53040                                 this.dialog.realm + '.username',  
53041                                     this.findField('username').getValue()
53042                             );
53043                             Roo.state.Manager.set(
53044                                 this.dialog.realm + '.lang',  
53045                                 this.findField('lang').getValue() 
53046                             );
53047                             
53048                             this.dialog.fillAuth(act.result.data);
53049                               
53050                             this.dialog.hide();
53051                             
53052                             if (Roo.get('loading-mask')) {
53053                                 Roo.get('loading-mask').show();
53054                             }
53055                             Roo.XComponent.build();
53056                             
53057                              
53058                             
53059                         }
53060                     },
53061                     items : [
53062                         {
53063                             xtype : 'TextField',
53064                             xns : Roo.form,
53065                             fieldLabel: "Email Address",
53066                             name: 'username',
53067                             width:200,
53068                             autoCreate : {tag: "input", type: "text", size: "20"}
53069                         },
53070                         {
53071                             xtype : 'TextField',
53072                             xns : Roo.form,
53073                             fieldLabel: "Password",
53074                             inputType: 'password',
53075                             name: 'password',
53076                             width:200,
53077                             autoCreate : {tag: "input", type: "text", size: "20"},
53078                             listeners : {
53079                                 specialkey : function(e,ev) {
53080                                     if (ev.keyCode == 13) {
53081                                         this.form.dialog.el.mask("Logging in");
53082                                         this.form.doAction('submit', {
53083                                             url: this.form.dialog.url,
53084                                             method: this.form.dialog.method
53085                                         });
53086                                     }
53087                                 }
53088                             }  
53089                         },
53090                         {
53091                             xtype : 'ComboBox',
53092                             xns : Roo.form,
53093                             fieldLabel: "Language",
53094                             name : 'langdisp',
53095                             store: {
53096                                 xtype : 'SimpleStore',
53097                                 fields: ['lang', 'ldisp'],
53098                                 data : [
53099                                     [ 'en', 'English' ],
53100                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53101                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53102                                 ]
53103                             },
53104                             
53105                             valueField : 'lang',
53106                             hiddenName:  'lang',
53107                             width: 200,
53108                             displayField:'ldisp',
53109                             typeAhead: false,
53110                             editable: false,
53111                             mode: 'local',
53112                             triggerAction: 'all',
53113                             emptyText:'Select a Language...',
53114                             selectOnFocus:true,
53115                             listeners : {
53116                                 select :  function(cb, rec, ix) {
53117                                     this.form.switchLang(rec.data.lang);
53118                                 }
53119                             }
53120                         
53121                         }
53122                     ]
53123                 }
53124                   
53125                 
53126             ]
53127         }
53128     ],
53129     buttons : [
53130         {
53131             xtype : 'Button',
53132             xns : 'Roo',
53133             text : "Forgot Password",
53134             listeners : {
53135                 click : function() {
53136                     //console.log(this);
53137                     var n = this.form.findField('username').getValue();
53138                     if (!n.length) {
53139                         Roo.MessageBox.alert("Error", "Fill in your email address");
53140                         return;
53141                     }
53142                     Roo.Ajax.request({
53143                         url: this.dialog.url,
53144                         params: {
53145                             passwordRequest: n
53146                         },
53147                         method: this.dialog.method,
53148                         success:  function(response, opts)  {  // check successfull...
53149                         
53150                             var res = this.dialog.processResponse(response);
53151                             if (!res.success) { // error!
53152                                Roo.MessageBox.alert("Error" ,
53153                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53154                                return;
53155                             }
53156                             Roo.MessageBox.alert("Notice" ,
53157                                 "Please check you email for the Password Reset message");
53158                         },
53159                         failure : function() {
53160                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53161                         }
53162                         
53163                     });
53164                 }
53165             }
53166         },
53167         {
53168             xtype : 'Button',
53169             xns : 'Roo',
53170             text : "Login",
53171             listeners : {
53172                 
53173                 click : function () {
53174                         
53175                     this.dialog.el.mask("Logging in");
53176                     this.form.doAction('submit', {
53177                             url: this.dialog.url,
53178                             method: this.dialog.method
53179                     });
53180                 }
53181             }
53182         }
53183     ]
53184   
53185   
53186 })
53187  
53188
53189
53190